Class: Pathutil

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

Overview

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

Defined Under Namespace

Modules: Helpers

Constant Summary collapse

VERSION =
"0.16.2"

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. –



737
738
739
740
741
# File 'lib/pathutil.rb', line 737

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

Instance Attribute Details

#encodingObject

– –

See Also:

  • as this is an alias.


482
483
484
485
486
# File 'lib/pathutil.rb', line 482

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. –



747
748
749
750
751
752
# File 'lib/pathutil.rb', line 747

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



723
724
725
726
727
# File 'lib/pathutil.rb', line 723

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



760
761
762
763
764
765
766
767
# File 'lib/pathutil.rb', line 760

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



775
776
777
778
779
780
781
782
# File 'lib/pathutil.rb', line 775

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



171
172
173
174
175
# File 'lib/pathutil.rb', line 171

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



183
184
185
186
187
# File 'lib/pathutil.rb', line 183

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.


139
140
141
# File 'lib/pathutil.rb', line 139

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



160
161
162
163
164
# File 'lib/pathutil.rb', line 160

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



149
150
151
152
153
# File 'lib/pathutil.rb', line 149

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

#absoluteObject

– Make a path absolute –



38
39
40
41
42
43
# File 'lib/pathutil.rb', line 38

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



194
195
196
197
198
# File 'lib/pathutil.rb', line 194

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 –



610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
# File 'lib/pathutil.rb', line 610

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



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

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



513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/pathutil.rb', line 513

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. –



573
574
575
576
577
578
579
580
581
582
583
584
585
586
# File 'lib/pathutil.rb', line 573

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



355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/pathutil.rb', line 355

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



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

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.


50
51
52
# File 'lib/pathutil.rb', line 50

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

#conservative_cleanpathObject Also known as: cleanpath_conservative



633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
# File 'lib/pathutil.rb', line 633

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



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

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



385
386
387
388
389
390
# File 'lib/pathutil.rb', line 385

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



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

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



440
441
442
443
444
445
# File 'lib/pathutil.rb', line 440

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



373
374
375
376
377
378
# File 'lib/pathutil.rb', line 373

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.


273
274
275
276
# File 'lib/pathutil.rb', line 273

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.


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

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



290
291
292
293
294
295
# File 'lib/pathutil.rb', line 290

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



298
299
300
# File 'lib/pathutil.rb', line 298

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

#normalizeObject

– –

See Also:

  • as this is an alias.


473
474
475
476
477
# File 'lib/pathutil.rb', line 473

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



397
398
399
400
401
402
# File 'lib/pathutil.rb', line 397

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



493
494
495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/pathutil.rb', line 493

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.


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

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.


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

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. –



533
534
535
536
537
538
539
540
541
542
543
544
545
546
# File 'lib/pathutil.rb', line 533

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. –



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

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



429
430
431
432
433
434
# File 'lib/pathutil.rb', line 429

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



282
283
284
# File 'lib/pathutil.rb', line 282

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)


453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# File 'lib/pathutil.rb', line 453

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



61
62
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
# File 'lib/pathutil.rb', line 61

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



409
410
411
412
413
# File 'lib/pathutil.rb', line 409

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”]



128
129
130
131
132
# File 'lib/pathutil.rb', line 128

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

#strip_windows_drive(path = @path) ⇒ Object

– Strips the windows drive from the path. –



598
599
600
601
602
# File 'lib/pathutil.rb', line 598

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



420
421
422
# File 'lib/pathutil.rb', line 420

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

#to_regexp(guard: true) ⇒ Object



589
590
591
592
593
# File 'lib/pathutil.rb', line 589

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. –



553
554
555
556
557
558
559
560
561
562
563
564
565
566
# File 'lib/pathutil.rb', line 553

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