lib/pa.rb in pa-1.1.4 vs lib/pa.rb in pa-1.2.0

- old
+ new

@@ -1,11 +1,15 @@ +require "tmpdir" + =begin rdoc Pa(Path) is similary to Pathname, but more powerful. it combines fileutils, tmpdir, find, tempfile, File, Dir, Pathname all class methods support Pa as parameter. +support "~/foo" path. Pa("~/foo") is "/home/x/foo" + Examples: --------- pa = Pa('/home/a.vim') pa.dir #=> '/home' pa.base #=> 'a.vim' @@ -69,82 +73,358 @@ Error = Class.new Exception EUnkonwType = Class.new Error class << self - def method_missing(name, *args, &blk) - # dir -> dir2 - name2 = "#{name}2".to_sym - if public_methods.include?(name2) - ret = __send__(name2, *args, &blk) - return case ret - when Array - ret.map{|v| Pa(v)} - when String - Pa(ret) + DELEGATE_METHODS = [:join, :build] + + # get path of an object. + # + # return obj#path if object has a 'path' instance method + # + # nil -> nil + # + # + # @param [String,#path] obj + # @return [String,nil] path + def get(obj) + if String === obj + obj + elsif obj.respond_to?(:path) + obj.path + elsif obj.nil? + nil + else + raise ArgumentError, "Pa.get() not support type -- #{obj.inspect}(#{obj.class})" + end + end + + # split path + # + # @example + # path="/home/a/file" + # split2(path) #=> "/home/a", "file" + # split2(path, :all => true) #=> "/", "home", "a", "file" + # + # @param [String,Pa] name + # @param [Hash] o option + # @option o [Boolean] :all split all parts + # @return [Array<String>] + def split2(name, o={}) + dir, fname = File.split(get(name)) + ret = Util.wrap_array(File.basename(fname)) + + if o[:all] + loop do + dir1, fname = File.split(dir) + break if dir1 == dir + ret.unshift fname + dir = dir1 end end + ret.unshift dir + ret + end - raise NoMethodError, "no method -- #{name}" + # special case + def split(*args) + dir, *names = split2(*args) + [ Pa(dir), *names] end + + # join paths, skip nil and empty string. + # + # @param [*Array<String>] *paths + # @return [String] + def join2(*paths) + paths.map!{|v|get(v)} + + # skip nil + paths.compact! + # skip empty string + paths.delete("") + + File.join(*paths) + end + + # build a path + # options :path, :dir, :fname, :base, :name, :fext, :ext + # use Pa.join2 + # @example + # + # Pa.build2(dir: "/home", name: "guten", ext: "avi") => "/home/guten.avi + # Pa.build2("/home/guten.avi") { |pa| "#{pa.dir}/foo.#{pa.ext}" } => "/home/foo.avi + # + # @overload build2(path){|pa|=>String} + # @overload build2(data={}) + # @overload build2(data={}){} + def build2(*args, &blk) + data = Hash===args.last ? args.pop : {} + path = args[0] || build_path2(data) + blk ||= proc {|pa| pa.p } + + blk.call(Pa(path)) + end + + DELEGATE_METHODS.each { |mth| + class_eval <<-EOF + def #{mth}(*args, &blk) + Pa(Pa.#{mth}2(*args, &blk)) + end + EOF + } + + private + + # wrap result to Pa + def _wrap(obj) + case obj + when Array + obj.map{|v| Pa(v)} + when String + Pa(obj) + end + end + + def build_path2(data={}) + if data[:path] + path = data[:path] + elsif data[:fname] || data[:base] + path = join2(data[:dir], data[:fname] || data[:base]) + else + path = join2(data[:dir], data[:name]) + if data[:fext] + path << data[:fext] + elsif data[:ext] + path << ".#{data[:ext]}" + end + end + + path + end end - attr_reader :path - alias p path - alias path2 path - alias p2 path + DELEGATE_METHODS2 = [ :join2 ] + DELEGATE_METHODS = [ :dir, :build, :join ] - # @param [String,#path] path + attr_reader :path2 + attr_reader :absolute2, :dir2, :dir_strict2, :base2, :fname2, :name2, :short2, :ext2, :fext2 + + # @param [String, #path] path def initialize(path) - @path = path.respond_to?(:path) ? path.path : path + # convert ~ to ENV["HOME"] + @path2 = Pa.get(path) + @path2.sub!(/^~/, ENV["HOME"]) if @path2 # nil + initialize_variables - end + end chainable = Module.new do def initialize_variables; end end include chainable - # @param [String,#path] - # @return [Pa] the same Pa object - def replace(path) - @path = path.respond_to?(:path) ? path.path : path - initialize_variables - end + def absolute2 + @absolute2 ||= File.absolute_path(path) + end + # => ".", "..", "/", "c:" + def dir2 + @dir2 ||= File.dirname(path) + end + + # Pa("foo") => "" + # Pa("./foo") => "." + def dir_strict2 + return @dir_strict2 if @dir_strict2 + + dir = File.dirname(path) + + @dir_strict2 = if %w[. ..].include?(dir) && path !~ %r!^\.\.?/! + "" + else + dir + end + end + + def base2 + @base2 ||= File.basename(path) + end + + def name2 + @name2 ||= File.basename(path).match(/^(.+?)(?:\.([^.]+))?$/)[1] + end + + # => "ogg", "" + def ext2 + @ext2 ||= File.basename(path).match(/^(.+?)(?:\.([^.]+))?$/)[2] || "" + end + + # => ".ogg", "" + def fext2 + @fext2 ||= ext2.empty? ? "" : ".#{ext2}" + end + + alias fname2 base2 + + # both x, x2 return String + alias path path2 + alias base base2 + alias fname fname2 + alias name name2 + alias ext ext2 + alias fext fext2 + + # abbretive + alias p2 path2 + alias p2 path + alias a2 absolute2 + alias d2 dir2 + alias d_s2 dir_strict2 + alias b2 base2 + alias n2 name2 + alias fn2 fname2 + alias e2 ext2 + alias fe2 fext2 + alias p path + alias b base + alias fn fname + alias n name + alias e ext + alias fe fext + # return '#<Pa @path="foo", @absolute="/home/foo">' # # @return [String] def inspect ret="#<" + self.class.to_s + " " - ret += "@path=\"#{path}\", @absolute=\"#{absolute}\"" + ret += "@path=\"#{path}\", @absolute2=\"#{absolute2}\"" ret += " >" ret end # return '/home/foo' # # @return [String] path def to_s - @path + path end - # missing method goes to Pa.class-method - def method_missing(name, *args, &blk) - self.class.__send__(name, path, *args, &blk) + # @param [String,#path] + # @return [Pa] the same Pa object + def replace(path) + @path2 = Pa.get(path) + initialize_variables end - def <=> other - other_path = if other.respond_to?(:path) - other.path - elsif String === other - other - else - raise Error, "not support type -- #{other.class}" - end + def ==(other) + case other + when Pa + self.path == other.path + else + false + end + end - path <=> other_path + def <=>(other) + path <=> Pa.get(other) end + + def =~(regexp) + path =~ regexp + end + + # add string to path + # + # @example + # pa = Pa('/home/foo/a.txt') + # pa+'~' #=> new Pa('/home/foo/a.txt~') + # + # @param [String] str + # @return [Pa] + def +(str) + Pa(path+str) + end + + def short2 + @short2 ||= Pa.shorten2(@path) + end + + # @return [String] + def sub2(*args, &blk) + path.sub(*args, &blk) + end + + # @return [String] + def gsub2(*args, &blk) + path.gsub(*args, &blk) + end + + # @return [Pa] + def sub(*args, &blk) + Pa(sub2(*args, &blk)) + end + + # @return [Pa] + def gsub(*args, &blk) + Pa(gsub2(*args, &blk)) + end + + # @return [Pa] + def sub!(*args,&blk) + self.replace path.sub(*args,&blk) + end + + # @return [Pa] + def gsub!(*args,&blk) + self.replace path.gsub(*args,&blk) + end + + # @return [MatchData] + def match(*args,&blk) + path.match(*args,&blk) + end + + # @return [Boolean] + def start_with?(*args) + path.start_with?(*args) + end + + # @return [Boolean] + def end_with?(*args) + path.end_with?(*args) + end + + # @return [String] + def build2(data={}, &blk) + return Pa.new(blk.call(self)) if blk + + d = if data[:path] + {path: data[:path]} + elsif data[:fname] || data[:base] + {dir: dir_strict2, fname: data[:fname], base: data[:base]} + else + {dir: dir_strict2, name: name2, ext: ext2}.merge(data) + end + + Pa.build2(d) + end + + DELEGATE_METHODS2.each { |mth2| + class_eval <<-EOF + def #{mth2}(*args, &blk) + Pa.#{mth2}(path, *args, &blk) + end + EOF + } + + DELEGATE_METHODS.each {|mth| + class_eval <<-EOF + def #{mth}(*args, &blk) + Pa(#{mth}2(*args, &blk)) + end + EOF + } end require "pa/path" require "pa/cmd" require "pa/directory"