Class: Staticky::Files

Inherits:
Object
  • Object
show all
Defined in:
lib/staticky/files.rb,
lib/staticky/files/path.rb,
lib/staticky/files/error.rb,
lib/staticky/files/adapter.rb,
lib/staticky/files/version.rb,
lib/staticky/files/file_system.rb,
lib/staticky/files/memory_file_system.rb,
lib/staticky/files/memory_file_system/node.rb

Overview

rubocop:disable Metrics/ClassLength

Defined Under Namespace

Modules: Path Classes: Adapter, Delimiter, Error, FileSystem, IOError, MemoryFileSystem, MissingTargetError, NotMemoryFileError, UnknownMemoryNodeError

Constant Summary collapse

OPEN_MODE =
::File::RDWR
WRITE_MODE =
(::File::CREAT | ::File::WRONLY | ::File::TRUNC).freeze
VERSION =
"0.2.1"

Instance Method Summary collapse

Constructor Details

#initialize(memory: false, adapter: Adapter.call(memory:)) ⇒ Staticky::Files

Creates a new instance

Memory file system is experimental

Parameters:

  • (defaults to: false)

    use in-memory, ephemeral file system

  • (defaults to: Adapter.call(memory:))


20
21
22
# File 'lib/staticky/files.rb', line 20

def initialize(memory: false, adapter: Adapter.call(memory:))
  @adapter = adapter
end

Instance Method Details

#append(path, contents) ⇒ Object

Adds a new line at the bottom of the file

Raises:

  • in case of I/O error

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the contents to add

API:

  • public



317
318
319
320
321
322
323
324
325
326
# File 'lib/staticky/files.rb', line 317

def append(path, contents)
  mkdir_p(path)
  touch(path)

  content = adapter.readlines(path)
  content << newline unless newline?(content.last)
  content << newline(contents)

  write(path, content)
end

#chdir(path) ⇒ Object

Temporary changes the current working directory of the process to the given path and yield the given block.

Raises:

  • in case of I/O error

Parameters:

  • the target directory

  • the code to execute with the target directory



132
133
134
# File 'lib/staticky/files.rb', line 132

def chdir(path, &)
  adapter.chdir(path, &)
end

#chmod(path, mode) ⇒ Object

Sets UNIX permissions of the file at the given path.

Accepts permissions in numeric mode only, best provided as octal numbers matching the standard UNIX octal permission modes, such as ‘0o544` for a file writeable by its owner and readable by others, or `0o755` for a file writeable by its owner and executable by everyone.

Raises:

  • in case of I/O error

Parameters:

  • the path to the file

  • the UNIX permissions mode



70
71
72
73
74
75
76
77
# File 'lib/staticky/files.rb', line 70

def chmod(path, mode)
  unless mode.is_a?(Integer)
    raise Staticky::Files::Error,
      "mode should be an integer (e.g. 0o755)"
  end

  adapter.chmod(path, mode)
end

#cp(source, destination) ⇒ Object

Copies source into destination. All the intermediate directories are created. If the destination already exists, it overrides the contents.

Raises:

  • in case of I/O error

Since:

  • 0.1.0

Parameters:

  • the path to the source file

  • the path to the destination file

API:

  • public



200
201
202
# File 'lib/staticky/files.rb', line 200

def cp(source, destination)
  adapter.cp(source, destination)
end

#delete(path) ⇒ Object

Deletes given path (file).

Raises:

  • in case of I/O error

Since:

  • 0.1.0

Parameters:

  • the path to file

API:

  • public



212
213
214
# File 'lib/staticky/files.rb', line 212

def delete(path)
  adapter.rm(path)
end

#delete_directory(path) ⇒ Object

Deletes given path (directory).

Raises:

  • in case of I/O error

Since:

  • 0.1.0

Parameters:

  • the path to file

API:

  • public



224
225
226
# File 'lib/staticky/files.rb', line 224

def delete_directory(path)
  adapter.rm_rf(path)
end

#directory?(path) ⇒ TrueClass, FalseClass

Checks if ‘path` is a directory

