lib/vendor/fssm/fssm/pathname.rb in compass-0.11.alpha.3 vs lib/vendor/fssm/fssm/pathname.rb in compass-0.11.alpha.4

- old
+ new

@@ -1,267 +1,211 @@ -# The bundled ruby pathname library is a slow and hideous beast. -# There. I said it. This version is based on pathname3. +require 'fileutils' +require 'find' module FSSM class Pathname < String + SYMLOOP_MAX = 8 - SEPARATOR = Regexp.quote(File::SEPARATOR) + ROOT = '/'.freeze + DOT = '.'.freeze + DOT_DOT = '..'.freeze - if File::ALT_SEPARATOR - ALT_SEPARATOR = Regexp.quote(File::ALT_SEPARATOR) - SEPARATOR_PAT = Regexp.compile("[#{SEPARATOR}#{ALT_SEPARATOR}]") - else - SEPARATOR_PAT = Regexp.compile(SEPARATOR) - end - - if RUBY_PLATFORM =~ /(:?mswin|mingw|bccwin)/ - PREFIX_PAT = Regexp.compile("^([A-Za-z]:#{SEPARATOR_PAT})") - else - PREFIX_PAT = Regexp.compile("^(#{SEPARATOR_PAT})") - end - class << self def for(path) - path = path.is_a?(::FSSM::Pathname) ? path : new(path) - path.dememo - path + path.is_a?(::FSSM::Pathname) ? path : new("#{path}") end end def initialize(path) - if path =~ %r{\0} - raise ArgumentError, "path cannot contain ASCII NULLs" - end - - dememo - + raise ArgumentError, "path cannot contain ASCII NULLs" if path =~ %r{\0} super(path) end - def to_path - self + def <=>(other) + self.tr('/', "\0").to_s <=> other.to_str.tr('/', "\0") + rescue NoMethodError + nil end - def to_s - "#{self}" + def ==(other) + left = self.cleanpath.tr('/', "\0").to_s + right = self.class.for(other).cleanpath.tr('/', "\0").to_s + + left == right + rescue NoMethodError + false end - alias to_str to_s + def +(path) + dup << path + end - def to_a - return @segments if @segments - set_prefix_and_names - @segments = @names.dup - @segments.delete('.') - @segments.unshift(@prefix) unless @prefix.empty? - @segments + def <<(path) + replace( join(path).cleanpath! ) end - alias segments to_a - - def each_filename(&block) - to_a.each(&block) + def absolute? + self[0, 1].to_s == ROOT end def ascend parts = to_a parts.length.downto(1) do |i| yield self.class.join(parts[0, i]) end end - def descend - parts = to_a - 1.upto(parts.length) do |i| - yield self.class.join(parts[0, i]) - end + def children + entries[2..-1] end - def root? - set_prefix_and_names - @names.empty? && !@prefix.empty? - end - - def parent - self + '..' - end - - def relative? - set_prefix_and_names - @prefix.empty? - end - - def absolute? - !relative? - end - - def +(path) - dup << path - end - - def <<(path) - replace(join(path).cleanpath!) - end - def cleanpath! parts = to_a final = [] parts.each do |part| case part - when '.' then + when DOT then next - when '..' then + when DOT_DOT then case final.last - when '..' then - final.push('..') - when nil then - final.push('..') + when ROOT then + next + when DOT_DOT then + final.push(DOT_DOT) + when nil then + final.push(DOT_DOT) else final.pop end else final.push(part) end end - replace(final.empty? ? Dir.pwd : File.join(*final)) + replace(final.empty? ? DOT : self.class.join(*final)) end def cleanpath dup.cleanpath! end - def realpath - raise unless self.exist? + def descend + parts = to_a + 1.upto(parts.length) { |i| yield self.class.join(parts[0, i]) } + end - if File.symlink?(self) - file = self.dup + def dot? + self == DOT + end - while true - file = File.join(File.dirname(file), File.readlink(file)) - break unless File.symlink?(file) - end + def dot_dot? + self == DOT_DOT + end - self.class.new(file).clean - else - self.class.new(Dir.pwd) + self + def each_filename(&blk) + to_a.each(&blk) + end + + def mountpoint? + stat1 = self.lstat + stat2 = self.parent.lstat + + stat1.dev != stat2.dev || stat1.ino == stat2.ino + rescue Errno::ENOENT + false + end + + def parent + self + '..' + end + + def realpath + path = self + + SYMLOOP_MAX.times do + link = path.readlink + link = path.dirname + link if link.relative? + path = link end + + raise Errno::ELOOP, self + rescue Errno::EINVAL + path.expand_path end + def relative? + !absolute? + end + def relative_path_from(base) base = self.class.for(base) - if self.absolute? != base.absolute? - raise ArgumentError, 'no relative path between a relative and absolute' - end + raise ArgumentError, 'no relative path between a relative and absolute' if self.absolute? != base.absolute? - if self.prefix != base.prefix - raise ArgumentError, "different prefix: #{@prefix.inspect} and #{base.prefix.inspect}" - end + return self if base.dot? + return self.class.new(DOT) if self == base - base = base.cleanpath!.segments - dest = dup.cleanpath!.segments + base = base.cleanpath.to_a + dest = self.cleanpath.to_a while !dest.empty? && !base.empty? && dest[0] == base[0] base.shift dest.shift end - base.shift if base[0] == '.' - dest.shift if dest[0] == '.' + base.shift if base[0] == DOT + dest.shift if dest[0] == DOT - if base.include?('..') - raise ArgumentError, "base directory may not contain '..'" - end + raise ArgumentError, "base directory may not contain '#{DOT_DOT}'" if base.include?(DOT_DOT) - path = base.fill('..') + dest + path = base.fill(DOT_DOT) + dest path = self.class.join(*path) - path = self.class.new('.') if path.empty? + path = self.class.new(DOT) if path.empty? path end - def replace(path) - if path =~ %r{\0} - raise ArgumentError, "path cannot contain ASCII NULLs" - end + def root? + !!(self =~ %r{^#{ROOT}+$}) + end - dememo + def to_a + array = to_s.split(File::SEPARATOR) + array.delete('') + array.insert(0, ROOT) if absolute? + array + end - super(path) + alias segments to_a + + def to_path + self end + def to_s + "#{self}" + end + + alias to_str to_s + def unlink Dir.unlink(self) true rescue Errno::ENOTDIR File.unlink(self) true end - - def prefix - set_prefix_and_names - @prefix - end - - def names - set_prefix_and_names - @names - end - - def dememo - @set = nil - @segments = nil - @prefix = nil - @names = nil - end - - private - - def set_prefix_and_names - return if @set - - @names = [] - - if (match = PREFIX_PAT.match(self)) - @prefix = match[0].to_s - @names += match.post_match.split(SEPARATOR_PAT) - else - @prefix = '' - @names += self.split(SEPARATOR_PAT) - end - - @names.compact! - @names.delete('') - - @set = true - end - end class Pathname - class << self - def glob(pattern, flags=0) - dirs = Dir.glob(pattern, flags) - dirs.map! {|path| new(path)} + def self.[](pattern) + Dir[pattern].map! {|d| FSSM::Pathname.new(d) } + end - if block_given? - dirs.each {|dir| yield dir} - nil - else - dirs - end - end - - def [](pattern) - Dir[pattern].map! {|path| new(path)} - end - - def pwd - new(Dir.pwd) - end + def self.pwd + FSSM::Pathname.new(Dir.pwd) end def entries Dir.entries(self).map! {|e| FSSM::Pathname.new(e) } end @@ -276,10 +220,28 @@ def rmdir Dir.rmdir(self) end + def self.glob(pattern, flags = 0) + dirs = Dir.glob(pattern, flags) + dirs.map! {|path| FSSM::Pathname.new(path) } + + if block_given? + dirs.each {|dir| yield dir } + nil + else + dirs + end + end + + def glob(pattern, flags = 0, &block) + patterns = [pattern].flatten + patterns.map! {|p| self.class.glob(self.to_s + p, flags, &block) } + patterns.flatten + end + def chdir blk = lambda { yield self } if block_given? Dir.chdir(self, &blk) end end @@ -370,12 +332,10 @@ end def zero? FileTest.zero?(self) end - - alias exist? exists? end class Pathname def atime File.atime(self) @@ -405,14 +365,14 @@ File.utime(self, atime, mtime) end end class Pathname - class << self - def join(*parts) - new(File.join(*parts.reject {|p| p.empty? })) - end + def self.join(*parts) + last_part = FSSM::Pathname.new(parts.last) + return last_part if last_part.absolute? + FSSM::Pathname.new(File.join(*parts.reject {|p| p.empty? })) end def basename self.class.new(File.basename(self)) end @@ -476,10 +436,14 @@ def size? File.size?(self) end + def split + File.split(self).map {|part| FSSM::Pathname.new(part) } + end + def symlink(to) File.symlink(self, to) end def truncate @@ -523,6 +487,16 @@ def find Find.find(self) {|path| yield FSSM::Pathname.new(path) } end end + class Pathname + class << self + alias getwd pwd + end + + alias absolute expand_path + alias delete unlink + alias exist? exists? + alias fnmatch fnmatch? + end end