Class: Pathname

Inherits:
Object show all
Defined in:
lib/pleasant_path/pathname.rb,
lib/pleasant_path/json/pathname.rb,
lib/pleasant_path/yaml/pathname.rb

Constant Summary collapse

NULL =

File::NULL as a Pathname. On POSIX systems, this should be equivalent to Pathname.new(“/dev/null”).

Pathname.new(File::NULL)

Instance Method Summary collapse

Instance Method Details

#^(sibling) ⇒ Pathname

Joins the Pathname’s dirname with the given sibling.

The mnemonic for this operator is that the result is formed by going up one directory level from the original path, then going back down to sibling.

Examples:

Pathname.new("path/to/file1") ^ "file2"  # == Pathname.new("path/to/file2")

Parameters:

Returns:



29
30
31
# File 'lib/pleasant_path/pathname.rb', line 29

def ^(sibling)
  self.dirname / sibling
end

#append_file(source) ⇒ self

Appends the contents of file indicated by source to the file indicated by the Pathname. Returns the Pathname.

Examples:

File.read("yearly.log")  # == "one\ntwo\n"
File.read("daily.log")   # == "three\nfour\n"

Pathname.new("yearly.log").append_file("daily.log")
  # == Pathname.new("yearly.log")

File.read("yearly.log")  # == "one\ntwo\nthree\nfour\n"

Parameters:

Returns:

  • (self)


1105
1106
1107
1108
# File 'lib/pleasant_path/pathname.rb', line 1105

def append_file(source)
  self.open("a"){|destination| IO::copy_stream(source, destination) }
  self
end

#append_lines(lines, eol: $/) ⇒ self

Appends each object in lines as a string plus eol (end-of-line) characters to the file indicated by the Pathname. Creates the file if it does not exist, including any necessary parent directories. Returns the Pathname.

Examples:

File.exist?("path/to/file")  # false

Pathname.new("path/to/file").append_lines([:one, :two]).append_lines([:three, :four])
  # == Pathname.new("path/to/file")

File.read("path/to/file")    # == "one\ntwo\nthree\nfour\n"

Parameters:

Returns:

  • (self)


1008
1009
1010
1011
# File 'lib/pleasant_path/pathname.rb', line 1008

def append_lines(lines, eol: $/)
  self.make_dirname.open("a"){|f| f.write_lines(lines, eol: eol) }
  self
end

#append_text(text) ⇒ self

Appends text to the file indicated by the Pathname. Creates the file if it does not exist, including any necessary parent directories. Returns the Pathname.

Examples:

Dir.exist?("path")           # == false
Dir.exist?("path/to")        # == false
File.exist?("path/to/file")  # == false

Pathname.new("path/to/file").append_text("hello").append_text(" world")
  # == Pathname.new("path/to/file")

File.read("path/to/file")    # == "hello world"

Parameters:

Returns:

  • (self)


966
967
968
969
# File 'lib/pleasant_path/pathname.rb', line 966

def append_text(text)
  self.make_dirname.open("a"){|f| f.write(text) }
  self
end

#available_name(format = "%{name}_%{i}%{ext}", i: 1) ⇒ Pathname

Finds an available name based on the Pathname. If the Pathname does not point to an existing file or directory, returns the Pathname. Otherwise, iteratively generates and tests names until one is found that does not point to an existing file or directory.

Names are generated using a Hash-style format string with three populated values:

  • %{name}: original Pathname basename without extname

  • %{ext}: original Pathname extname, including leading dot

  • %{i}: iteration counter; can be initialized via :i kwarg

Examples:

Incremental

Pathname.new("dir/file.txt").available_name  # == Pathname.new("dir/file.txt")

FileUtils.mkdir("dir")
FileUtils.touch("dir/file.txt")

Pathname.new("dir/file.txt").available_name  # == Pathname.new("dir/file_1.txt")

FileUtils.touch("dir/file_1.txt")
FileUtils.touch("dir/file_2.txt")

Pathname.new("dir/file.txt").available_name  # == Pathname.new("dir/file_3.txt")

Specifying format

FileUtils.touch("file.txt")

Pathname.new("file.txt").available_name("%{name} (%{i})%{ext}")
  # == Pathname.new("file (1).txt")

Specifying initial counter

FileUtils.touch("file.txt")

Pathname.new("file.txt").available_name(i: 0)
  # == Pathname.new("file_0.txt")

Parameters:

  • format (String) (defaults to: "%{name}_%{i}%{ext}")
  • i (Integer) (defaults to: 1)

Returns:



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/pleasant_path/pathname.rb', line 399