Examples:

require "staticky/files"

Staticky::Files.new.directory?(__dir__)  # => true
Staticky::Files.new.directory?(__FILE__) # => false

Staticky::Files.new.directory?("missing_directory") # => false

Since:

  • 0.1.0

Parameters:

  • the path to directory

Returns:

  • the result of the check

API:

  • public



264
265
266
# File 'lib/staticky/files.rb', line 264

def directory?(path)
  adapter.directory?(path)
end

#entries(path) ⇒ Object

Reads entries from a directory

Raises:

  • in case of I/O error

Since:

  • 0.1.0

Parameters:

  • the path to file

API:

  • public



843
844
845
# File 'lib/staticky/files.rb', line 843

def entries(path)
  adapter.entries(path)
end

#executable?(path) ⇒ TrueClass, FalseClass

Checks if ‘path` is an executable

Examples:

require "staticky/files"

Staticky::Files.new.executable?("/path/to/ruby") # => true
Staticky::Files.new.executable?(__FILE__)        # => false

Staticky::Files.new.directory?("missing_file") # => false

Since:

  • 0.1.0

Parameters:

  • the path to file

Returns:

  • the result of the check

API:

  • public



284
285
286
# File 'lib/staticky/files.rb', line 284

def executable?(path)
  adapter.executable?(path)
end

#exist?(path) ⇒ TrueClass, FalseClass

Checks if ‘path` exist

Examples:

require "staticky/files"

Staticky::Files.new.exist?(__FILE__) # => true
Staticky::Files.new.exist?(__dir__)  # => true

Staticky::Files.new.exist?("missing_file") # => false

Since:

  • 0.1.0

Parameters:

  • the path to file

Returns:

  • the result of the check

API:

  • public



244
245
246
# File 'lib/staticky/files.rb', line 244

def exist?(path)
  adapter.exist?(path)
end

#expand_path(path, dir = pwd) ⇒ String

Converts a path to an absolute path.

Relative paths are referenced from the current working directory of the process unless ‘dir` is given.

Parameters:

  • the path to the file

  • (defaults to: pwd)

    the base directory

Returns:

  • the expanded path



98
99
100
# File 'lib/staticky/files.rb', line 98

def expand_path(path, dir = pwd)
  adapter.expand_path(path, dir)
end

#glob(pattern) ⇒ Object

Reads files matching the given glob pattern

Since:

  • 0.1.0

Parameters:

  • the glob pattern

API:

  • public



853
854
855
# File 'lib/staticky/files.rb', line 853

def glob(pattern)
  adapter.glob(pattern)
end

#inject_line_after(path, target, contents) ⇒ Object

Inject ‘contents` in `path` after `target`.

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target to replace

  • the contents to inject

API:

  • public



422
423
424
# File 'lib/staticky/files.rb', line 422

def inject_line_after(path, target, contents)
  _inject_line_after(path, target, contents, method(:index))
end

#inject_line_after_last(path, target, contents) ⇒ Object

Inject ‘contents` in `path` after last `target`.

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target to replace

  • the contents to inject

API:

  • public



441
442
443
# File 'lib/staticky/files.rb', line 441

def inject_line_after_last(path, target, contents)
  _inject_line_after(path, target, contents, method(:rindex))
end

#inject_line_at_block_bottom(path, target, *contents) ⇒ Object

Inject ‘contents` in `path` within the first Ruby block that matches `target`. The given `contents` will appear at the BOTTOM of the Ruby block.

Examples:

Inject a single line

require "staticky/files"

files = Staticky::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

Inject multiple lines

require "staticky/files"

files = Staticky::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

Inject a block

require "staticky/files"

files = Staticky::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 = "settings do\n  load!\nend\n"
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

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target matcher for Ruby block

  • the contents to inject

API:

  • public



