Class: Pathutil

Inherits:
Object
  • Object
show all
Extended by:
Forwardable::Extended, Helpers
Defined in:
lib/pathutil/version.rb,
lib/pathutil.rb,
lib/pathutil/helpers.rb

Overview

Copyright: 2015-2016 Jordon Bedwell - MIT License Encoding: utf-8

Defined Under Namespace

Modules: Helpers

Constant Summary collapse

VERSION =
"0.14.0"

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers

allowed, load_yaml, make_tmpname

Constructor Details

#initialize(path) ⇒ Object

Note:

A lot of this class can be compatible with Pathname.

– Initialize a new instance. –



19
20
21
22
23
# File 'lib/pathutil.rb', line 19

def initialize(path)
  return @path = path if path.is_a?(String)
  return @path = path.to_path if path.respond_to?(:to_path)
  return @path = path.to_s
end

Class Attribute Details

.encodingObject

Note:

you are encouraged to override this if you need to.

– Aliases the default system encoding to us so that we can do most read and write operations with that encoding, instead of being crazy. –



743
744
745
746
747
# File 'lib/pathutil.rb', line 743

def encoding
  return @encoding ||= begin
    Encoding.default_external
  end
end

Instance Attribute Details

#encodingObject

– –

See Also:

  • as this is an alias.


485
486
487
488
489
# File 'lib/pathutil.rb', line 485

def encoding
  return @encoding ||= begin
    self.class.encoding
  end
end

Class Method Details

.normalizeObject

– Normalize CRLF -> LF on Windows reads, to ease your troubles. Normalize LF -> CLRF on Windows write, to ease your troubles. –



753
754
755
756
757
758
# File 'lib/pathutil.rb', line 753

def normalize
  return @normalize ||= {
    :read  => Gem.win_platform?,
    :write => Gem.win_platform?
  }
end

.pwdObject Also known as: gcwd, cwd

Note:

We do nothing special here.

– Get the current directory that Ruby knows about. –

Returns:

  • Pathutil



729
730
731
732
733
# File 'lib/pathutil.rb', line 729

def pwd
  new(
    Dir.pwd
  )
end

.tmpdir(*args) ⇒ Object

Note:

if you adruptly exit it will not remove the dir.

Note:

this directory is removed on exit.

– Make a temporary directory. –

Returns:

  • Pathutil



766
767
768
769
770
771
772
773
# File 'lib/pathutil.rb', line 766

def tmpdir(*args)
  rtn = new(make_tmpname(*args)).tap(&:mkdir)
  ObjectSpace.define_finalizer(rtn, proc do
    rtn.rm_rf
  end)

  rtn
end

.tmpfile(*args) ⇒ Object

Note:

if you adruptly exit it will not remove the dir.

Note:

this file is removed on exit.

– Make a temporary file. –

Returns:

  • Pathutil



781
782
783
784
785
786
787
788
# File 'lib/pathutil.rb', line 781

def tmpfile(*args)
  rtn = new(make_tmpname(*args)).tap(&:touch)
  ObjectSpace.define_finalizer(rtn, proc do
    rtn.rm_rf
  end)

  rtn
end

Instance Method Details

#<(other) ⇒ Object

– Strictly check to see if a path is behind other path but within it. –

Examples:

Pathutil.new(“/”) < Pathutil.new(“/hello”) # => true

Returns:

  • true|false



173
174
175
176
177
# File 'lib/pathutil.rb', line 173

def <(other)
  mine, other = expanded_paths(other)
  return false if other == mine
  other.in_path?(mine)
end

#<=(other) ⇒ Object

– Check to see if a path is behind the other path but within it. –

Examples:

Pathutil.new(“/hello”) < Pathutil.new(“/hello”) # => true

Pathutil.new(“/”) < Pathutil.new(“/hello”) # => true

Returns:

  • true|false



185
186
187
188
189
# File 'lib/pathutil.rb', line 185

def <=(other)
  mine, other = expanded_paths(other)
  return true if other == mine
  other.in_path?(mine)
end

#===(other) ⇒ Object

