class Pathutil
def <(other)
def <(other) mine, other = expanded_paths(other) return false if other == mine other.in_path?(mine) end
def <=(other)
def <=(other) mine, other = expanded_paths(other) return true if other == mine other.in_path?(mine) end
def ===(other)
def ===(other) other.is_a?(self.class) && @path == other end
def >(other)
def >(other) mine, other = expanded_paths(other) return false if other == mine mine.in_path?(other) end
def >=(other)
def >=(other) mine, other = expanded_paths(other) return true if other == mine mine.in_path?(other) end
def absolute?
def absolute? @path.start_with?("/") end
def ascend
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
def binread(*args, **kwd)
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
def binwrite(data, *args, **kwd)
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
def chdir
def chdir if !block_given? Dir.chdir( @path ) else Dir.chdir @path do yield end end end
def children
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
def descend
def descend unless block_given? return to_enum( __method__ ) end ascend.to_a.reverse_each do |val| yield val end nil end
def each_filename
def each_filename return to_enum(__method__) unless block_given? @path.split(File::SEPARATOR).delete_if(&:empty?).each do |file| yield file end end
def each_line
def each_line return to_enum(__method__) unless block_given? readlines.each do |line| yield line end nil end
def encoding
def encoding return @encoding ||= begin self.class.encoding end end
def encoding
def encoding return @encoding ||= begin Encoding.default_external end end
def enforce_root(root)
def enforce_root(root) curr, root = expanded_paths(root) if curr.in_path?(root) return curr else File.join( root, curr ) end end
def expanded_paths(path)
def expanded_paths(path) return expand_path, self.class.new(path).expand_path end
def find
def find return to_enum(__method__) unless block_given? Find.find @path do |val| yield self.class.new(val) end end
def fnmatch?(matcher)
def fnmatch?(matcher) matcher.is_a?(Regexp) ? !!(self =~ matcher) : \ File.fnmatch(self, matcher) end
def glob(pattern, flags = 0)
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
def in_path?(path)
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
def initialize(path)
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
def inspect
def inspect "#<#{self.class}:#{@path}>" end
def normalize
def normalize return @normalize ||= begin self.class.normalize end end
def normalize
def normalize return @normalize ||= { :read => Gem.win_platform?, :write => Gem.win_platform? } end
def parent
def parent return self if @path == "/" self.class.new(absolute?? File.dirname(@path) : File.join( @path, ".." )) end
def pwd
def pwd new( Dir.pwd ) end
def read(*args, **kwd)
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
def read_json(throw_missing: false)
def read_json(throw_missing: false) JSON.parse( read ) rescue Errno::ENOENT throw_missing ? raise : ( return {} ) end
def read_yaml(throw_missing: false, **kwd)
def read_yaml(throw_missing: false, **kwd) self.class.load_yaml( read, **kwd ) rescue Errno::ENOENT throw_missing ? raise : ( return {} ) end
def readlines(*args, **kwd)
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
def relative_path_from(from)
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
def root?
def root? self == File::SEPARATOR end
def safe_copy(to, root: nil)
def safe_copy(to, root: nil) raise ArgumentError, "must give a root" unless root to = self.class.new(to) root = self.class.new(root) return safe_copy_directory(to, :root => root) if directory? safe_copy_file(to, :root => root) end
def safe_copy_directory(to, root: nil)
def safe_copy_directory(to, root: nil) if !in_path?(root) raise Errno::EPERM, "#{self} not in #{ root }" else to.mkdir_p unless to.exist? children do |file| if !file.in_path?(root) raise Errno::EPERM, "#{file} not in #{ root }" elsif file.file? FileUtils.cp(file, to, { :preserve => true }) else path = file.realpath path.safe_copy(to.join(file.basename), { :root => root }) end end end end
def safe_copy_file(to, root: nil)
def safe_copy_file(to, root: nil) raise Errno::EPERM, "#{self} not in #{root}" unless in_path?(root) FileUtils.cp(self, to, { :preserve => true }) end
def search_backwards(file, backwards: Float::INFINITY)
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
def split
def split File.split(@path).collect! do |path| self.class.new(path) end end
def split_path
def split_path @path.split( File::SEPARATOR ) end
def sub_ext(ext)
def sub_ext(ext) self.class.new(@path.chomp(File.extname(@path)) + ext) end
def tmpdir(*args)
def tmpdir(*args) rtn = new(make_tmpname(*args)).tap(&:mkdir) ObjectSpace.define_finalizer(rtn, proc do rtn.rm_rf end) rtn end
def tmpfile(*args)
def tmpfile(*args) rtn = new(make_tmpname(*args)).tap(&:touch) ObjectSpace.define_finalizer(rtn, proc do rtn.rm_rf end) rtn end
def write(data, *args, **kwd)
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