def available_name(format = "%{name}_%{i}%{ext}", i: 1)
  return self unless self.exist?

  dirname = File.dirname(self)
  format = "%{dirname}/" + format unless dirname == "."

  values = {
    dirname: dirname,
    name: File.basename(self, ".*"),
    ext: self.extname,
    i: i,
  }

  while (path = format % values) && File.exist?(path)
    values[:i] += 1
  end

  path.to_pathname
end

#chdirself #chdir {|working_dir| ... } ⇒ Object

Changes the current working directory to the Pathname. If no block is given, this method returns the Pathname. Otherwise, the block is called with the Pathname, the original working directory is restored after the block exits, and this method returns the return value of the block.

Examples:

FileUtils.mkdir("dir1")
FileUtils.mkdir("dir2")

Pathname.new("dir1").chdir  # == Pathname.new("dir1")
Pathname.pwd                # == Pathname.new("dir1")

Pathname.new("dir2").chdir{|path| "in #{path}" }  # == "in dir2"
Pathname.pwd                                      # == Pathname.new("dir1")

Overloads:

  • #chdirself

    Returns:

    • (self)
  • #chdir {|working_dir| ... } ⇒ Object

    Yield Parameters:

    Yield Returns:

    Returns:

Raises:

  • (SystemCallError)

    if the Pathname does not point to an existing directory

See Also:



263
264
265
266
267
268
269
270
271
272
# File 'lib/pleasant_path/pathname.rb', line 263

def chdir
  if block_given?
    Dir.chdir(self) do |dir|
      yield dir.to_pathname
    end
  else
    Dir.chdir(self)
    self
  end
end

#common_path(other) ⇒ Pathname

Returns the longest path that the Pathname and other have in common.

Examples:

f1 = Pathname.new("dir1/file1")
f2 = Pathname.new("dir1/subdir1/file2")
f3 = Pathname.new("dir1/subdir1/file3")
f4 = Pathname.new("dir2/file4")

f1.common_path(f2)  # == Pathname.new("dir1/")
f2.common_path(f3)  # == Pathname.new("dir1/subdir1/")
f3.common_path(f4)  # == Pathname.new("")

[f1, f2, f3].reduce(&:common_path)  # == Pathname.new("dir1/")

Parameters:

Returns:

See Also:



77
78
79
# File 'lib/pleasant_path/pathname.rb', line 77

def common_path(other)
  File.common_path([self.to_s, other.to_s]).to_pathname
end

#copy(destination) ⇒ Pathname

Copies the file or directory indicated by the Pathname to destination, in the same manner as FileUtils.cp_r. Returns destination as a Pathname.

Examples:

FileUtils.mkpath("dir/files")
FileUtils.touch("dir/files/file1")
FileUtils.mkpath("other_dir")

Pathname.new("dir/files").copy("other_dir/same_files")
  # == Pathname.new("other_dir/same_files")

File.exist?("dir/files/file1")             # == true
File.exist?("other_dir/same_files/file1")  # == true

Parameters:

Returns:

See Also:



625
626
627
628
# File 'lib/pleasant_path/pathname.rb', line 625

def copy(destination)
  FileUtils.cp_r(self, destination)
  destination.to_pathname
end

#copy_as(destination) ⇒ Pathname #copy_as(destination) {|source, destination| ... } ⇒ Pathname

Copies the file or directory indicated by the Pathname to destination, replacing any existing file or directory.

If a block is given and a file or directory does exist at the destination, the block is called with the source and destination Pathnames, and the return value of the block is used as the new destination. If the block returns the source Pathname or nil, the copy is aborted.

Creates any necessary parent directories of the destination. Returns the destination as a Pathname (or the source Pathname in the case that the copy is aborted).

WARNING: Due to system API limitations, the copy is performed in two steps, non-atomically. First, any file or directory existing at the destination is deleted. Next, the source is copied to the destination. The second step can fail independently of the first, e.g. due to insufficient disk space, leaving the file or directory previously at the destination deleted without replacement.

Examples:

Without a block

FileUtils.touch("file")

Pathname.new("file").copy_as("dir/file")
  # == Pathname.new("dir/file")

File.exist?("file")      # == true
File.exist?("dir/file")  # == true

Error on older source

File.write("file", "A")
sleep 1
File.write("file.new", "B")

Pathname.new("file.new").copy_as("file") do |source, destination|
  if source.mtime < destination.mtime
    raise "cannot replace newer file #{destination} with #{source}"
  end
  destination
end                    # == Pathname.new("file")

