Module: Philiprehberger::PathnameKit

Defined in:
lib/philiprehberger/pathname_kit.rb,
lib/philiprehberger/pathname_kit/version.rb

Defined Under Namespace

Classes: Error

Constant Summary collapse

VERSION =
'0.6.0'

Class Method Summary collapse

Class Method Details

.append(path, content) ⇒ String

Append content to a file, creating parent directories if needed. Creates the file if it does not exist.

Parameters:

  • path (String)

    the file path

  • content (String)

    the content to append

Returns:

  • (String)

    the path written

Raises:

  • (Error)

    if path is nil or empty



259
260
261
262
263
264
265
266
267
# File 'lib/philiprehberger/pathname_kit.rb', line 259

def self.append(path, content)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  path_str = path.to_s
  ensure_directory(File.dirname(path_str))
  File.open(path_str, 'a') { |f| f.write(content.to_s) }
  path_str
end

.atomic_write(path) {|IO| ... } ⇒ void

This method returns an undefined value.

Write content atomically by writing to a temp file then renaming.

Parameters:

  • path (String)

    the file path to write to

Yields:

  • (IO)

    the temporary file to write to

Raises:

  • (Error)

    if path is nil or empty



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/philiprehberger/pathname_kit.rb', line 20

def self.atomic_write(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  dir = File.dirname(path)
  ensure_directory(dir)

  temp = Tempfile.new(['atomic', File.extname(path)], dir)
  begin
    yield temp
    temp.close
    FileUtils.mv(temp.path, path)
  rescue StandardError
    temp.close
    temp.unlink
    raise
  end
end

.basename(path) ⇒ String

Get the filename component of a path (without directory).

Parameters:

  • path (String)

    the file path

Returns:

  • (String)

    the filename

Raises:

  • (Error)

    if path is nil or empty



371
372
373
374
375
376
# File 'lib/philiprehberger/pathname_kit.rb', line 371

def self.basename(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  File.basename(path.to_s)
end

.checksum(path, algorithm: :sha256) ⇒ String

Computes a digest checksum of a file.

Parameters:

  • path (String)

    file path

  • algorithm (Symbol) (defaults to: :sha256)

    :md5, :sha1, :sha256, or :sha512

Returns:

  • (String)

    hex digest string

Raises:

  • (PathnameKit::Error)

    if path is nil/empty, file doesn’t exist, or algorithm is invalid



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/philiprehberger/pathname_kit.rb', line 235

def self.checksum(path, algorithm: :sha256)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  path_str = path.to_s
  raise Error, "file does not exist: #{path_str}" unless File.exist?(path_str)

  digest_class = case algorithm
                 when :md5 then Digest::MD5
                 when :sha1 then Digest::SHA1
                 when :sha256 then Digest::SHA256
                 when :sha512 then Digest::SHA512
                 else raise Error, "unsupported algorithm: #{algorithm}"
                 end
  digest_class.file(path_str).hexdigest
end

.copy(src, dest) ⇒ String

Copies a file to a destination, creating parent directories as needed.

Parameters:

  • src (String)

    source file path

  • dest (String)

    destination file path

Returns:

  • (String)

    destination path

Raises:



127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/philiprehberger/pathname_kit.rb', line 127

def self.copy(src, dest)
  raise Error, 'source path cannot be nil' if src.nil?
  raise Error, 'source path cannot be empty' if src.to_s.empty?
  raise Error, 'destination path cannot be nil' if dest.nil?
  raise Error, 'destination path cannot be empty' if dest.to_s.empty?
  raise Error, "source file does not exist: #{src}" unless File.exist?(src.to_s)

  dest_str = dest.to_s
  FileUtils.mkdir_p(File.dirname(dest_str))
  FileUtils.cp(src.to_s, dest_str)
  dest_str
end

.directory?(path) ⇒ Boolean

Check if the given path is a directory.

Parameters:

  • path (String)

    the path to check

Returns:

  • (Boolean)

    true if the path is a directory

Raises:

  • (Error)

    if path is nil or empty



359
360
361
362
363
364
# File 'lib/philiprehberger/pathname_kit.rb', line 359

def self.directory?(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  File.directory?(path.to_s)
end

.dirname(path) ⇒ String

Get the directory component of a path.

Parameters:

  • path (String)

    the file path

Returns:

  • (String)

    the directory portion

Raises:

  • (Error)

    if path is nil or empty



383
384
385
386
387
388
# File 'lib/philiprehberger/pathname_kit.rb', line 383

def self.dirname(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  File.dirname(path.to_s)
end

.each_line(path) {|String| ... } ⇒ Enumerator

Stream a file line by line without loading it entirely into memory.

Parameters:

  • path (String)

    the file path

Yields:

  • (String)

    each line, including the trailing newline if present

Returns:

  • (Enumerator)

    when no block is given

Raises:

  • (Error)

    if path is nil/empty or the file does not exist



192
193
194
195
196
197
198
199
200
# File 'lib/philiprehberger/pathname_kit.rb', line 192

def self.each_line(path, &block)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?
  raise Error, "file not found: #{path}" unless File.exist?(path.to_s)

  return File.foreach(path.to_s).each unless block

  File.foreach(path.to_s, &block)
end

.empty?(path) ⇒ Boolean

Check if a file is empty (zero bytes).

Parameters:

  • path (String)

    the file path

Returns:

  • (Boolean)

    true if the file is empty

Raises:

  • (Error)

    if path is nil/empty or the file does not exist



291
292
293
294
295
296
297
# File 'lib/philiprehberger/pathname_kit.rb', line 291

def self.empty?(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?
  raise Error, "file not found: #{path}" unless File.exist?(path.to_s)

  File.empty?(path.to_s)
end

.ensure_directory(path) ⇒ void

This method returns an undefined value.

Ensure a directory exists, creating it and all parents if needed.

Parameters:

  • path (String)

    the directory path

Raises:

  • (Error)

    if path is nil or empty



44
45
46
47
48
49
# File 'lib/philiprehberger/pathname_kit.rb', line 44

def self.ensure_directory(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  FileUtils.mkdir_p(path)
end

.exists?(path) ⇒ Boolean

Check if a file or directory exists at the given path.

Parameters:

  • path (String)

    the file or directory path

Returns:

  • (Boolean)

    true if the path exists

Raises:

  • (Error)

    if path is nil or empty



347
348
349
350
351
352
# File 'lib/philiprehberger/pathname_kit.rb', line 347

def self.exists?(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  File.exist?(path.to_s)
end

.expand(path) ⇒ String

Expand a path to its absolute form with tilde expansion.

Parameters:

  • path (String)

    the file path

Returns:

  • (String)

    the expanded absolute path

Raises:

  • (Error)

    if path is nil or empty



316
317
318
319
320
321
# File 'lib/philiprehberger/pathname_kit.rb', line 316

def self.expand(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  File.expand_path(path.to_s)
end

.extension(path) ⇒ String

Get the file extension including the leading dot.

Parameters:

  • path (String)

    the file path

Returns:

  • (String)

    the extension (e.g. “.rb”) or empty string

Raises:

  • (Error)

    if path is nil or empty



304
305
306
307
308
309
# File 'lib/philiprehberger/pathname_kit.rb', line 304

def self.extension(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  File.extname(path.to_s)
end

.find(glob) ⇒ Array<String>

Find files matching a glob pattern.

Parameters:

  • glob (String)

    the glob pattern

Returns:

  • (Array<String>)

    matching file paths

Raises:

  • (Error)

    if glob is nil or empty



71
72
73
74
75
76
# File 'lib/philiprehberger/pathname_kit.rb', line 71

def self.find(glob)
  raise Error, 'glob cannot be nil' if glob.nil?
  raise Error, 'glob cannot be empty' if glob.to_s.empty?

  Dir.glob(glob)
end

.identical?(path1, path2) ⇒ Boolean

Compare two files by SHA-256 digest.

Parameters:

  • path1 (String)

    first file path

  • path2 (String)

    second file path

Returns:

  • (Boolean)

    true if files have identical contents

Raises:

  • (Error)

    if either path is nil/empty or does not exist



275
276
277
278
279
280
281
282
283
284
# File 'lib/philiprehberger/pathname_kit.rb', line 275

def self.identical?(path1, path2)
  raise Error, 'first path cannot be nil' if path1.nil?
  raise Error, 'first path cannot be empty' if path1.to_s.empty?
  raise Error, 'second path cannot be nil' if path2.nil?
  raise Error, 'second path cannot be empty' if path2.to_s.empty?
  raise Error, "file does not exist: #{path1}" unless File.exist?(path1.to_s)
  raise Error, "file does not exist: #{path2}" unless File.exist?(path2.to_s)

  Digest::SHA256.file(path1.to_s).hexdigest == Digest::SHA256.file(path2.to_s).hexdigest
end

.line_count(path) ⇒ Integer

Count the number of lines in a file.

Parameters:

  • path (String)

    the file path

Returns:

  • (Integer)

    the number of lines

Raises:

  • (Error)

    if path is nil or empty

  • (Error)

    if the file does not exist



113
114
115
116
117
118
119
# File 'lib/philiprehberger/pathname_kit.rb', line 113

def self.line_count(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?
  raise Error, "file not found: #{path}" unless File.exist?(path)

  File.readlines(path).size
end

.move(src, dest) ⇒ String

Moves a file to a destination, creating parent directories as needed.

Parameters:

  • src (String)

    source file path

  • dest (String)

    destination file path

Returns:

  • (String)

    destination path

Raises:



146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/philiprehberger/pathname_kit.rb', line 146

def self.move(src, dest)
  raise Error, 'source path cannot be nil' if src.nil?
  raise Error, 'source path cannot be empty' if src.to_s.empty?
  raise Error, 'destination path cannot be nil' if dest.nil?
  raise Error, 'destination path cannot be empty' if dest.to_s.empty?
  raise Error, "source file does not exist: #{src}" unless File.exist?(src.to_s)

  dest_str = dest.to_s
  FileUtils.mkdir_p(File.dirname(dest_str))
  FileUtils.mv(src.to_s, dest_str)
  dest_str
end

.mtime(path) ⇒ Time

Get the last modification time of a file or directory.

Parameters:

  • path (String)

    the file or directory path

Returns:

  • (Time)

    the modification time

Raises:

  • (Error)

    if path is nil/empty or does not exist



395
396
397
398
399
400
401
# File 'lib/philiprehberger/pathname_kit.rb', line 395

def self.mtime(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?
  raise Error, "path not found: #{path}" unless File.exist?(path.to_s)

  File.mtime(path.to_s)
end

.read(path) ⇒ String

Read a file’s contents.

Parameters:

  • path (String)

    the file path

Returns:

  • (String)

Raises:

  • (Error)

    if path is nil/empty or the file does not exist



164
165
166
167
168
169
170
# File 'lib/philiprehberger/pathname_kit.rb', line 164

def self.read(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?
  raise Error, "file not found: #{path}" unless File.exist?(path.to_s)

  File.read(path.to_s)
end

.relative_to(path, base) ⇒ String

Return path expressed relative to base as a String.

Both arguments may be String or Pathname. Inputs are wrapped in Pathname and expanded so relative inputs are resolved against the current working directory before the relative path is computed.

Parameters:

  • path (String, Pathname)

    the target path

  • base (String, Pathname)

    the base path to compute relativity against

Returns:

  • (String)

    the relative path

Raises:

  • (Error)

    if either argument is nil or empty



333
334
335
336
337
338
339
340
# File 'lib/philiprehberger/pathname_kit.rb', line 333

def self.relative_to(path, base)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?
  raise Error, 'base cannot be nil' if base.nil?
  raise Error, 'base cannot be empty' if base.to_s.empty?

  Pathname.new(path.to_s).expand_path.relative_path_from(Pathname.new(base.to_s).expand_path).to_s
end

.safe_delete(path) ⇒ Boolean

Safely delete a file, returning true if deleted and false if not found.

Parameters:

  • path (String)

    the file path to delete

Returns:

  • (Boolean)

    true if the file was deleted

Raises:

  • (Error)

    if path is nil or empty



56
57
58
59
60
61
62
63
64
# File 'lib/philiprehberger/pathname_kit.rb', line 56

def self.safe_delete(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  return false unless File.exist?(path)

  File.delete(path)
  true
end

.size(path) ⇒ Integer

Get the size of a file in bytes.

Parameters:

  • path (String)

    the file path

Returns:

  • (Integer)

Raises:

  • (Error)

    if path is nil/empty or the file does not exist



207
208
209
210
211
212
213
# File 'lib/philiprehberger/pathname_kit.rb', line 207

def self.size(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?
  raise Error, "file not found: #{path}" unless File.exist?(path.to_s)

  File.size(path.to_s)
end

.tempfile(ext = '.tmp') {|String| ... } ⇒ Object

Create a temporary file with a given extension and yield its path.

Parameters:

  • ext (String) (defaults to: '.tmp')

    the file extension (e.g. ‘.txt’)

Yields:

  • (String)

    the temporary file path

Returns:

  • (Object)

    the block return value



83
84
85
86
87
88
89
90
91
# File 'lib/philiprehberger/pathname_kit.rb', line 83

def self.tempfile(ext = '.tmp')
  temp = Tempfile.new(['tmp', ext])
  begin
    yield temp.path
  ensure
    temp.close
    temp.unlink
  end
end

.touch(path) ⇒ void

This method returns an undefined value.

Touch a file, creating it if it does not exist and updating its mtime.

Parameters:

  • path (String)

    the file path

Raises:

  • (Error)

    if path is nil or empty



98
99
100
101
102
103
104
105
# File 'lib/philiprehberger/pathname_kit.rb', line 98

def self.touch(path)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  dir = File.dirname(path)
  ensure_directory(dir)
  FileUtils.touch(path)
end

.with_tempdir {|String| ... } ⇒ Object

Create a temporary directory and yield its path. The directory and all its contents are removed when the block returns.

Yields:

  • (String)

    the temporary directory path

Returns:

  • (Object)

    the block return value



220
221
222
223
224
225
226
227
# File 'lib/philiprehberger/pathname_kit.rb', line 220

def self.with_tempdir
  dir = Dir.mktmpdir
  begin
    yield dir
  ensure
    FileUtils.rm_rf(dir)
  end
end

.write(path, content) ⇒ String

Atomically write content to a file, creating parent directories as needed.

Parameters:

  • path (String)

    the file path

  • content (String)

    the content to write

Returns:

  • (String)

    the path written

Raises:

  • (Error)

    if path is nil/empty



178
179
180
181
182
183
184
# File 'lib/philiprehberger/pathname_kit.rb', line 178

def self.write(path, content)
  raise Error, 'path cannot be nil' if path.nil?
  raise Error, 'path cannot be empty' if path.to_s.empty?

  atomic_write(path.to_s) { |f| f.write(content.to_s) }
  path.to_s
end