– A stricter version of ‘==` that also makes sure the object matches. –

Returns:

  • true|false

See Also:

  • for more details.


141
142
143
# File 'lib/pathutil.rb', line 141

def ===(other)
  other.is_a?(self.class) && @path == other
end

#>(other) ⇒ Object

– Strictly checks to see if a path is deeper but within the path of the other. –

Examples:

Pathutil.new(“/hello/world”) > Pathutil.new(“/hello”) # => true

Returns:

  • true|false



162
163
164
165
166
# File 'lib/pathutil.rb', line 162

def >(other)
  mine, other = expanded_paths(other)
  return false if other == mine
  mine.in_path?(other)
end

#>=(other) ⇒ Object

– Checks to see if a path falls within a path and deeper or is the other. –

Examples:

Pathutil.new(“/hello”) >= Pathutil.new(“/”) # => true

Pathutil.new(“/hello”) >= Pathutil.new(“/hello”) # => true

Returns:

  • true|false



151
152
153
154
155
# File 'lib/pathutil.rb', line 151

def >=(other)
  mine, other = expanded_paths(other)
  return true if other == mine
  mine.in_path?(other)
end

#absoluteObject

– Make a path absolute –



40
41
42
43
44
45
# File 'lib/pathutil.rb', line 40

def absolute
  return self if absolute?
  self.class.new("/").join(
    @path
  )
end

#absolute?Boolean

Note:

“./” is considered relative.

– Check to see if the path is absolute, as in: starts with “/” –

Returns:

  • (Boolean)

    true|false



196
197
198
199
200
# File 'lib/pathutil.rb', line 196

def absolute?
  return !!(
    @path =~ %r!\A(?:[A-Za-z]:)?(?:\\+|/+)!
  )
end

#aggressive_cleanpathObject Also known as: cleanpath_aggressive

– rubocop:disable Metrics/AbcSize rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity –



615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/pathutil.rb', line 615

def aggressive_cleanpath
  return self.class.new("/") if root?

  _out = split_path.each_with_object([]) do |part, out|
    next if part == "." || (part == ".." && out.last == "")
    if part == ".." && out.last && out.last != ".."
      out.pop

    else
      out.push(
        part
      )
    end
  end

  # --

  return self.class.new("/") if _out == [""].freeze
  return self.class.new(".") if _out.empty? && (end_with?(".") || relative?)
  self.class.new(_out.join("/"))
end

#ascend { ... } ⇒ Object

– Break apart the path and yield each with the previous parts. –

Examples:

Pathutil.new(“/hello/world”).ascend.to_a # => [“/”, “/hello”, “/hello/world”]

Pathutil.new(“/hello/world”).ascend { |path| $stdout.puts path }

Yields:

  • Pathutil

Returns:

  • Enum



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/pathutil.rb', line 209

def ascend
  unless block_given?
    return to_enum(
      __method__
    )
  end

  yield(
    path = self
  )

  while (new_path = path.dirname)
    if path == new_path || new_path == "."
      break
    else
      path = new_path
      yield  new_path
    end
  end

  nil
end

#binread(*args, **kwd) ⇒ Object

Note:

You can set the default encodings via the class.

– Binread took two steroid shots: it can normalize your string, and encode. –

Returns:

  • String



516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/pathutil.rb', line 516

def binread(*args, **kwd)
  kwd[:encoding] ||= encoding

  if normalize[:read]
    File.binread(self, *args, kwd).encode({
      :universal_newline => true
    })

  else
    File.read(
      self, *args, kwd
    )
  end
end

#binwrite(data, *args, **kwd) ⇒ Object

Note:

You can set the default encodings via the class.

– Binwrite took two steroid shots: it can normalize your string, and encode. –



576
577
578
579
580
581
582
583
584
585
586
587
588
589
# File 'lib/pathutil.rb', line 576

def binwrite(data, *args, **kwd)
  kwd[:encoding] ||= encoding

  if normalize[:write]
    File.binwrite(self, data.encode(
      :crlf_newline => true
    ), *args, kwd)

  else
    File.binwrite(
      self, data, *args, kwd
    )
  end
end

#chdir { ... } ⇒ Object

Note:

you do not need to ship a block at all.

– Move to the current directory temporarily (or for good) and do work son. –

Yields:

  • &block

Returns:

  • nil



358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/pathutil.rb', line 358

def chdir
  if !block_given?
    Dir.chdir(
      @path
    )

  else
    Dir.chdir @path do
      yield
    end
  end
end

#children { ... } ⇒ Object Also known as: entries

– Grab all of the children from the current directory, including hidden. –

Yields:

  • Pathutil



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/pathutil.rb', line 310

def children
  ary = []

  Dir.foreach(@path) do |path|
    if path == "." || path == ".."
      next
    else
      path = self.class.new(File.join(@path, path))
      yield path if block_given?
      ary.push(
        path
      )
    end
  end

  ary
end

#cleanpath(symlink = false) ⇒ Object

Note:

This is a wholesale rip and cleanup of Pathname#cleanpath

– –

Returns:

  • Pathutil

See Also:

  • Pathname#cleanpath.


52
53
54
# File 'lib/pathutil.rb', line 52

def cleanpath(symlink = false)
  symlink ? conservative_cleanpath : aggressive_cleanpath
end

#conservative_cleanpathObject Also known as: cleanpath_conservative



639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
# File 'lib/pathutil.rb', line 639

def conservative_cleanpath
  _out = split_path.each_with_object([]) do |part, out|
    next if part == "." || (part == ".." && out.last == "")
    out.push(
      part
    )
  end

  # --

  if !_out.empty? && basename == "." && _out.last != "" && _out.last != ".."
    _out << "."
  end

  # --

  return self.class.new("/") if _out == [""].freeze
  return self.class.new(".") if _out.empty? && (end_with?(".") || relative?)
  return self.class.new(_out.join("/")).join("") if @path =~ %r!/\z! \
    && _out.last != "." && _out.last != ".."
  self.class.new(_out.join("/"))
end

#descend { ... } ⇒ Object

– Break apart the path in reverse order and descend into the path. –

Examples:

Pathutil.new(“/hello/world”).descend.to_a # => [“/hello/world”, “/hello”, “/”]

Pathutil.new(“/hello/world”).descend { |path| $stdout.puts path }

Yields:

  • Pathutil

Returns:

  • Enum



239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/pathutil.rb', line 239

def descend
  unless block_given?
    return to_enum(
      __method__
    )
  end

  ascend.to_a.reverse_each do |val|
    yield val
  end

  nil
end

#each_filename { ... } ⇒ Object

– Splits the path returning each part (filename) back to you. –

Yields:

  • Pathutil

Returns:

  • Enum



388
389
390
391
392
393
# File 'lib/pathutil.rb', line 388

def each_filename
  return to_enum(__method__) unless block_given?
  @path.split(File::SEPARATOR).delete_if(&:empty?).each do |file|
    yield file
  end
end

#each_line { ... } ⇒ Object

– Wraps ‘readlines` and allows you to yield on the result. –