File.read("file.new")  # == "B"
File.read("file")      # == "B"

Abort on conflict

File.write("file1", "A")
File.write("file2", "B")

Pathname.new("file1").copy_as("file2") do |source, destination|
  puts "#{source} not copied to #{destination} due to conflict"
  nil
end                 # == Pathname.new("file1")

File.read("file1")  # == "A"
File.read("file2")  # == "B"

New destination on conflict

File.write("file1", "A")
File.write("file2", "B")

Pathname.new("file1").copy_as("file2") do |source, destination|
  destination.available_name
end                   # == Pathname.new("file2_1")

File.read("file1")    # == "A"
File.read("file2")    # == "B"
File.read("file2_1")  # == "A"

Overloads:



708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'lib/pleasant_path/pathname.rb', line 708

def copy_as(destination)
  destination = destination.to_pathname

  if block_given? && destination.exist? && self.exist? && !File.identical?(self, destination)
    destination = yield self, destination
    destination = nil if destination == self
  end

  if destination
    destination.delete! unless File.identical?(self, destination)
    self.copy(destination.make_dirname)
  end

  destination || self
end

#copy_into(directory) ⇒ Pathname #copy_into(directory) {|source, destination| ... } ⇒ Pathname

Copies the file or directory indicated by the Pathname into directory, replacing any existing file or directory of the same basename.

If a block is given and a file or directory does exist at the resultant destination, the block is called with the source and destination Pathnames, and the return value of the block is used as the new destination. If the block returns the source Pathname or nil, the copy is aborted.

Creates any necessary parent directories of the destination. Returns the destination as a Pathname (or the source Pathname in the case that the copy is aborted).

WARNING: Due to system API limitations, the copy is performed in two steps, non-atomically. First, any file or directory existing at the destination is deleted. Next, the source is copied to the destination. The second step can fail independently of the first, e.g. due to insufficient disk space, leaving the file or directory previously at the destination deleted without replacement.

Examples:

Without a block

FileUtils.touch("file")

Pathname.new("file").copy_into("dir")
  # == Pathname.new("dir/file")

File.exist?("file")      # == true
File.exist?("dir/file")  # == true

With a block

FileUtils.mkpath("files")
FileUtils.touch("files/file1")
FileUtils.mkpath("dir/files")
FileUtils.touch("dir/files/file2")

Pathname.new("files").copy_into("dir") do |source, destination|
  source                        # == Pathname.new("files")
  destination                   # == Pathname.new("dir/files")
end                             # == Pathname.new("dir/files")

File.exist?("files/file1")      # == true
File.exist?("dir/files/file1")  # == true
File.exist?("dir/files/file2")  # == false

Overloads:

See Also:



782
783
784
# File 'lib/pleasant_path/pathname.rb', line 782

def copy_into(directory, &block)
  self.copy_as(directory / self.basename, &block)
end

#delete!self

Recursively deletes the directory or file indicated by the Pathname. Similar to Pathname#rmtree, but does not raise an exception if the file does not exist. Returns the Pathname.

Examples:

File.exist?("path/to/file")   # == true

Pathname.new("path").delete!  # == Pathname.new("path")

Dir.exist?("path")            # == false
Dir.exist?("path/to")         # == false
File.exist?("path/to/file")   # == false

Returns:

  • (self)


354
355
356
357
# File 'lib/pleasant_path/pathname.rb', line 354

def delete!
  self.rmtree if self.exist?
  self
end

#dirsArray<Pathname>

Returns the direct child directories of the directory indicated by the Pathname. Returned Pathnames are prefixed by the original Pathname.

Examples:

FileUtils.mkdir("parent")
FileUtils.mkdir("parent/dir1")
FileUtils.mkdir("parent/dir1/dir1")
FileUtils.mkdir("parent/dir2")
FileUtils.touch("parent/file1")

Pathname.new("parent").dirs
  # == [
  #      Pathname.new("parent/dir1"),
  #      Pathname.new("parent/dir2")
  #    ]

Returns:

Raises:

  • (SystemCallError)

    if the Pathname does not point to an existing directory



106
107
108
# File 'lib/pleasant_path/pathname.rb', line 106

def dirs
  self.children.tap{|c| c.select!(&:dir?) }
end

#dirs_rArray<Pathname>

Returns all (recursive) descendent directories of the directory indicated by the Pathname. Returned Pathnames are prefixed by the original Pathname, and are in depth-first order.

Examples:

FileUtils.mkdir("parent")
FileUtils.mkdir("parent/dir1")
FileUtils.mkdir("parent/dir1/dir1")
FileUtils.mkdir("parent/dir2")
FileUtils.touch("parent/dir2/file1")

