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 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)


1151
1152
1153
1154
# File 'lib/pleasant_path/pathname.rb', line 1151

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 end-of-line (EOL) 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)


1056
1057
1058
1059
# File 'lib/pleasant_path/pathname.rb', line 1056

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)


1014
1015
1016
1017
# File 'lib/pleasant_path/pathname.rb', line 1014

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:



432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/pleasant_path/pathname.rb', line 432

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

#chdirPathname #chdir {|working_dir| ... } ⇒ retval

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, 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:

  • #chdirPathname

    Returns:

  • #chdir {|working_dir| ... } ⇒ retval

    Yield Parameters:

    Yield Returns:

    Returns:

    • (retval)

Raises:

  • (SystemCallError)

    if the Pathname does not point to an existing directory

See Also:



271
272
273
274
275
276
277
278
279
280
# File 'lib/pleasant_path/pathname.rb', line 271

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:



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

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. Creates any necessary parent directories of the destination. Returns destination as a Pathname.

Examples:

File.exist?("path/to/file")         # == true
Dir.exist?("other")                 # == false
Dir.exist?("other/dir")             # == false
File.exist?("other/dir/same_file")  # == false

Pathname.new("path/to/file").copy("other/dir/same_file")
  # == Pathname.new("other/dir/same_file")

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

Parameters:

Returns:

See Also:



668
669
670
671
672
673
# File 'lib/pleasant_path/pathname.rb', line 668

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

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

Copies the file or directory indicated by the Pathname to a 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:



753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
# File 'lib/pleasant_path/pathname.rb', line 753

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)
  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:



827
828
829
# File 'lib/pleasant_path/pathname.rb', line 827

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)


387
388
389
390
# File 'lib/pleasant_path/pathname.rb', line 387

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

#dirsArray<Pathname>

Returns the immediate 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



114
115
116
# File 'lib/pleasant_path/pathname.rb', line 114

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:



137
138
139
# File 'lib/pleasant_path/pathname.rb', line 137

def dirs_r
  self.find_dirs.to_a
end

#edit_lines(eol: $/) {|lines| ... } ⇒ Array<String>

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

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:

See Also:



1133
1134
1135
# File 'lib/pleasant_path/pathname.rb', line 1133

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

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

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

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:

See Also:



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

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

#existenceself?

Returns the Pathname if exist? returns 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)


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

def existence
  self if self.exist?
end

#filesArray<Pathname>

Returns the immediate 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



190
191
192
# File 'lib/pleasant_path/pathname.rb', line 190

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:



213
214
215
# File 'lib/pleasant_path/pathname.rb', line 213

def files_r
  self.find_files.to_a
end

#find_dirsEnumerator<Pathname> #find_dirs {|descendent| ... } ⇒ 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 no block is given, this method returns an Enumerator. Otherwise, the block is called with each descendent Pathname, and this method returns the original Pathname.

Overloads:

See Also:



157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/pleasant_path/pathname.rb', line 157

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_filesEnumerator<Pathname> #find_files {|descendent| ... } ⇒ 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 no block is given, this method returns an Enumerator. Otherwise, the block is called with each descendent Pathname, and this method returns the original Pathname.

Overloads:

See Also:



233
234
235
236
237
238
239
240
241
# File 'lib/pleasant_path/pathname.rb', line 233

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

Parses the contents of the file indicated by the Pathname 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("in.json", point.to_json)

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

Parameters:

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

Returns:



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

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

#load_yamlObject

Parses the contents of the file indicated by the Pathname 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("in.yaml", point.to_yaml)

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

Returns:



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

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:



299
300
301
302
# File 'lib/pleasant_path/pathname.rb', line 299

def make_dir
  self.mkpath
  self
end

#make_dirnameself

Creates the directory indicated by the Pathname 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)



321
322
323
324
# File 'lib/pleasant_path/pathname.rb', line 321

def make_dirname
  self.dirname.make_dir
  self
end

#make_filePathname

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:

Raises:

  • (SystemCallError)

    if the Pathname points to an existent directory



342
343
344
345
# File 'lib/pleasant_path/pathname.rb', line 342

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. Creates any necessary parent directories of the destination. Returns destination as a Pathname.

Examples:

File.exist?("path/to/file")         # == true
Dir.exist?("other")                 # == false
Dir.exist?("other/dir")             # == false
File.exist?("other/dir/same_file")  # == false

Pathname.new("path/to/file").move("other/dir/same_file")
  # == Pathname.new("other/dir/same_file")

File.exist?("path/to/file")         # == false
Dir.exist?("other")                 # == true
Dir.exist?("other/dir")             # == true
File.exist?("other/dir/same_file")  # == true

Parameters:

Returns:

See Also:



475
476
477
478
479
480
# File 'lib/pleasant_path/pathname.rb', line 475

def move(destination)
  destination = destination.to_pathname
  destination.make_dirname
  FileUtils.mv(self, destination)
  destination
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 a 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:



560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
# File 'lib/pleasant_path/pathname.rb', line 560

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)
    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:



641
642
643
# File 'lib/pleasant_path/pathname.rb', line 641

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

#parentnamePathname

Returns the basename of the parent directory (dirname).

Examples:

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

Returns:



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

def parentname
  self.dirname.basename
end

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

Parses the contents of the file indicated by the Pathname as JSON. The returned result will 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("in.json", '{"key": "value"}')

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

Parameters:

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

Returns:

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


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

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 (EOL) characters.

Reads all lines from the file indicated by the Pathname, and returns them with all end-of-line (EOL) 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:



1081
1082
1083
# File 'lib/pleasant_path/pathname.rb', line 1081

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

#read_yamlnil, ...

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

Examples:

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

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

Returns:

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


15
16
17
18
19
20
21
22
23
24
# File 'lib/pleasant_path/yaml/pathname.rb', line 15

def read_yaml
  self.open("r") do |f|
    # HACK fix Ruby 2.6 warning, but still support Ruby < 2.6
    if Gem::Version.new(Psych::VERSION) >= Gem::Version.new("3.1.0.pre1")
      YAML.safe_load(f, filename: self)
    else
      YAML.safe_load(f, [], [], false, self)
    end
  end
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:



891
892
893
# File 'lib/pleasant_path/pathname.rb', line 891

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:



970
971
972
973
974
975
# File 'lib/pleasant_path/pathname.rb', line 970

def rename_extname(new_extname, &block)
  unless new_extname.start_with?(".") || new_extname.empty?
    new_extname = ".#{new_extname}"
  end
  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

#touch_fileself

Deprecated.

Use #make_file.

Creates the file indicated by the Pathname, including any necessary parent directories. If the file already exists, its modification time (mtime) and access time (atime) are updated. Returns the Pathname.

Examples:

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

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

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

Returns:

  • (self)

See Also:



367
368
369
370
371
# File 'lib/pleasant_path/pathname.rb', line 367

def touch_file
  self.make_dirname
  FileUtils.touch(self)
  self
end

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

Writes each object in lines as a string plus end-of-line (EOL) 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)


1035
1036
1037
1038
# File 'lib/pleasant_path/pathname.rb', line 1035

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)


993
994
995
996
# File 'lib/pleasant_path/pathname.rb', line 993

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