Examples:

Pathutil.new(“/hello/world”).each_line { |line| $stdout.puts line }

Yields:

  • Pathutil

Returns:

  • Enum



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

def each_line
  return to_enum(__method__) unless block_given?
  readlines.each do |line|
    yield line
  end

  nil
end

#enforce_root(root) ⇒ Object Also known as: prepend

– Expands the path and left joins the root to the path. –

Returns:

  • Pathutil



443
444
445
446
447
448
# File 'lib/pathutil.rb', line 443

def enforce_root(root)
  return self if !relative? && in_path?(root)
  self.class.new(root).join(
    self
  )
end

#find { ... } ⇒ Object

– Find all files without care and yield the given block. –

Yields:

  • Pathutil

Returns:

  • Enum



376
377
378
379
380
381
# File 'lib/pathutil.rb', line 376

def find
  return to_enum(__method__) unless block_given?
  Find.find @path do |val|
    yield self.class.new(val)
  end
end

#fnmatch?(matcher) ⇒ Boolean Also known as: fnmatch

– Unlike traditional ‘fnmatch`, with this one `Regexp` is allowed. –

Examples:

Pathutil.new(“/hello”).fnmatch?(“/hello”) # => true

Pathutil.new(“/hello”).fnmatch?(/h/) # => true

Returns:

  • (Boolean)

    true|false

See Also:

  • for more information.


275
276
277
278
# File 'lib/pathutil.rb', line 275

def fnmatch?(matcher)
  matcher.is_a?(Regexp) ? !!(self =~ matcher) : \
    File.fnmatch(matcher, self)
end

#glob(pattern, flags = 0) { ... } ⇒ Object

– Allows you to glob however you wish to glob in the current ‘Pathutil` –