Pathname.new("parent").dirs_r
  # == [
  #      Pathname.new("parent/dir1"),
  #      Pathname.new("parent/dir1/dir1"),
  #      Pathname.new("parent/dir2")
  #    ]

Returns:



129
130
131
# File 'lib/pleasant_path/pathname.rb', line 129

def dirs_r
  self.find_dirs.to_a
end

#edit_lines(eol: $/) {|lines| ... } ⇒ self

Reads the file indicated by the Pathname, and yields the entire contents as an Array of lines to the given block for editing. Writes the return value of the block back to the file, overwriting previous contents. eol (end-of-line) characters are stripped from each line when reading, and appended to each line when writing. Returns the Pathname.

Examples:

Dedup lines of file

File.read("entries.txt")  # == "AAA\nBBB\nBBB\nCCC\nAAA\n"

Pathname.new("entries.txt").edit_lines(&:uniq)
  # == ["AAA", "BBB", "CCC"]

File.read("entries.txt")  # == "AAA\nBBB\nCCC\n"

Parameters:

  • eol (String) (defaults to: $/)

Yields:

  • (lines)

Yield Parameters:

Yield Returns:

Returns:

  • (self)

See Also:



1086
1087
1088
1089
# File 'lib/pleasant_path/pathname.rb', line 1086

def edit_lines(eol: $/, &block)
  File.edit_lines(self, eol: eol, &block)
  self
end

#edit_text {|text| ... } ⇒ self

Reads the file indicated by the Pathname, and yields the entire contents as a String to the given block for editing. Writes the return value of the block back to the file, overwriting previous contents. Returns the Pathname.

Examples:

Update JSON data file

File.read("data.json")  # == '{"nested":{"key":"value"}}'

Pathname.new("data.json").edit_text do |text|
  data = JSON.parse(text)
  data["nested"]["key"] = "new value"
  data.to_json
end                     # == '{"nested":{"key":"new value"}}'

File.read("data.json")  # == '{"nested":{"key":"new value"}}'

Yields:

  • (text)

Yield Parameters:

Yield Returns:

Returns:

  • (self)

See Also:



1059
1060
1061
1062
# File 'lib/pleasant_path/pathname.rb', line 1059

def edit_text(&block)
  File.edit_text(self, &block)
  self
end

#existenceself?

Returns the Pathname if exist? is true, otherwise returns nil.

Examples:

FileUtils.mkdir("dir1")
FileUtils.touch("dir1/file1")

Pathname.new("dir1/file1").existence  # == Pathname.new("dir1/file1")

Pathname.new("dir1/file2").existence  # == nil

Returns:

  • (self, nil)


54
55
56
# File 'lib/pleasant_path/pathname.rb', line 54

def existence
  self if self.exist?
end

#filesArray<Pathname>

Returns the direct child files of the directory indicated by the Pathname. Returned Pathnames are prefixed by the original Pathname.

Examples:

FileUtils.mkdir("parent")
FileUtils.touch("parent/file1")
FileUtils.mkdir("parent/dir1")
FileUtils.touch("parent/dir1/file1")
FileUtils.touch("parent/file2")

Pathname.new("parent").files
  # == [
  #      Pathname.new("parent/file1"),
  #      Pathname.new("parent/file2")
  #    ]

Returns:

Raises:

  • (SystemCallError)

    if the Pathname does not point to an existing directory



182
183
184
# File 'lib/pleasant_path/pathname.rb', line 182

def files
  self.children.tap{|c| c.select!(&:file?) }
end

#files_rArray<Pathname>

Returns all (recursive) descendent files of the directory indicated by the Pathname. Returned Pathnames are prefixed by the original Pathname, and are in depth-first order.

Examples:

FileUtils.mkdir("parent")
FileUtils.touch("parent/file1")
FileUtils.mkdir("parent/dir1")
FileUtils.touch("parent/dir1/file1")
FileUtils.touch("parent/file2")

Pathname.new("parent").files_r
  # == [
  #      Pathname.new("parent/dir1/file1"),
  #      Pathname.new("parent/file1")
  #      Pathname.new("parent/file2")
  #    ]

Returns:



205
206
207
# File 'lib/pleasant_path/pathname.rb', line 205

def files_r
  self.find_files.to_a
end

#find_dirs {|descendent| ... } ⇒ self #find_dirsEnumerator<Pathname>

Iterates over all (recursive) descendent directories of the directory indicated by the Pathname. Iterated Pathnames are prefixed by the original Pathname, and are in depth-first order.