669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
# File 'lib/staticky/files.rb', line 669

def inject_line_at_block_bottom(path, target, *contents)
  content   = adapter.readlines(path)
  starting  = index(content, path, target)
  line      = content[starting]
  delimiter = if line.match?(INLINE_OPEN_BLOCK_MATCHER)
    INLINE_BLOCK_DELIMITER
  else
    BLOCK_DELIMITER
  end
  target    = content[starting..]
  ending    = closing_block_index(target, starting, path, line, delimiter)
  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_line_at_block_top(path, target, *contents) ⇒ Object

Inject ‘contents` in `path` within the first Ruby block that matches `target`. The given `contents` will appear at the TOP of the Ruby block.

Examples:

Inject a single line

require "staticky/files"

files = Staticky::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

Inject multiple lines

require "staticky/files"

files = Staticky::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

Inject a block

require "staticky/files"

files = Staticky::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 = "settings do\n  load!\nend\n"
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

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target matcher for Ruby block

  • the contents to inject

API:

  • public



551
552
553
554
555
556
557
558
559
560
561
# File 'lib/staticky/files.rb', line 551

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_line_at_class_bottom(path, target, *contents) ⇒ Object

Inject ‘contents` in `path` at the bottom of the Ruby class that matches `target`. The given `contents` will appear at the BOTTOM of the Ruby class.

Examples:

Inject a single line

require "staticky/files"

files = Staticky::Files.new
path = "config/application.rb"

File.read(path)
# # frozen_string_literal: true
#
# class Application
# end

# inject a single line
files.inject_line_at_class_bottom(path, /Application/, %(attr_accessor :name))

File.read(path)
# # frozen_string_literal: true
#
# class Application
#   attr_accessor :name
# end

Inject multiple lines

require "staticky/files"

files = Staticky::Files.new
path = "math.rb"

File.read(path)
# # frozen_string_literal: true
#
# class Math
# end

# inject multiple lines
files.inject_line_at_class_bottom(path,
                                  /Math/,
                                  ["def sum(a, b)", "  a + b", "end"])

File.read(path)
# # frozen_string_literal: true
#
# class Math
#   def sum(a, b)
#     a + b
#   end
# end

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

Since:

  • 0.4.0

Parameters:

  • the path to file

  • the target matcher for Ruby class

  • the contents to inject

API:

  • public



749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
# File 'lib/staticky/files.rb', line 749

def inject_line_at_class_bottom(path, target, *contents)
  content   = adapter.readlines(path)
  starting  = index(content, path, target)
  line      = content[starting]
  target    = content[starting..]
  ending    = closing_class_index(
    target,
    starting,
    path,
    line,
    BLOCK_DELIMITER
  )
  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_line_before(path, target, contents) ⇒ Object

Inject ‘contents` in `path` before `target`.

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target to replace

  • the contents to inject

API:

  • public



384
385
386
# File 'lib/staticky/files.rb', line 384

def inject_line_before(path, target, contents)
  _inject_line_before(path, target, contents, method(:index))
end

#inject_line_before_last(path, target, contents) ⇒ Object

Inject ‘contents` in `path` after last `target`.

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target to replace

  • the contents to inject

API:

  • public



403
404
405
# File 'lib/staticky/files.rb', line 403

def inject_line_before_last(path, target, contents)
  _inject_line_before(path, target, contents, method(:rindex))
end

#join(*path) ⇒ String

Returns a new string formed by joining the strings using Operating System path separator

Parameters:

  • path tokens

Returns:

  • the joined path



85
86
87
# File 'lib/staticky/files.rb', line 85

def join(*path)
  adapter.join(*path)
end

#mkdir(path) ⇒ Object

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.

Examples:

require "staticky/files"

Staticky::Files.new.mkdir("path/to/directory")
  # => creates the `path/to/directory` directory

# WRONG this isn't probably what you want, check `.mkdir_p`
Staticky::Files.new.mkdir("path/to/file.rb")
  # => creates the `path/to/file.rb` directory