Yields:

  • Pathutil

Returns:

  • Enum

See Also:

  • for a list of flags.


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'lib/pathutil.rb', line 334

def glob(pattern, flags = 0)
  unless block_given?
    return to_enum(
      __method__, pattern, flags
    )
  end

  chdir do
    Dir.glob(pattern, flags).each do |file|
      yield self.class.new(
        File.join(@path, file)
      )
    end
  end

  nil
end

#in_path?(path) ⇒ Boolean

– Allows you to check if the current path is in the path you want. –

Returns:

  • (Boolean)

    true|false



292
293
294
295
296
297
# File 'lib/pathutil.rb', line 292

def in_path?(path)
  path = self.class.new(path).expand_path.split_path
  mine = (symlink?? expand_path.realpath : expand_path).split_path
  path.each_with_index { |part, index| return false if mine[index] != part }
  true
end

#inspectObject



301
302
303
# File 'lib/pathutil.rb', line 301

def inspect
  "#<#{self.class}:#{@path}>"
end

#normalizeObject

– –

See Also:

  • as this is an alias.


476
477
478
479
480
# File 'lib/pathutil.rb', line 476

def normalize
  return @normalize ||= begin
    self.class.normalize
  end
end

#parentObject

Note:

This will simply return self if “/”.

– Get the parent of the current path. –

Returns:

  • Pathutil



400
401
402
403
404
405
# File 'lib/pathutil.rb', line 400

def parent
  return self if @path == "/"
  self.class.new(absolute?? File.dirname(@path) : File.join(
    @path, ".."
  ))
end

#read(*args, **kwd) ⇒ Object

Note:

You can set the default encodings via the class.

– Read took two steroid shots: it can normalize your string, and encode. –

Returns:

  • String



496
497
498
499
500
501
502
503
504
505
506
507
508
509
# File 'lib/pathutil.rb', line 496

def read(*args, **kwd)
  kwd[:encoding] ||= encoding

  if normalize[:read]
    File.read(self, *args, kwd).encode({
      :universal_newline => true
    })

  else
    File.read(
      self, *args, kwd
    )
  end
end

#read_json(throw_missing: false) ⇒ Object

– Read the file as a JSON file turning it into an object. –

Returns:

  • Hash

See Also:

  • as this is a direct alias of that method.


113
114
115
116
117
118
119
120
121
122
# File 'lib/pathutil.rb', line 113

def read_json(throw_missing: false)
  JSON.parse(
    read
  )

rescue Errno::ENOENT
  throw_missing ? raise : (
    return {}
  )
end

#read_yaml(throw_missing: false, **kwd) ⇒ Object

– Read the file as a YAML file turning it into an object. –

Returns:

  • Hash

See Also:

  • as this a direct alias of that method.


97
98
99
100
101
102
103
104
105
106
# File 'lib/pathutil.rb', line 97

def read_yaml(throw_missing: false, **kwd)
  self.class.load_yaml(
    read, **kwd
  )

rescue Errno::ENOENT
  throw_missing ? raise : (
    return {}
  )
end

#readlines(*args, **kwd) ⇒ Object

Note:

You can set the default encodings via the class.

– Readlines took two steroid shots: it can normalize your string, and encode. –



536
537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'lib/pathutil.rb', line 536

def readlines(*args, **kwd)
  kwd[:encoding] ||= encoding

  if normalize[:read]
    File.readlines(self, *args, kwd).encode({
      :universal_newline => true
    })

  else
    File.readlines(
      self, *args, kwd
    )
  end
end

#relativeObject

– Make a path relative. –



29
30
31
32
33
34
# File 'lib/pathutil.rb', line 29

def relative
  return self if relative?
  self.class.new(strip_windows_drive.gsub(
    %r!\A(\\+|/+)!, ""
  ))
end

#relative_path_from(from) ⇒ Object