If a block is given, each descendent Pathname is yielded, and this method returns the original Pathname. Otherwise, an Enumerator is returned.

Overloads:

  • #find_dirs {|descendent| ... } ⇒ self

    Yield Parameters:

    Returns:

    • (self)
  • #find_dirsEnumerator<Pathname>

    Returns:

See Also:



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/pleasant_path/pathname.rb', line 149

def find_dirs
  return to_enum(__method__) unless block_given?

  self.find do |path|
    if path.file?
      Find.prune
    elsif path != self
      yield path
    end
  end

  self
end

#find_files {|descendent| ... } ⇒ self #find_filesEnumerator<Pathname>

Iterates over all (recursive) descendent files of the directory indicated by the Pathname. Iterated Pathnames are prefixed by the original Pathname, and are in depth-first order.

If a block is given, each descendent Pathname is yielded, and this method returns the original Pathname. Otherwise, an Enumerator is returned.

Overloads:

  • #find_files {|descendent| ... } ⇒ self

    Yield Parameters:

    Returns:

    • (self)
  • #find_filesEnumerator<Pathname>

    Returns:

See Also:



225
226
227
228
229
230
231
232
233
# File 'lib/pleasant_path/pathname.rb', line 225

def find_files
  return to_enum(__method__) unless block_given?

  self.find do |path|
    yield path if path.file?
  end

  self
end

#load_json(options = {}) ⇒ Object

Reads the file indicated by the Pathname, and parses the contents as JSON, deserializing arbitrary data types via type information embedded in the JSON. This is UNSAFE for JSON from an untrusted source. To consume untrusted JSON, use #read_json instead.

For information about options, see JSON.parse. By default, this method uses JSON.load_default_options.

For information about serializing custom types to JSON, see the JSON readme.

Examples:

require "json/add/core" # provides Struct#to_json
Point = Struct.new(:x, :y)
point = Point.new(10, 20)
File.write("file.json", point.to_json)

Pathname.new("file.json").load_json  # == Point.new(10, 20)

Parameters:

  • options (Hash{Symbol => Object}) (defaults to: {})

Returns:



52
53
54
# File 'lib/pleasant_path/json/pathname.rb', line 52

def load_json(options = {})
  JSON.load(self, nil, options)
end

#load_yamlObject

Reads the file indicated by the Pathname, and parses the contents as YAML, deserializing arbitrary data types via type information embedded in the YAML. This is UNSAFE for YAML from an untrusted source. To consume untrusted YAML, use #read_yaml instead.

Examples:

Point = Struct.new(:x, :y)
point = Point.new(10, 20)
File.write("file.yaml", point.to_yaml)

Pathname.new("file.yaml").load_yaml  # == Point.new(10, 20)

Returns:



33
34
35
# File 'lib/pleasant_path/yaml/pathname.rb', line 33

def load_yaml
  YAML.load_file(self)
end

#make_dirself

Creates the directory indicated by the Pathname, including any necessary parent directories. Returns the Pathname.

Examples:

Dir.exist?("path")                # == false
Dir.exist?("path/to")             # == false

Pathname.new("path/to").make_dir  # == Pathname.new("path/to")

Dir.exist?("path")                # == true
Dir.exist?("path/to")             # == true

Returns:

  • (self)

Raises:

  • (SystemCallError)

    if the Pathname points to an existing file (non-directory)

See Also:



291
292
293
294
# File 'lib/pleasant_path/pathname.rb', line 291

def make_dir
  self.mkpath
  self
end

#make_dirnameself

Creates the directory indicated by the Pathname’s dirname, including any necessary parent directories. Returns the Pathname.

Examples:

Dir.exist?("path")                         # == false
Dir.exist?("path/to")                      # == false

Pathname.new("path/to/file").make_dirname  # == Pathname.new("path/to/file")

Dir.exist?("path")                         # == true
Dir.exist?("path/to")                      # == true
Dir.exist?("path/to/file")                 # == false

Returns:

  • (self)

Raises:

  • (SystemCallError)

    if any element of the dirname points to an existing file (non-directory)



313
314
315
316
# File 'lib/pleasant_path/pathname.rb', line 313

def make_dirname
  self.dirname.make_dir
  self
end

#make_fileself

Creates the file indicated by the Pathname, including any necessary parent directories. Returns the Pathname.

Examples:

Dir.exist?("path")                      # == false
Dir.exist?("path/to")                   # == false

Pathname.new("path/to/file").make_file  # == Pathname.new("path/to/file")