Raises:

  • in case of I/O error

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to directory

API:

  • public



158
159
160
# File 'lib/staticky/files.rb', line 158

def mkdir(path)
  adapter.mkdir(path)
end

#mkdir_p(path) ⇒ Object

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.

Examples:

require "staticky/files"

Staticky::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`
Staticky::Files.new.mkdir_p("path/to/directory")
  # => creates the `path/to` directory

Raises:

  • in case of I/O error

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to directory

API:

  • public



185
186
187
# File 'lib/staticky/files.rb', line 185

def mkdir_p(path)
  adapter.mkdir_p(path)
end

#open(path, mode = OPEN_MODE) {|the| ... } ⇒ File, Staticky::Files::MemoryFileSystem::Node

Opens (or creates) a new file for both read/write operations

Yield Parameters:

Raises:

  • in case of I/O error

Parameters:

  • the target file

  • (defaults to: OPEN_MODE)

    Ruby file open mode

  • ::File.open args

  • the block to yield

Returns:

  • the opened file



121
122
123
# File 'lib/staticky/files.rb', line 121

def open(path, mode = OPEN_MODE, ...)
  adapter.open(path, mode, ...)
end

#pwdString

Returns the name of the current working directory.

Returns:

  • the current working directory.



105
106
107
# File 'lib/staticky/files.rb', line 105

def pwd
  adapter.pwd
end

#read(path) ⇒ String

Read file content

TODO: allow buffered read

Raises:

  • in case of I/O error

Parameters:

  • the path to file

Returns:

  • the file contents



32
33
34
# File 'lib/staticky/files.rb', line 32

def read(path)
  adapter.read(path)
end

#remove_block(path, target) ⇒ Object

Removes ‘target` block from `path`

Examples:

require "staticky/files"

puts File.read("app.rb")

# class App
#   configure do
#     root __dir__
#   end
# end

Staticky::Files.new.remove_block("app.rb", "configure")

puts File.read("app.rb")

# class App
# end

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target block to remove

API:

  • public



816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
# File 'lib/staticky/files.rb', line 816

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

#remove_line(path, target) ⇒ Object

Removes line from ‘path`, matching `target`.

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target to remove

API:

  • public



780
781
782
783
784
785
786
# File 'lib/staticky/files.rb', line 780

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(path, target, replacement) ⇒ Object

Replace first line in ‘path` that contains `target` with `replacement`.

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target to replace

  • the replacement

API:

  • public



341
342
343
344
345
346
# File 'lib/staticky/files.rb', line 341

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(path, target, replacement) ⇒ Object

Replace last line in ‘path` that contains `target` with `replacement`.

Raises:

  • in case of I/O error

  • if ‘target` cannot be found in `path`

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the target to replace

  • the replacement

API:

  • public



361
362
363
364
365
366
367
# File 'lib/staticky/files.rb', line 361

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

#touch(path) ⇒ Object

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

Raises:

  • in case of I/O error

Parameters:

  • the path to file



43
44
45
# File 'lib/staticky/files.rb', line 43

def touch(path)
  adapter.touch(path)
end

#unshift(path, line) ⇒ Object

Adds a new line at the top of the file

Raises:

  • in case of I/O error

See Also:

Since:

  • 0.1.0

Parameters:

  • the path to file

  • the line to add

API:

  • public



299
300
301
302
303
304
# File 'lib/staticky/files.rb', line 299

def unshift(path, line)
  content = adapter.readlines(path)
  content.unshift(newline(line))

  write(path, content)
end

#write(path, *content) ⇒ Object

Creates a new file or rewrites the contents of an existing file for the given path and content All the intermediate directories are created.

Raises:

  • in case of I/O error

Parameters:

  • the path to file

  • the content to write



55
56
57
# File 'lib/staticky/files.rb', line 55

def write(path, *content)
  adapter.write(path, *content)
end