# = Pathname # # Ruby's standard Pathname class with extensions. # # == Authors: # # * Daniel Burger # * Thomas Sawyer # # == Copying # # Copyright (c) 2006 Thomas Sawyer, Daniel Burger # # Ruby License # # This module is free software. You may use, modify, and/or redistribute this # software under the same terms as Ruby. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. require 'pathname' require 'facets/file/rootname' class Pathname # Alias #to_s to #to_str when #to_str is not defined. # alias_method(:to_str, :to_s) unless method_defined?(:to_str) # Alternate to Pathname#new. # # Pathname['/usr/share'] # def self.[](path) new(path) end # Start a path. Another alias for #new. # # Pathname / 'usr' # def self./(path) new(path) end # Root constant for building paths from root directory onward. def self.root Pathname.new('/') end # Home constant for building paths from root directory onward. # # TODO: Pathname#home needs to be more robust. # def self.home Pathname.new('~') end # Work constant for building paths from root directory onward. # def self.work Pathname.new('.') end # Platform dependent null device. # # CREDIT Daniel Burger def self.null case RUBY_PLATFORM when /mswin/i 'NUL' when /amiga/i 'NIL:' when /openvms/i 'NL:' else '/dev/null' end end # Try to get this into standard Pathname class. alias_method :/, :+ # def rootname self.class.new(File.rootname(to_s)) end # def split_root head, tail = *::File.split_root(to_s) [self.class.new(head), self.class.new(tail)] end # Glob pathnames. def glob(match, *opts) flags = glob_flags(opts) Dir.glob(::File.join(self.to_s, match), flags).collect{ |m| self.class.new(m) } end # Return the first glob match. # # DEPRECATE: While slightly faster then glob().first, not really worth it # unless this can be rewritten to shortcut on first match (using fnmatch?). # In wich case, is there a better name for this method? def glob_first(match, *opts) flags = glob_flags(opts) file = ::Dir.glob(::File.join(self.to_s, match), flags).first file ? self.class.new(file) : nil end # Return globbed matches with pathnames relative to the current pathname. def glob_relative(match, *opts) flags = glob_flags(opts) files = Dir.glob(::File.join(self.to_s, match), flags) files = files.map{ |f| f.sub(self.to_s.chomp('/') + '/', '') } files.collect{ |m| self.class.new(m) } end private def glob_flags(opts) flags = 0 opts.each do |opt| case opt when Symbol, String flags += ::File.const_get("FNM_#{opt}".upcase) else flags += opt end end flags end public # def empty? Dir.glob(::File.join(to_s, '*')).empty? end # def uptodate?(*sources) ::FileUtils.uptodate?(to_s, sources.flatten) end # def outofdate?(*sources) ::FileUtils.outofdate?(to_s, sources.flatten) end # Recursively visit a directory located by its path, yielding each resource # as its full matching pathname object. If called on a file, yield the file. # # call-seq: # visit => yield each file # visit(all: true) => yield visited directories as well # visit(hidden: true) => yield hidden files and directories as well # # Example use case: # # # Locate any file but *.haml within app/**/* # Pathname.new("app").visit do |f| # next unless f.to_s =~ /\.haml$/ # f # end # # TODO: Use #map instead of #each ? # # CREDIT: Jean-Denis Vauguet def visit(options = {:all => false, :hidden => false}) if self.directory? children.each do |entry| next if entry.basename.to_s[0] == "." && !options[:hidden] yield(entry) unless entry.directory? && !options[:all] ##entry.visit(:all => options[:all]) { |sub_entry| yield sub_entry } if entry.directory? entry.visit(:all => options[:all], :hidden => options[:hidden]) do |sub_entry| yield(sub_entry) end if entry.directory? end else yield self end end ## # Already included in 1.8.4+ version of Ruby (except for inclusion flag) ## if not instance_methods.include?(:ascend) ## ## # Calls the _block_ for every successive parent directory of the ## # directory path until the root (absolute path) or +.+ (relative path) ## # is reached. ## def ascend(inclusive=false,&block) # :yield: ## cur_dir = self ## yield( cur_dir.cleanpath ) if inclusive ## until cur_dir.root? or cur_dir == Pathname.new(".") ## cur_dir = cur_dir.parent ## yield cur_dir ## end ## end ## ## end ## ## # Already included in 1.8.4+ version of Ruby ## if ! instance_methods.include?(:descend) ## ## # Calls the _block_ for every successive subdirectory of the ## # directory path from the root (absolute path) until +.+ ## # (relative path) is reached. ## def descend() ## @path.scan(%r{[^/]*/?})[0...-1].inject('') do |path, dir| ## yield Pathname.new(path << dir) ## path ## end ## end ## ## end # Does a directory contain a matching entry? # Or if the pathname is a file, same as #fnmatch. def include?(pattern,*opts) if directory? glob_first(pattern,*opts) else fnmatch(pattern,*opts) end end end class NilClass # Provide platform dependent null path. # # CREDIT Daniel Burger def to_path Pathname.null end end