Dir.exist?("path")                      # == true
Dir.exist?("path/to")                   # == true
File.exist?("path/to/file")             # == true

Returns:

  • (self)

Raises:

  • (SystemCallError)

    if the Pathname points to an existing directory, or if any element of the dirname points to an existing file (non-directory)



335
336
337
338
# File 'lib/pleasant_path/pathname.rb', line 335

def make_file
  self.make_dirname.open("a"){}
  self
end

#move(destination) ⇒ Pathname

Moves the file or directory indicated by the Pathname to destination, in the same manner as FileUtils.mv. Returns destination as a Pathname.

Examples:

FileUtils.mkpath("dir/files")
FileUtils.touch("dir/files/file1")
FileUtils.mkpath("other_dir")

Pathname.new("dir/files").move("other_dir/same_files")
  # == Pathname.new("other_dir/same_files")

Dir.exist?("dir/files")                    # == false
File.exist?("other_dir/same_files/file1")  # == true

Parameters:

Returns:

See Also:



438
439
440
441
# File 'lib/pleasant_path/pathname.rb', line 438

def move(destination)
  FileUtils.mv(self, destination)
  destination.to_pathname
end

#move_as(destination) ⇒ Pathname #move_as(destination) {|source, destination| ... } ⇒ Pathname Also known as: rename_as

Moves the file or directory indicated by the Pathname to destination, replacing any existing file or directory.

If a block is given and a file or directory does exist at the destination, the block is called with the source and destination Pathnames, and the return value of the block is used as the new destination. If the block returns the source Pathname or nil, the move is aborted.

Creates any necessary parent directories of the destination. Returns the destination as a Pathname (or the source Pathname in the case that the move is aborted).

WARNING: Due to system API limitations, the move is performed in two steps, non-atomically. First, any file or directory existing at the destination is deleted. Next, the source is moved to the destination. The second step can fail independently of the first, e.g. due to insufficient disk space, leaving the file or directory previously at the destination deleted without replacement.

Examples:

Without a block

FileUtils.touch("file")

Pathname.new("file").move_as("dir/file")
  # == Pathname.new("dir/file")

File.exist?("file")      # == false
File.exist?("dir/file")  # == true

Error on older source

FileUtils.touch("file")
sleep 1
FileUtils.touch("file.new")

Pathname.new("file.new").move_as("file") do |source, destination|
  if source.mtime < destination.mtime
    raise "cannot replace newer file #{destination} with #{source}"
  end
  destination
end                      # == Pathname.new("file")

File.exist?("file.new")  # == false
File.exist?("file")      # == true

Abort on conflict

FileUtils.touch("file1")
FileUtils.touch("file2")

Pathname.new("file1").move_as("file2") do |source, destination|
  puts "#{source} not moved to #{destination} due to conflict"
  nil
end                   # == Pathname.new("file1")

File.exist?("file1")  # == true
File.exist?("file2")  # == true

New destination on conflict

FileUtils.touch("file1")
FileUtils.touch("file2")

Pathname.new("file1").move_as("file2") do |source, destination|
  destination.available_name
end                     # == Pathname.new("file2_1")

File.exist?("file1")    # == false
File.exist?("file2")    # == true
File.exist?("file2_1")  # == true

Overloads:



521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/pleasant_path/pathname.rb', line 521

def move_as(destination)
  destination = destination.to_pathname

  if block_given? && destination.exist? && self.exist? && !File.identical?(self, destination)
    destination = (yield self, destination) || self
  end

  if destination != self
    if File.identical?(self, destination)
      # FileUtils.mv raises an ArgumentError when both paths refer to
      # the same file.  On case-insensitive file systems, this occurs
      # even when both paths have different casing.  We want to
      # disregard the ArgumentError at all times, and change the
      # filename casing when applicable.
      File.rename(self, destination)
    else
      destination.delete!
      self.move(destination.make_dirname)
    end
  end

  destination
end

#move_into(directory) ⇒ Pathname #move_into(directory) {|source, destination| ... } ⇒ Pathname

Moves the file or directory indicated by the Pathname into directory, replacing any existing file or directory of the same basename.

If a block is given and a file or directory does exist at the resultant destination, the block is called with the source and destination Pathnames, and the return value of the block is used as the new destination. If the block returns the source Pathname or nil, the move is aborted.

Creates any necessary parent directories of the destination. Returns the destination as a Pathname (or the source Pathname in the case that the move is aborted).

WARNING: Due to system API limitations, the move is performed in two steps, non-atomically. First, any file or directory existing at the destination is deleted. Next, the source is moved to the destination. The second step can fail independently of the first, e.g. due to insufficient disk space, leaving the file or directory previously at the destination deleted without replacement.

