From 505c4f5ef312086ee688f6576a705fab86fb09bf Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Thu, 22 Apr 2021 08:56:51 +0200 Subject: [PATCH] Extracted `Dry::CLI::Utils::Files` into `dry-files` (#114) --- docsite/source/file-utilities.html.md | 35 - docsite/source/index.html.md | 1 - lib/dry/cli/utils/files.rb | 443 ----------- spec/unit/dry/cli/utils/files_spec.rb | 1045 ------------------------- 4 files changed, 1524 deletions(-) delete mode 100644 docsite/source/file-utilities.html.md delete mode 100644 lib/dry/cli/utils/files.rb delete mode 100644 spec/unit/dry/cli/utils/files_spec.rb diff --git a/docsite/source/file-utilities.html.md b/docsite/source/file-utilities.html.md deleted file mode 100644 index b08219d..0000000 --- a/docsite/source/file-utilities.html.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: File Utilities -layout: gem-single -name: dry-cli ---- - -File utilities are a set of useful methods to manipulate files and directories, which must be required manually. [API doc](http://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files) - -```ruby -require 'dry/cli/utils/files' -``` - -## List of implemented commands - -- [append](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#append-class_method) - adds a new line at the bottom of the file; -- [cp](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#cp-class_method) - copies source into destination; -- [delete](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#delete-class_method) - deletes given path (file); -- [delete_directory](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#delete_directory-class_method) - deletes given path (directory); -- [directory?](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#directory?-class_method) - checks if path is a directory; -- [exist?](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#exist?-class_method) - checks if `path` exist; -- [inject_line_after](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#inject_line_after-class_method) - inject `contents` in `path` after `target`; -- [inject_line_after_last](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#inject_line_after_last-class_method) - inject `contents` in `path` after last `target`; -- [inject_line_before](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#inject_line_before-class_method) - inject `contents` in `path` before `target`; -- [inject_line_before_last](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#inject_line_before_last-class_method) - inject `contents` in `path` after last `target`; -- [mkdir](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#mkdir-class_method) - creates a directory for the given path; -- [mkdir_p](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#mkdir_p-class_method) - creates a directory for the given path; -- [remove_block](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#remove_block-class_method) - removes `target` block from `path`; -- [remove_line](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#remove_ine-class_method) - removes line from `path`, matching `target`; -- [replace_first_line](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#replace_first_line-class_method) - replace first line in `path` that contains `target` with `replacement`; -- [replace_last_line](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#replace_last_line-class_method) - replace last line in `path` that contains `target` with `replacement`; -- [touch](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#touch-class_method) - creates an empty file for the given path; -- [unshift](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#unshift-class_method) - adds a new line at the top of the file; -- [write](https://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files#write-class_method) - creates a new file or rewrites the contents of an existing file for the given path and content All the intermediate directories are created; - -You can find more information in [API doc](http://www.rubydoc.info/gems/dry-cli/Dry/CLI/Utils/Files). diff --git a/docsite/source/index.html.md b/docsite/source/index.html.md index 47ad6d9..c8be17f 100644 --- a/docsite/source/index.html.md +++ b/docsite/source/index.html.md @@ -12,7 +12,6 @@ sections: - variadic-arguments - commands-with-subcommands-and-params - callbacks - - file-utilities --- `dry-cli` is a general-purpose framework for developing Command Line Interface (CLI) applications. It represents commands as objects that can be registered and offers support for arguments, options and forwarding variadic arguments to a sub-command. diff --git a/lib/dry/cli/utils/files.rb b/lib/dry/cli/utils/files.rb deleted file mode 100644 index d3e7265..0000000 --- a/lib/dry/cli/utils/files.rb +++ /dev/null @@ -1,443 +0,0 @@ -# frozen_string_literal: true - -require "pathname" -require "fileutils" - -module Dry - class CLI - module Utils - # Files utilities - # - # @since 0.3.1 - module Files # rubocop:disable Metrics/ModuleLength - # Creates an empty file for the given path. - # All the intermediate directories are created. - # If the path already exists, it doesn't change the contents - # - # @param path [String,Pathname] the path to file - # - # @since 0.3.1 - def self.touch(path) - mkdir_p(path) - FileUtils.touch(path) - end - - # Creates a new file or rewrites the contents - # of an existing file for the given path and content - # All the intermediate directories are created. - # - # @param path [String,Pathname] the path to file - # @param content [String, Array] the content to write - # - # @since 0.3.1 - def self.write(path, *content) - mkdir_p(path) - open(path, ::File::CREAT | ::File::WRONLY | ::File::TRUNC, *content) # rubocop:disable Security/Open - this isn't a call to `::Kernel.open`, but to `self.open` - end - - # Copies source into destination. - # All the intermediate directories are created. - # If the destination already exists, it overrides the contents. - # - # @param source [String,Pathname] the path to the source file - # @param destination [String,Pathname] the path to the destination file - # - # @since 0.3.1 - def self.cp(source, destination) - mkdir_p(destination) - FileUtils.cp(source, destination) - end - - # Creates a directory for the given path. - # It assumes that all the tokens in `path` are meant to be a directory. - # All the intermediate directories are created. - # - # @param path [String,Pathname] the path to directory - # - # @since 0.3.1 - # - # @see .mkdir_p - # - # @example - # require "dry/cli/utils/files" - # - # Dry::CLI::Utils::Files.mkdir("path/to/directory") - # # => creates the `path/to/directory` directory - # - # # WRONG this isn't probably what you want, check `.mkdir_p` - # Dry::CLI::Utils::Files.mkdir("path/to/file.rb") - # # => creates the `path/to/file.rb` directory - def self.mkdir(path) - FileUtils.mkdir_p(path) - end - - # Creates a directory for the given path. - # It assumes that all the tokens, but the last, in `path` are meant to be - # a directory, whereas the last is meant to be a file. - # All the intermediate directories are created. - # - # @param path [String,Pathname] the path to directory - # - # @since 0.3.1 - # - # @see .mkdir - # - # @example - # require "dry/cli/utils/files" - # - # Dry::CLI::Utils::Files.mkdir_p("path/to/file.rb") - # # => creates the `path/to` directory, but NOT `file.rb` - # - # # WRONG it doesn't create the last directory, check `.mkdir` - # Dry::CLI::Utils::Files.mkdir_p("path/to/directory") - # # => creates the `path/to` directory - def self.mkdir_p(path) - Pathname.new(path).dirname.mkpath - end - - # Deletes given path (file). - # - # @param path [String,Pathname] the path to file - # - # @raise [Errno::ENOENT] if the path doesn't exist - # - # @since 0.3.1 - def self.delete(path) - FileUtils.rm(path) - end - - # Deletes given path (directory). - # - # @param path [String,Pathname] the path to file - # - # @raise [Errno::ENOENT] if the path doesn't exist - # - # @since 0.3.1 - def self.delete_directory(path) - FileUtils.remove_entry_secure(path) - end - - # Adds a new line at the top of the file - # - # @param path [String,Pathname] the path to file - # @param line [String] the line to add - # - # @raise [Errno::ENOENT] if the path doesn't exist - # - # @see .append - # - # @since 0.3.1 - def self.unshift(path, line) - content = ::File.readlines(path) - content.unshift("#{line}\n") - - write(path, content) - end - - # Adds a new line at the bottom of the file - # - # @param path [String,Pathname] the path to file - # @param contents [String] the contents to add - # - # @raise [Errno::ENOENT] if the path doesn't exist - # - # @see .unshift - # - # @since 0.3.1 - def self.append(path, contents) - mkdir_p(path) - - content = ::File.readlines(path) - content << "\n" unless content.last.end_with?("\n") - content << "#{contents}\n" - - write(path, content) - end - - # Replace first line in `path` that contains `target` with `replacement`. - # - # @param path [String,Pathname] the path to file - # @param target [String,Regexp] the target to replace - # @param replacement [String] the replacement - # - # @raise [Errno::ENOENT] if the path doesn't exist - # @raise [ArgumentError] if `target` cannot be found in `path` - # - # @see .replace_last_line - # - # @since 0.3.1 - def self.replace_first_line(path, target, replacement) - content = ::File.readlines(path) - content[index(content, path, target)] = "#{replacement}\n" - - write(path, content) - end - - # Replace last line in `path` that contains `target` with `replacement`. - # - # @param path [String,Pathname] the path to file - # @param target [String,Regexp] the target to replace - # @param replacement [String] the replacement - # - # @raise [Errno::ENOENT] if the path doesn't exist - # @raise [ArgumentError] if `target` cannot be found in `path` - # - # @see .replace_first_line - # - # @since 0.3.1 - def self.replace_last_line(path, target, replacement) - content = ::File.readlines(path) - content[-index(content.reverse, path, target) - 1] = "#{replacement}\n" - - write(path, content) - end - - # Inject `contents` in `path` before `target`. - # - # @param path [String,Pathname] the path to file - # @param target [String,Regexp] the target to replace - # @param contents [String] the contents to inject - # - # @raise [Errno::ENOENT] if the path doesn't exist - # @raise [ArgumentError] if `target` cannot be found in `path` - # - # @see .inject_line_after - # @see .inject_line_before_last - # @see .inject_line_after_last - # - # @since 0.3.1 - def self.inject_line_before(path, target, contents) - _inject_line_before(path, target, contents, method(:index)) - end - - # Inject `contents` in `path` after last `target`. - # - # @param path [String,Pathname] the path to file - # @param target [String,Regexp] the target to replace - # @param contents [String] the contents to inject - # - # @raise [Errno::ENOENT] if the path doesn't exist - # @raise [ArgumentError] if `target` cannot be found in `path` - # - # @see .inject_line_before - # @see .inject_line_after - # @see .inject_line_after_last - # - # @since 1.3.0 - def self.inject_line_before_last(path, target, contents) - _inject_line_before(path, target, contents, method(:rindex)) - end - - # Inject `contents` in `path` after `target`. - # - # @param path [String,Pathname] the path to file - # @param target [String,Regexp] the target to replace - # @param contents [String] the contents to inject - # - # @raise [Errno::ENOENT] if the path doesn't exist - # @raise [ArgumentError] if `target` cannot be found in `path` - # - # @see .inject_line_before - # @see .inject_line_before_last - # @see .inject_line_after_last - # - # @since 0.3.1 - def self.inject_line_after(path, target, contents) - _inject_line_after(path, target, contents, method(:index)) - end - - # Inject `contents` in `path` after last `target`. - # - # @param path [String,Pathname] the path to file - # @param target [String,Regexp] the target to replace - # @param contents [String] the contents to inject - # - # @raise [Errno::ENOENT] if the path doesn't exist - # @raise [ArgumentError] if `target` cannot be found in `path` - # - # @see .inject_line_before - # @see .inject_line_after - # @see .inject_line_before_last - # @see .inject_line_after_last - # - # @since 1.3.0 - def self.inject_line_after_last(path, target, contents) - _inject_line_after(path, target, contents, method(:rindex)) - end - - # Removes line from `path`, matching `target`. - # - # @param path [String,Pathname] the path to file - # @param target [String,Regexp] the target to remove - # - # @raise [Errno::ENOENT] if the path doesn't exist - # @raise [ArgumentError] if `target` cannot be found in `path` - # - # @since 0.3.1 - def self.remove_line(path, target) - content = ::File.readlines(path) - i = index(content, path, target) - - content.delete_at(i) - write(path, content) - end - - # Removes `target` block from `path` - # - # @param path [String,Pathname] the path to file - # @param target [String] the target block to remove - # - # @raise [Errno::ENOENT] if the path doesn't exist - # @raise [ArgumentError] if `target` cannot be found in `path` - # - # @since 0.3.1 - # - # @example - # require "dry/cli/utils/files" - # - # puts File.read("app.rb") - # - # # class App - # # configure do - # # root __dir__ - # # end - # # end - # - # Dry::CLI::Utils::Files.remove_block("app.rb", "configure") - # - # puts File.read("app.rb") - # - # # class App - # # end - def self.remove_block(path, target) - content = ::File.readlines(path) - starting = index(content, path, target) - line = content[starting] - size = line[/\A[[:space:]]*/].bytesize - closing = (" " * size) + (target.match?(/{/) ? "}" : "end") - ending = starting + index(content[starting..-1], path, closing) - - content.slice!(starting..ending) - write(path, content) - - remove_block(path, target) if match?(content, target) - end - - # Checks if `path` exist - # - # @param path [String,Pathname] the path to file - # - # @return [TrueClass,FalseClass] the result of the check - # - # @since 0.3.1 - # - # @example - # require "dry/cli/utils/files" - # - # Dry::CLI::Utils::Files.exist?(__FILE__) # => true - # Dry::CLI::Utils::Files.exist?(__dir__) # => true - # - # Dry::CLI::Utils::Files.exist?("missing_file") # => false - def self.exist?(path) - File.exist?(path) - end - - # Checks if `path` is a directory - # - # @param path [String,Pathname] the path to directory - # - # @return [TrueClass,FalseClass] the result of the check - # - # @since 0.3.1 - # - # @example - # require "dry/cli/utils/files" - # - # Dry::CLI::Utils::Files.directory?(__dir__) # => true - # Dry::CLI::Utils::Files.directory?(__FILE__) # => false - # - # Dry::CLI::Utils::Files.directory?("missing_directory") # => false - def self.directory?(path) - File.directory?(path) - end - - # private - - # @since 0.3.1 - # @api private - def self.match?(content, target) - !line_number(content, target).nil? - end - - private_class_method :match? - - # @since 0.3.1 - # @api private - def self.open(path, mode, *content) - ::File.open(path, mode) do |file| - file.write(Array(content).flatten.join) - end - end - - private_class_method :open - - # @since 0.3.1 - # @api private - def self.index(content, path, target) - line_number(content, target) || - raise(ArgumentError, "Cannot find `#{target}' inside `#{path}'.") - end - - private_class_method :index - - # @since 1.3.0 - # @api private - def self.rindex(content, path, target) - line_number(content, target, finder: content.method(:rindex)) || - raise(ArgumentError, "Cannot find `#{target}' inside `#{path}'.") - end - - private_class_method :rindex - - # @since 1.3.0 - # @api private - def self._inject_line_before(path, target, contents, finder) - content = ::File.readlines(path) - i = finder.call(content, path, target) - - content.insert(i, "#{contents}\n") - write(path, content) - end - - private_class_method :_inject_line_before - - # @since 1.3.0 - # @api private - def self._inject_line_after(path, target, contents, finder) - content = ::File.readlines(path) - i = finder.call(content, path, target) - - content.insert(i + 1, "#{contents}\n") - write(path, content) - end - - private_class_method :_inject_line_after - - # @since 0.3.1 - # @api private - def self.line_number(content, target, finder: content.method(:index)) - finder.call do |l| - case target - when ::String - l.include?(target) - when Regexp - l =~ target - end - end - end - - private_class_method :line_number - end - end - end -end diff --git a/spec/unit/dry/cli/utils/files_spec.rb b/spec/unit/dry/cli/utils/files_spec.rb deleted file mode 100644 index a2bfc9a..0000000 --- a/spec/unit/dry/cli/utils/files_spec.rb +++ /dev/null @@ -1,1045 +0,0 @@ -# frozen_string_literal: true - -require "dry/cli/utils/files" -require "securerandom" - -RSpec.describe Dry::CLI::Utils::Files do - let(:root) { Pathname.new(Dir.pwd).join("tmp", SecureRandom.uuid).tap(&:mkpath) } - - after do - FileUtils.remove_entry_secure(root) - end - - describe ".touch" do - it "creates an empty file" do - path = root.join("touch") - described_class.touch(path) - - expect(path).to exist - expect(path).to have_content("") - end - - it "creates intermediate directories" do - path = root.join("path", "to", "file", "touch") - described_class.touch(path) - - expect(path).to exist - expect(path).to have_content("") - end - - it "leaves untouched existing file" do - path = root.join("touch") - path.open("wb+") { |p| p.write("foo") } - described_class.touch(path) - - expect(path).to exist - expect(path).to have_content("foo") - end - end - - describe ".write" do - it "creates an file with given contents" do - path = root.join("write") - described_class.write(path, "Hello\nWorld") - - expect(path).to exist - expect(path).to have_content("Hello\nWorld") - end - - it "creates intermediate directories" do - path = root.join("path", "to", "file", "write") - described_class.write(path, ":)") - - expect(path).to exist - expect(path).to have_content(":)") - end - - it "overwrites file when it already exists" do - path = root.join("write") - described_class.write(path, "many many many many words") - described_class.write(path, "new words") - - expect(path).to exist - expect(path).to have_content("new words") - end - end - - describe ".cp" do - let(:source) { root.join("..", "source") } - - before do - source.delete if source.exist? - end - - it "creates a file with given contents" do - described_class.write(source, "the source") - - destination = root.join("cp") - described_class.cp(source, destination) - - expect(destination).to exist - expect(destination).to have_content("the source") - end - - it "creates intermediate directories" do - source = root.join("..", "source") - described_class.write(source, "the source for intermediate directories") - - destination = root.join("cp", "destination") - described_class.cp(source, destination) - - expect(destination).to exist - expect(destination).to have_content("the source for intermediate directories") - end - - it "overrides already existing file" do - source = root.join("..", "source") - described_class.write(source, "the source") - - destination = root.join("cp") - described_class.write(destination, "the destination") - described_class.cp(source, destination) - - expect(destination).to exist - expect(destination).to have_content("the source") - end - end - - describe ".mkdir" do - it "creates directory" do - path = root.join("mkdir") - described_class.mkdir(path) - - expect(path).to be_directory - end - - it "creates intermediate directories" do - path = root.join("path", "to", "mkdir") - described_class.mkdir(path) - - expect(path).to be_directory - end - end - - describe ".mkdir_p" do - it "creates directory" do - directory = root.join("mkdir_p") - path = directory.join("file.rb") - described_class.mkdir_p(path) - - expect(directory).to be_directory - expect(path).to_not exist - end - - it "creates intermediate directories" do - directory = root.join("path", "to", "mkdir_p") - path = directory.join("file.rb") - described_class.mkdir_p(path) - - expect(directory).to be_directory - expect(path).to_not exist - end - end - - describe ".delete" do - it "deletes path" do - path = root.join("delete", "file") - described_class.touch(path) - described_class.delete(path) - - expect(path).to_not exist - end - - it "raises error if path doesn't exist" do - path = root.join("delete", "file") - - expect { described_class.delete(path) }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".delete_directory" do - it "deletes directory" do - path = root.join("delete", "directory") - described_class.mkdir(path) - described_class.delete_directory(path) - - expect(path).to_not exist - end - - it "raises error if directory doesn't exist" do - path = root.join("delete", "directory") - - expect { described_class.delete_directory(path) }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".unshift" do - it "adds a line at the top of the file" do - path = root.join("unshift.rb") - content = <<~CONTENT - class Unshift - end - CONTENT - - described_class.write(path, content) - described_class.unshift(path, "# frozen_string_literal: true") - - expected = <<~CONTENT - # frozen_string_literal: true - class Unshift - end - CONTENT - - expect(path).to have_content(expected) - end - - # https://github.com/hanami/utils/issues/348 - it "adds a line at the top of a file that doesn't end with a newline" do - path = root.join("unshift_missing_newline.rb") - content = "get '/tires', to: 'sunshine#index'" - - described_class.write(path, content) - described_class.unshift(path, "root to: 'home#index'") - - expected = "root to: 'home#index'\nget '/tires', to: 'sunshine#index'" - - expect(path).to have_content(expected) - end - - it "raises error if path doesn't exist" do - path = root.join("unshift_no_exist.rb") - - expect { described_class.unshift(path, "# frozen_string_literal: true") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".append" do - it "adds a line at the bottom of the file" do - path = root.join("append.rb") - content = <<~CONTENT - class Append - end - CONTENT - - described_class.write(path, content) - described_class.append(path, "\nFoo.register Append") - - expected = <<~CONTENT - class Append - end - - Foo.register Append - CONTENT - - expect(path).to have_content(expected) - end - - # https://github.com/hanami/utils/issues/348 - it "adds a line at the bottom of a file that doesn't end with a newline" do - path = root.join("append_missing_newline.rb") - content = "root to: 'home#index'" - - described_class.write(path, content) - described_class.append(path, "get '/tires', to: 'sunshine#index'") - - expected = <<~CONTENT - root to: 'home#index' - get '/tires', to: 'sunshine#index' - CONTENT - - expect(path).to have_content(expected) - end - - it "raises error if path doesn't exist" do - path = root.join("append_no_exist.rb") - - expect { described_class.append(path, "\n Foo.register Append") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".replace_first_line" do - it "replaces string target with replacement" do - path = root.join("replace_string.rb") - content = <<~CONTENT - class Replace - def self.perform - end - end - CONTENT - - described_class.write(path, content) - described_class.replace_first_line(path, "perform", " def self.call(input)") - - expected = <<~CONTENT - class Replace - def self.call(input) - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "replaces regexp target with replacement" do - path = root.join("replace_regexp.rb") - content = <<~CONTENT - class Replace - def self.perform - end - end - CONTENT - - described_class.write(path, content) - described_class.replace_first_line(path, /perform/, " def self.call(input)") - - expected = <<~CONTENT - class Replace - def self.call(input) - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "replaces only the first occurrence of target with replacement" do - path = root.join("replace_first.rb") - content = <<~CONTENT - class Replace - def self.perform - end - - def self.perform - end - end - CONTENT - - described_class.write(path, content) - described_class.replace_first_line(path, "perform", " def self.call(input)") - - expected = <<~CONTENT - class Replace - def self.call(input) - end - - def self.perform - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "raises error if target cannot be found in path" do - path = root.join("replace_not_found.rb") - content = <<~CONTENT - class Replace - def self.perform - end - end - CONTENT - - described_class.write(path, content) - - expect { described_class.replace_first_line(path, "not existing target", " def self.call(input)") }.to raise_error do |exception| - expect(exception).to be_kind_of(ArgumentError) - expect(exception.message).to eq("Cannot find `not existing target' inside `#{path}'.") - end - - expect(path).to have_content(content) - end - - it "raises error if path doesn't exist" do - path = root.join("replace_no_exist.rb") - - expect { described_class.replace_first_line(path, "perform", " def self.call(input)") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".replace_last_line" do - it "replaces string target with replacement" do - path = root.join("replace_last_string.rb") - content = <<~CONTENT - class ReplaceLast - def self.perform - end - end - CONTENT - - described_class.write(path, content) - described_class.replace_last_line(path, "perform", " def self.call(input)") - - expected = <<~CONTENT - class ReplaceLast - def self.call(input) - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "replaces regexp target with replacement" do - path = root.join("replace_last_regexp.rb") - content = <<~CONTENT - class ReplaceLast - def self.perform - end - end - CONTENT - - described_class.write(path, content) - described_class.replace_last_line(path, /perform/, " def self.call(input)") - - expected = <<~CONTENT - class ReplaceLast - def self.call(input) - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "replaces only the last occurrence of target with replacement" do - path = root.join("replace_last.rb") - content = <<~CONTENT - class ReplaceLast - def self.perform - end - - def self.perform - end - end - CONTENT - - described_class.write(path, content) - described_class.replace_last_line(path, "perform", " def self.call(input)") - - expected = <<~CONTENT - class ReplaceLast - def self.perform - end - - def self.call(input) - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "raises error if target cannot be found in path" do - path = root.join("replace_last_not_found.rb") - content = <<~CONTENT - class ReplaceLast - def self.perform - end - end - CONTENT - - described_class.write(path, content) - - expect { described_class.replace_last_line(path, "not existing target", " def self.call(input)") }.to raise_error do |exception| - expect(exception).to be_kind_of(ArgumentError) - expect(exception.message).to eq("Cannot find `not existing target' inside `#{path}'.") - end - - expect(path).to have_content(content) - end - - it "raises error if path doesn't exist" do - path = root.join("replace_last_no_exist.rb") - - expect { described_class.replace_last_line(path, "perform", " def self.call(input)") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".inject_line_before" do - it "injects line before target (string)" do - path = root.join("inject_before_string.rb") - content = <<~CONTENT - class InjectBefore - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.inject_line_before(path, "call", " # It performs the operation") - - expected = <<~CONTENT - class InjectBefore - # It performs the operation - def self.call - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "injects line before target (regexp)" do - path = root.join("inject_before_regexp.rb") - content = <<~CONTENT - class InjectBefore - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.inject_line_before(path, /call/, " # It performs the operation") - - expected = <<~CONTENT - class InjectBefore - # It performs the operation - def self.call - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "raises error if target cannot be found in path" do - path = root.join("inject_before_not_found.rb") - content = <<~CONTENT - class InjectBefore - def self.call - end - end - CONTENT - - described_class.write(path, content) - - expect { described_class.inject_line_before(path, "not existing target", " # It performs the operation") }.to raise_error do |exception| - expect(exception).to be_kind_of(ArgumentError) - expect(exception.message).to eq("Cannot find `not existing target' inside `#{path}'.") - end - - expect(path).to have_content(content) - end - - it "raises error if path doesn't exist" do - path = root.join("inject_before_no_exist.rb") - - expect { described_class.inject_line_before(path, "call", " # It performs the operation") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".inject_line_before_last" do - it "injects line before last target (string)" do - path = root.join("inject_before_last_string.rb") - content = <<~CONTENT - class InjectBefore - def self.call - end - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.inject_line_before_last(path, "call", " # It performs the operation") - - expected = <<~CONTENT - class InjectBefore - def self.call - end - # It performs the operation - def self.call - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "injects line before last target (regexp)" do - path = root.join("inject_before_last_regexp.rb") - content = <<~CONTENT - class InjectBefore - def self.call - end - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.inject_line_before_last(path, /call/, " # It performs the operation") - - expected = <<~CONTENT - class InjectBefore - def self.call - end - # It performs the operation - def self.call - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "raises error if target cannot be found in path" do - path = root.join("inject_before_last_not_found.rb") - content = <<~CONTENT - class InjectBefore - def self.call - end - def self.call - end - end - CONTENT - - described_class.write(path, content) - - expect { described_class.inject_line_before_last(path, "not existing target", " # It performs the operation") }.to raise_error do |exception| - expect(exception).to be_kind_of(ArgumentError) - expect(exception.message).to eq("Cannot find `not existing target' inside `#{path}'.") - end - - expect(path).to have_content(content) - end - - it "raises error if path doesn't exist" do - path = root.join("inject_before_last_no_exist.rb") - - expect { described_class.inject_line_before_last(path, "call", " # It performs the operation") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".inject_line_after" do - it "injects line after target (string)" do - path = root.join("inject_after.rb") - content = <<~CONTENT - class InjectAfter - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.inject_line_after(path, "call", " :result") - - expected = <<~CONTENT - class InjectAfter - def self.call - :result - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "injects line after target (regexp)" do - path = root.join("inject_after.rb") - content = <<~CONTENT - class InjectAfter - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.inject_line_after(path, /call/, " :result") - - expected = <<~CONTENT - class InjectAfter - def self.call - :result - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "raises error if target cannot be found in path" do - path = root.join("inject_after_not_found.rb") - content = <<~CONTENT - class InjectAfter - def self.call - end - end - CONTENT - - described_class.write(path, content) - - expect { described_class.inject_line_after(path, "not existing target", " :result") }.to raise_error do |exception| - expect(exception).to be_kind_of(ArgumentError) - expect(exception.message).to eq("Cannot find `not existing target' inside `#{path}'.") - end - - expect(path).to have_content(content) - end - - it "raises error if path doesn't exist" do - path = root.join("inject_after_no_exist.rb") - - expect { described_class.inject_line_after(path, "call", " :result") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".inject_line_after_last" do - it "injects line after last target (string)" do - path = root.join("inject_after_last.rb") - content = <<~CONTENT - class InjectAfter - def self.call - end - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.inject_line_after_last(path, "call", " :result") - - expected = <<~CONTENT - class InjectAfter - def self.call - end - def self.call - :result - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "injects line after last target (regexp)" do - path = root.join("inject_after_last.rb") - content = <<~CONTENT - class InjectAfter - def self.call - end - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.inject_line_after_last(path, /call/, " :result") - - expected = <<~CONTENT - class InjectAfter - def self.call - end - def self.call - :result - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "raises error if target cannot be found in path" do - path = root.join("inject_after_last_not_found.rb") - content = <<~CONTENT - class InjectAfter - def self.call - end - def self.call - end - end - CONTENT - - described_class.write(path, content) - - expect { described_class.inject_line_after_last(path, "not existing target", " :result") }.to raise_error do |exception| - expect(exception).to be_kind_of(ArgumentError) - expect(exception.message).to eq("Cannot find `not existing target' inside `#{path}'.") - end - - expect(path).to have_content(content) - end - - it "raises error if path doesn't exist" do - path = root.join("inject_after_last_no_exist.rb") - - expect { described_class.inject_line_after_last(path, "call", " :result") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".remove_line" do - it "removes line (string)" do - path = root.join("remove_line_string.rb") - content = <<~CONTENT - # frozen_string_literal: true - class RemoveLine - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.remove_line(path, "frozen") - - expected = <<~CONTENT - class RemoveLine - def self.call - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "removes line (regexp)" do - path = root.join("remove_line_regexp.rb") - content = <<~CONTENT - # frozen_string_literal: true - class RemoveLine - def self.call - end - end - CONTENT - - described_class.write(path, content) - described_class.remove_line(path, /frozen/) - - expected = <<~CONTENT - class RemoveLine - def self.call - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "raises error if target cannot be found in path" do - path = root.join("remove_line_not_found.rb") - content = <<~CONTENT - # frozen_string_literal: true - class RemoveLine - def self.call - end - end - CONTENT - - described_class.write(path, content) - - expect { described_class.remove_line(path, "not existing target") }.to raise_error do |exception| - expect(exception).to be_kind_of(ArgumentError) - expect(exception.message).to eq("Cannot find `not existing target' inside `#{path}'.") - end - - expect(path).to have_content(content) - end - - it "raises error if path doesn't exist" do - path = root.join("remove_line_no_exist.rb") - - expect { described_class.remove_line(path, "frozen") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - - expect(path).to_not exist - end - end - - describe ".remove_block" do - it "removes block from Ruby file" do - path = root.join("remove_block_simple.rb") - content = <<~CONTENT - class RemoveBlock - configure do - root __dir__ - end - end - CONTENT - - described_class.write(path, content) - described_class.remove_block(path, "configure") - - expected = <<~CONTENT - class RemoveBlock - end - CONTENT - - expect(path).to have_content(expected) - end - - it "removes nested block from Ruby file" do - path = root.join("remove_block_simple.rb") - content = <<~CONTENT - class RemoveBlock - configure do - root __dir__ - - assets do - sources << [ - "path/to/sources" - ] - end - end - end - CONTENT - - described_class.write(path, content) - described_class.remove_block(path, "assets") - - expected = <<~CONTENT - class RemoveBlock - configure do - root __dir__ - - end - end - CONTENT - - expect(path).to have_content(expected) - end - - it "raises error if block cannot be found in path" do - path = root.join("remove_block_not_found.rb") - content = <<~CONTENT - class RemoveBlock - configure do - root __dir__ - end - end - CONTENT - - described_class.write(path, content) - - expect { described_class.remove_block(path, "not existing target") }.to raise_error do |exception| - expect(exception).to be_kind_of(ArgumentError) - expect(exception.message).to eq("Cannot find `not existing target' inside `#{path}'.") - end - - expect(path).to have_content(content) - end - - it "raises error if block cannot be found" do - path = root.join("remove_block_string_simple.rb") - content = <<~CONTENT - class RemoveBlock - configure do - root __dir__ - end - end - CONTENT - - described_class.write(path, content) - - expect { described_class.remove_block(path, "not existing target") }.to raise_error do |exception| - expect(exception).to be_kind_of(ArgumentError) - expect(exception.message).to eq("Cannot find `not existing target' inside `#{path}'.") - end - - expect(path).to have_content(content) - end - - it "raises an error when the file was not found" do - path = root.join("remove_block_not_found.rb") - - expect { described_class.remove_block(path, "configure") }.to raise_error do |exception| - expect(exception).to be_kind_of(Errno::ENOENT) - expect(exception.message).to match("No such file or directory") - end - end - end - - describe ".exist?" do - it "returns true for file" do - path = root.join("exist_file") - described_class.touch(path) - - expect(described_class.exist?(path)).to be(true) - end - - it "returns true for directory" do - path = root.join("exist_directory") - described_class.mkdir(path) - - expect(described_class.exist?(path)).to be(true) - end - - it "returns false for non-existing file" do - path = root.join("exist_not_found") - - expect(described_class.exist?(path)).to be(false) - end - end - - describe ".directory?" do - it "returns true for directory" do - path = root.join("directory_directory") - described_class.mkdir(path) - - expect(described_class.exist?(path)).to be(true) - end - - it "returns false for file" do - path = root.join("directory_file") - described_class.touch(path) - - expect(described_class.directory?(path)).to be(false) - end - - it "returns false for non-existing path" do - path = root.join("directory_not_found") - - expect(described_class.exist?(path)).to be(false) - end - end -end