class Dry::Files
File manipulations
@since 0.1.0 @api public
Constants
- CLOSE_BLOCK
@since 0.1.0 @api private
- CONTENT_OFFSET
@since 0.1.0 @api private
- INDENTATION
@since 0.1.0 @api private
- INLINE_CLOSE_BLOCK
@since 0.1.0 @api private
- INLINE_OPEN_BLOCK_MATCHER
@since 0.1.0 @api private
- NEW_LINE
@since 0.1.0 @api private
- SPACE
@since 0.1.0 @api private
- SPACE_MATCHER
@since 0.1.0 @api private
- VERSION
Attributes
@since 0.1.0 @api private
Public Class Methods
Creates a new instance
Memory file system is experimental
@param memory [TrueClass,FalseClass] use in-memory, ephemeral file system @param adapter [Dry::FileSystem]
@return [Dry::Files] a new files instance
@since 0.1.0 @api public
# File lib/dry/files.rb, line 28 def initialize(memory: false, adapter: Adapter.call(memory: memory)) @adapter = adapter end
Public Instance Methods
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 [Dry::Files::IOError] in case of I/O error
@see unshift
@since 0.1.0 @api public
# File lib/dry/files.rb, line 308 def append(path, contents) mkdir_p(path) content = adapter.readlines(path) content << newline unless newline?(content.last) content << newline(contents) write(path, content) end
Temporary changes the current working directory of the process to the given path and yield the given block.
@param path [String,Pathname] the target directory @param blk [Proc] the code to execute with the target directory
@raise [Dry::Files::IOError] in case of I/O error
@since 0.1.0
# File lib/dry/files.rb, line 123 def chdir(path, &blk) adapter.chdir(path, &blk) 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
@raise [Dry::Files::IOError] in case of I/O error
@since 0.1.0 @api public
# File lib/dry/files.rb, line 191 def cp(source, destination) adapter.cp(source, destination) end
Deletes given path (file).
@param path [String,Pathname] the path to file
@raise [Dry::Files::IOError] in case of I/O error
@since 0.1.0 @api public
# File lib/dry/files.rb, line 203 def delete(path) adapter.rm(path) end
Deletes given path (directory).
@param path [String,Pathname] the path to file
@raise [Dry::Files::IOError] in case of I/O error
@since 0.1.0 @api public
# File lib/dry/files.rb, line 215 def delete_directory(path) adapter.rm_rf(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.1.0 @api public
@example
require "dry/files" Dry::Files.new.directory?(__dir__) # => true Dry::Files.new.directory?(__FILE__) # => false Dry::Files.new.directory?("missing_directory") # => false
# File lib/dry/files.rb, line 255 def directory?(path) adapter.directory?(path) end
Checks if `path` is an executable
@param path [String,Pathname] the path to file
@return [TrueClass,FalseClass] the result of the check
@since 0.1.0 @api public
@example
require "dry/files" Dry::Files.new.executable?("/path/to/ruby") # => true Dry::Files.new.executable?(__FILE__) # => false Dry::Files.new.directory?("missing_file") # => false
# File lib/dry/files.rb, line 275 def executable?(path) adapter.executable?(path) end
Checks if `path` exist
@param path [String,Pathname] the path to file
@return [TrueClass,FalseClass] the result of the check
@since 0.1.0 @api public
@example
require "dry/files" Dry::Files.new.exist?(__FILE__) # => true Dry::Files.new.exist?(__dir__) # => true Dry::Files.new.exist?("missing_file") # => false
# File lib/dry/files.rb, line 235 def exist?(path) adapter.exist?(path) end
Converts a path to an absolute path.
Relative paths are referenced from the current working directory of the process unless `dir` is given.
@param path [String,Pathname] the path to the file @param dir [String,Pathname] the base directory
@return [String] the expanded path
@since 0.1.0
# File lib/dry/files.rb, line 101 def expand_path(path, dir = pwd) adapter.expand_path(path, dir) 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 [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@see inject_line_before
@see inject_line_before_last
@see inject_line_after_last
@since 0.1.0 @api public
# File lib/dry/files.rb, line 411 def 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 [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@see inject_line_before
@see inject_line_after
@see inject_line_before_last
@since 0.1.0 @api public
# File lib/dry/files.rb, line 430 def inject_line_after_last(path, target, contents) _inject_line_after(path, target, contents, method(:rindex)) end
Inject `contents` in `path` within the first Ruby block that matches `target`. The given `contents` will appear at the BOTTOM of the Ruby block.
@param path [String,Pathname] the path to file @param target [String,Regexp] the target matcher for Ruby block @param contents [String,Array<String>] the contents to inject
@raise [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@since 0.1.0 @api public
@example Inject a single line
require "dry/files" files = Dry::Files.new path = "config/application.rb" File.read(path) # # frozen_string_literal: true # # class Application # configure do # root __dir__ # end # end # inject a single line files.inject_line_at_block_bottom(path, /configure/, %(load_path.unshift("lib"))) File.read(path) # # frozen_string_literal: true # # class Application # configure do # root __dir__ # load_path.unshift("lib") # end # end
@example Inject multiple lines
require "dry/files" files = Dry::Files.new path = "config/application.rb" File.read(path) # # frozen_string_literal: true # # class Application # configure do # root __dir__ # end # end # inject multiple lines files.inject_line_at_block_bottom(path, /configure/, [%(load_path.unshift("lib")), "settings.load!"]) File.read(path) # # frozen_string_literal: true # # class Application # configure do # root __dir__ # load_path.unshift("lib") # settings.load! # end # end
@example Inject a block
require "dry/files" files = Dry::Files.new path = "config/application.rb" File.read(path) # # frozen_string_literal: true # # class Application # configure do # root __dir__ # end # end # inject a block block = <<~BLOCK settings do load! end BLOCK files.inject_line_at_block_bottom(path, /configure/, block) File.read(path) # # frozen_string_literal: true # # class Application # configure do # root __dir__ # settings do # load! # end # end # end
# File lib/dry/files.rb, line 658 def inject_line_at_block_bottom(path, target, *contents) content = adapter.readlines(path) starting = index(content, path, target) line = content[starting] size = line[SPACE_MATCHER].bytesize closing = (SPACE * size) + (target.match?(INLINE_OPEN_BLOCK_MATCHER) ? INLINE_CLOSE_BLOCK : CLOSE_BLOCK) ending = starting + index(content[starting..-CONTENT_OFFSET], path, closing) offset = SPACE * (content[ending][SPACE_MATCHER].bytesize + INDENTATION) contents = Array(contents).flatten contents = _offset_block_lines(contents, offset) content.insert(ending, contents) write(path, content) end
Inject `contents` in `path` within the first Ruby block that matches `target`. The given `contents` will appear at the TOP of the Ruby block.
@param path [String,Pathname] the path to file @param target [String,Regexp] the target matcher for Ruby block @param contents [String,Array<String>] the contents to inject
@raise [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@since 0.1.0 @api public
@example Inject a single line
require "dry/files" files = Dry::Files.new path = "config/application.rb" File.read(path) # # frozen_string_literal: true # # class Application # configure do # root __dir__ # end # end # inject a single line files.inject_line_at_block_top(path, /configure/, %(load_path.unshift("lib"))) File.read(path) # # frozen_string_literal: true # # class Application # configure do # load_path.unshift("lib") # root __dir__ # end # end
@example Inject multiple lines
require "dry/files" files = Dry::Files.new path = "config/application.rb" File.read(path) # # frozen_string_literal: true # # class Application # configure do # root __dir__ # end # end # inject multiple lines files.inject_line_at_block_top(path, /configure/, [%(load_path.unshift("lib")), "settings.load!"]) File.read(path) # # frozen_string_literal: true # # class Application # configure do # load_path.unshift("lib") # settings.load! # root __dir__ # end # end
@example Inject a block
require "dry/files" files = Dry::Files.new path = "config/application.rb" File.read(path) # # frozen_string_literal: true # # class Application # configure do # root __dir__ # end # end # inject a block block = <<~BLOCK settings do load! end BLOCK files.inject_line_at_block_top(path, /configure/, block) File.read(path) # # frozen_string_literal: true # # class Application # configure do # settings do # load! # end # root __dir__ # end # end
# File lib/dry/files.rb, line 540 def inject_line_at_block_top(path, target, *contents) content = adapter.readlines(path) starting = index(content, path, target) offset = SPACE * (content[starting][SPACE_MATCHER].bytesize + INDENTATION) contents = Array(contents).flatten contents = _offset_block_lines(contents, offset) content.insert(starting + CONTENT_OFFSET, contents) 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 [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@see inject_line_after
@see inject_line_before_last
@see inject_line_after_last
@since 0.1.0 @api public
# File lib/dry/files.rb, line 373 def 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 [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@see inject_line_before
@see inject_line_after
@see inject_line_after_last
@since 0.1.0 @api public
# File lib/dry/files.rb, line 392 def inject_line_before_last(path, target, contents) _inject_line_before(path, target, contents, method(:rindex)) end
Returns a new string formed by joining the strings using Operating System path separator
@param path [Array<String,Pathname>] path tokens
@return [String] the joined path
@since 0.1.0 @api public
# File lib/dry/files.rb, line 86 def join(*path) adapter.join(*path) 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
@raise [Dry::Files::IOError] in case of I/O error
@since 0.1.0 @api public
@see mkdir_p
@example
require "dry/files" Dry::Files.new.mkdir("path/to/directory") # => creates the `path/to/directory` directory # WRONG this isn't probably what you want, check `.mkdir_p` Dry::Files.new.mkdir("path/to/file.rb") # => creates the `path/to/file.rb` directory
# File lib/dry/files.rb, line 149 def mkdir(path) adapter.mkdir(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
@raise [Dry::Files::IOError] in case of I/O error
@since 0.1.0 @api public
@see mkdir
@example
require "dry/files" Dry::Files.new.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::Files.new.mkdir_p("path/to/directory") # => creates the `path/to` directory
# File lib/dry/files.rb, line 176 def mkdir_p(path) adapter.mkdir_p(path) end
Returns the name of the current working directory.
@return [String] the current working directory.
@since 0.1.0
# File lib/dry/files.rb, line 110 def pwd adapter.pwd end
Read file content
@param path [String,Pathname] the path to file
@return [String] the file contents
@raise [Dry::Files::IOError] in case of I/O error
@since 0.1.0 @api public
TODO: allow buffered read
# File lib/dry/files.rb, line 44 def read(path) adapter.read(path) end
Removes `target` block from `path`
@param path [String,Pathname] the path to file @param target [String] the target block to remove
@raise [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@since 0.1.0 @api public
@example
require "dry/files" puts File.read("app.rb") # class App # configure do # root __dir__ # end # end Dry::Files.new.remove_block("app.rb", "configure") puts File.read("app.rb") # class App # end
# File lib/dry/files.rb, line 721 def remove_block(path, target) content = adapter.readlines(path) starting = index(content, path, target) line = content[starting] size = line[SPACE_MATCHER].bytesize closing = (SPACE * size) + (target.match?(INLINE_OPEN_BLOCK_MATCHER) ? INLINE_CLOSE_BLOCK : CLOSE_BLOCK) ending = starting + index(content[starting..-CONTENT_OFFSET], path, closing) content.slice!(starting..ending) write(path, content) remove_block(path, target) if match?(content, target) end
Removes line from `path`, matching `target`.
@param path [String,Pathname] the path to file @param target [String,Regexp] the target to remove
@raise [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@since 0.1.0 @api public
# File lib/dry/files.rb, line 685 def remove_line(path, target) content = adapter.readlines(path) i = index(content, path, target) content.delete_at(i) 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 [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@see replace_last_line
@since 0.1.0 @api public
# File lib/dry/files.rb, line 331 def replace_first_line(path, target, replacement) content = adapter.readlines(path) content[index(content, path, target)] = newline(replacement) 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 [Dry::Files::IOError] in case of I/O error @raise [Dry::Files::MissingTargetError] if `target` cannot be found in `path`
@see replace_first_line
@since 0.1.0 @api public
# File lib/dry/files.rb, line 351 def replace_last_line(path, target, replacement) content = adapter.readlines(path) content[-index(content.reverse, path, target) - CONTENT_OFFSET] = newline(replacement) write(path, content) end
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
@raise [Dry::Files::IOError] in case of I/O error
@since 0.1.0 @api public
# File lib/dry/files.rb, line 58 def touch(path) adapter.touch(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 [Dry::Files::IOError] in case of I/O error
@see append
@since 0.1.0 @api public
# File lib/dry/files.rb, line 290 def unshift(path, line) content = adapter.readlines(path) content.unshift(newline(line)) write(path, content) 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<String>] the content to write
@raise [Dry::Files::IOError] in case of I/O error
@since 0.1.0 @api public
# File lib/dry/files.rb, line 73 def write(path, *content) adapter.write(path, *content) end
Private Instance Methods
@since 0.1.0 @api private
# File lib/dry/files.rb, line 826 def _inject_line_after(path, target, contents, finder) content = adapter.readlines(path) i = finder.call(content, path, target) content.insert(i + CONTENT_OFFSET, newline(contents)) write(path, content) end
@since 0.1.0 @api private
# File lib/dry/files.rb, line 816 def _inject_line_before(path, target, contents, finder) content = adapter.readlines(path) i = finder.call(content, path, target) content.insert(i, newline(contents)) write(path, content) end
@since 0.1.0 @api private
# File lib/dry/files.rb, line 836 def _offset_block_lines(contents, offset) contents.map do |line| if line.match?(NEW_LINE) line = line.split(NEW_LINE) _offset_block_lines(line, offset) else offset + line + NEW_LINE end end.join end
@since 0.1.0 @api private
# File lib/dry/files.rb, line 802 def index(content, path, target) line_number(content, target) or raise MissingTargetError.new(target, path) end
@since 0.1.0 @api private
# File lib/dry/files.rb, line 849 def 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
@since 0.1.0 @api private
# File lib/dry/files.rb, line 796 def match?(content, target) !line_number(content, target).nil? end
@since 0.1.0 @api private
# File lib/dry/files.rb, line 784 def newline(line = nil) "#{line}#{NEW_LINE}" end
@since 0.1.0 @api private
# File lib/dry/files.rb, line 790 def newline?(content) content.end_with?(NEW_LINE) end
@since 0.1.0 @api private
# File lib/dry/files.rb, line 809 def rindex(content, path, target) line_number(content, target, finder: content.method(:rindex)) or raise MissingTargetError.new(target, path) end