Examples:

Without a block

FileUtils.touch("file")

Pathname.new("file").move_into("dir")
  # == Pathname.new("dir/file")

File.exist?("file")      # == false
File.exist?("dir/file")  # == true

With a block

FileUtils.mkpath("files")
FileUtils.touch("files/file1")
FileUtils.mkpath("dir/files")
FileUtils.touch("dir/files/file2")

Pathname.new("files").move_into("dir") do |source, destination|
  source                        # == Pathname.new("files")
  destination                   # == Pathname.new("dir/files")
end                             # == Pathname.new("dir/files")

Dir.exist?("files")             # == false
File.exist?("dir/files/file1")  # == true
File.exist?("dir/files/file2")  # == false

Overloads:

See Also:



602
603
604
# File 'lib/pleasant_path/pathname.rb', line 602

def move_into(directory, &block)
  self.move_as(directory / self.basename, &block)
end

#parentnamePathname

Returns the basename of the Pathname’s dirname.

Examples:

Pathname.new("grand/parent/base").parentname  # == Pathname.new("parent")

Returns:



39
40
41
# File 'lib/pleasant_path/pathname.rb', line 39

def parentname
  self.dirname.basename
end

#read_json(options = {}) ⇒ nil, ...

Reads the file indicated by the Pathname, and parses the contents as JSON. The returned result will be composed of only basic data types, e.g. nil, true, false, Numeric, String, Array, and Hash.

For information about options, see JSON.parse. By default, this method uses JSON.load_default_options plus create_additions: false.

Examples:

File.write("file.json", '{"key": "value"}')

Pathname.new("file.json").read_json  # == { "key" => "value" }

Parameters:

  • options (Hash{Symbol => Object}) (defaults to: {})

Returns:

  • (nil, true, false, Numeric, String, Symbol, Array, Hash)


23
24
25
# File 'lib/pleasant_path/json/pathname.rb', line 23

def read_json(options = {})
  JSON.load(self, nil, { create_additions: false, **options })
end

#read_lines(eol: $/) ⇒ Array<String>

Note:

Not to be confused with Pathname#readlines, which retains end-of-line characters.

Reads all lines from the file indicated by the Pathname, and returns them with eol (end-of-line) characters stripped.

Examples:

File.read("path/to/file")                # == "one\ntwo\n"

Pathname.new("path/to/file").read_lines  # == ["one", "two"]

Parameters:

  • eol (String) (defaults to: $/)

Returns:

See Also:



1033
1034
1035
# File 'lib/pleasant_path/pathname.rb', line 1033

def read_lines(eol: $/)
  self.open("r"){|f| f.read_lines(eol: eol) }
end

#read_yamlnil, ...

Reads the file indicated by the Pathname, and parses the contents as YAML. The returned result will be composed of only basic data types, e.g. nil, true, false, Numeric, String, Array, and Hash.

Examples:

File.write("file.yaml", "key: value")

Pathname.new("file.yaml").read_yaml  # == { "key" => "value" }

Returns:

  • (nil, true, false, Numeric, String, Array, Hash)


16
17
18
# File 'lib/pleasant_path/yaml/pathname.rb', line 16

def read_yaml
  self.open("r"){|f| YAML.safe_load(f, filename: self) }
end

#rename_basename(new_basename) ⇒ Pathname #rename_basename(new_basename) {|source, destination| ... } ⇒ Pathname

Renames the file or directory indicated by the Pathname relative to its dirname, replacing any existing file or directory of the same basename.

If a block is given and a file or directory does exist at the resultant destination, the block is called with the source and destination Pathnames, and the return value of the block is used as the new destination. If the block returns the source Pathname or nil, the rename is aborted.

Returns the destination as a Pathname (or the source Pathname in the case that the rename is aborted).

WARNING: Due to system API limitations, the rename is performed in two steps, non-atomically. First, any file or directory existing at the destination is deleted. Next, the source is moved to the destination. The second step can fail independently of the first, e.g. due to insufficient disk space, leaving the file or directory previously at the destination deleted without replacement.

Examples:

Without a block

FileUtils.mkpath("dir")
FileUtils.touch("dir/file")

Pathname.new("dir/file").rename_basename("same_file")
  # == Pathname.new("dir/same_file")

File.exist?("dir/file")       # == false
File.exist?("dir/same_file")  # == true

With a block

FileUtils.mkpath("dir")
FileUtils.touch("dir/file1")
FileUtils.touch("dir/file2")