– A less complex version of ‘relative_path_from` that simply uses a `Regexp` and returns the full path if it cannot be determined. –

Returns:

  • Pathutil



432
433
434
435
436
437
# File 'lib/pathutil.rb', line 432

def relative_path_from(from)
  from = self.class.new(from).expand_path.gsub(%r!/$!, "")
  self.class.new(expand_path.gsub(%r!^#{
    from.regexp_escape
  }/!, ""))
end

#root?Boolean

– Allows you to quickly determine if the file is the root folder. –

Returns:

  • (Boolean)

    true|false



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

def root?
  !!(self =~ %r!\A(?:[A-Za-z]:)?(?:\\+|/+)\z!)
end

#safe_copy(to, root: nil, ignore: []) ⇒ Object

Note:

Ignore is ignored on safe_copy file because it’s explicit.

– Copy a directory, allowing symlinks if the link falls inside of the root. This is indented for people who wish some safety to their copies. –

Returns:

  • nil

Raises:

  • (ArgumentError)


456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# File 'lib/pathutil.rb', line 456

def safe_copy(to, root: nil, ignore: [])
  raise ArgumentError, "must give a root" unless root
  root = self.class.new(root)
  to   = self.class.new(to)

  if directory?
    safe_copy_directory(to, {
      :root => root, :ignore => ignore
    })

  else
    safe_copy_file(to, {
      :root => root
    })
  end
end

#search_backwards(file, backwards: Float::INFINITY) { ... } ⇒ Object

Note:

It will return all results that it finds across all ascending paths.

– Search backwards for a file (like Rakefile, _config.yml, opts.yml). –

Examples:

Pathutil.new(“~/”).expand_path.search_backwards(“.bashrc”) => [#<Pathutil:/home/user/.bashrc>]

Yields:

  • Pathutil

Returns:

  • Enum



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/pathutil.rb', line 63

def search_backwards(file, backwards: Float::INFINITY)
  ary = []

  ascend.with_index(1).each do |path, index|
    if index > backwards
      break

    else
      Dir.chdir path do
        if block_given?
          file = self.class.new(file)
          if yield(file)
            ary.push(
              file
            )
          end

        elsif File.exist?(file)
          ary.push(self.class.new(
            path.join(file)
          ))
        end
      end
    end
  end

  ary
end

#split { ... } ⇒ Object

– Split the file into its dirname and basename, so you can do stuff. –

Yields:

  • Pathutil

Returns:

  • nil



412
413
414
415
416
# File 'lib/pathutil.rb', line 412

def split
  File.split(@path).collect! do |path|
    self.class.new(path)
  end
end

#split_pathObject

Note:

The blank part is intentionally left there so that you can rejoin.

– Splits the path into all parts so that you can do step by step comparisons –

Examples:

Pathutil.new(“/my/path”).split_path # => [“”, “my”, “path”]



130
131
132
133
134
# File 'lib/pathutil.rb', line 130

def split_path
  @path.split(
    %r!\\+|/+!
  )
end

#strip_windows_drive(path = @path) ⇒ Object

– Strips the windows drive from the path. –



603
604
605
606
607
# File 'lib/pathutil.rb', line 603

def strip_windows_drive(path = @path)
  self.class.new(path.gsub(
    %r!\A[A-Za-z]:(?:\\+|/+)!, ""
  ))
end

#sub_ext(ext) ⇒ Object

Note:

Your extension should start with “.”

– Replace a files extension with your given extension. –

Returns:

  • Pathutil



423
424
425
# File 'lib/pathutil.rb', line 423

def sub_ext(ext)
  self.class.new(@path.chomp(File.extname(@path)) + ext)
end

#to_regexp(guard: true) ⇒ Object



593
594
595
596
597
# File 'lib/pathutil.rb', line 593

def to_regexp(guard: true)
  Regexp.new((guard ? "\\A" : "") + Regexp.escape(
    self
  ))
end

#write(data, *args, **kwd) ⇒ Object

Note:

You can set the default encodings via the class.

– Write took two steroid shots: it can normalize your string, and encode. –



556
557
558
559
560
561
562
563
564
565
566
567
568
569
# File 'lib/pathutil.rb', line 556

def write(data, *args, **kwd)
  kwd[:encoding] ||= encoding

  if normalize[:write]
    File.write(self, data.encode(
      :crlf_newline => true
    ), *args, kwd)

  else
    File.write(
      self, data, *args, kwd
    )
  end
end