Pathname.new("dir/file1").rename_basename("file2") do |source, destination|
  source                  # == Pathname.new("dir/file1")
  destination             # == Pathname.new("dir/file2")
end                       # == Pathname.new("dir/file2")

File.exist?("dir/file1")  # == false
File.exist?("dir/file2")  # == true

Overloads:

See Also:



846
847
848
# File 'lib/pleasant_path/pathname.rb', line 846

def rename_basename(new_basename, &block)
  self.move_as(self.dirname / new_basename, &block)
end

#rename_extname(new_extname) ⇒ Pathname #rename_extname(new_extname) {|source, destination| ... } ⇒ Pathname

Changes the file extension (extname) of the file indicated by the Pathname, replacing any existing file or directory of the same resultant basename.

If a block is given and a file or directory does exist at the resultant destination, the block is called with the source and destination Pathnames, and the return value of the block is used as the new destination. If the block returns the source Pathname or nil, the rename is aborted.

Returns the destination as a Pathname (or the source Pathname in the case that the rename is aborted).

WARNING: Due to system API limitations, the rename is performed in two steps, non-atomically. First, any file or directory existing at the destination is deleted. Next, the source is moved to the destination. The second step can fail independently of the first, e.g. due to insufficient disk space, leaving the file or directory previously at the destination deleted without replacement.

Examples:

Replace extension

FileUtils.mkpath("dir")
FileUtils.touch("dir/file.abc")

Pathname.new("dir/file.abc").rename_extname(".xyz")
  # == Pathname.new("dir/file.xyz")

File.exist?("dir/file.abc")  # == false
File.exist?("dir/file.xyz")  # == true

Add extension

FileUtils.mkpath("dir")
FileUtils.touch("dir/file")

Pathname.new("dir/file").rename_extname(".abc")
  # == Pathname.new("dir/file.abc")

File.exist?("dir/file")      # == false
File.exist?("dir/file.abc")  # == true

Remove extension

FileUtils.mkpath("dir")
FileUtils.touch("dir/file.abc")

Pathname.new("dir/file.abc").rename_extname("")
  # == Pathname.new("dir/file")

File.exist?("dir/file.abc")  # == false
File.exist?("dir/file")      # == true

With a block

FileUtils.mkpath("dir")
FileUtils.touch("dir/file.abc")
FileUtils.touch("dir/file.xyz")

Pathname.new("dir/file.abc").rename_extname(".xyz") do |source, destination|
  source                     # == Pathname.new("dir/file.abc")
  destination                # == Pathname.new("dir/file.xyz")
end                          # == Pathname.new("dir/file.xyz")

File.exist?("dir/file.abc")  # == false
File.exist?("dir/file.xyz")  # == true

Overloads:

See Also:



925
926
927
# File 'lib/pleasant_path/pathname.rb', line 925

def rename_extname(new_extname, &block)
  self.move_as(self.sub_ext(new_extname), &block)
end

#to_pathnameself

Returns the Pathname unmodified. Exists for parity with String#to_pathname.

Returns:

  • (self)


14
15
16
# File 'lib/pleasant_path/pathname.rb', line 14

def to_pathname
  self
end

#write_lines(lines, eol: $/) ⇒ self

Writes each object in lines as a string plus eol (end-of-line) characters to the file indicated by the Pathname, overwriting the file if it exists. Creates the file if it does not exist, including any necessary parent directories. Returns the Pathname.

Examples:

File.exist?("path/to/file")  # false

Pathname.new("path/to/file").write_lines([:one, :two])
  # == Pathname.new("path/to/file")

File.read("path/to/file")    # == "one\ntwo\n"

Parameters:

Returns:

  • (self)


987
988
989
990
# File 'lib/pleasant_path/pathname.rb', line 987

def write_lines(lines, eol: $/)
  self.make_dirname.open("w"){|f| f.write_lines(lines, eol: eol) }
  self
end

#write_text(text) ⇒ self

Writes text to the file indicated by the Pathname, overwriting the file if it exists. Creates the file if it does not exist, including any necessary parent directories. Returns the Pathname.

Examples:

Dir.exist?("path")           # == false
Dir.exist?("path/to")        # == false
File.exist?("path/to/file")  # == false

Pathname.new("path/to/file").write_text("hello world")
  # == Pathname.new("path/to/file")

File.read("path/to/file")    # == "hello world"

Parameters:

Returns:

  • (self)


945
946
947
948
# File 'lib/pleasant_path/pathname.rb', line 945

def write_text(text)
  self.make_dirname.open("w"){|f| f.write(text) }
  self
end