ruby-changes:10360
From: akr <ko1@a...>
Date: Fri, 30 Jan 2009 21:51:15 +0900 (JST)
Subject: [ruby-changes:10360] Ruby:r21904 (trunk): * lib/pathname.rb (Pathname#realdirpath): new method.
akr 2009-01-30 21:50:55 +0900 (Fri, 30 Jan 2009) New Revision: 21904 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=21904 Log: * lib/pathname.rb (Pathname#realdirpath): new method. [ruby-dev:36290] Modified files: trunk/ChangeLog trunk/NEWS trunk/lib/pathname.rb trunk/test/pathname/test_pathname.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 21903) +++ ChangeLog (revision 21904) @@ -1,3 +1,8 @@ +Fri Jan 30 21:49:32 2009 Tanaka Akira <akr@f...> + + * lib/pathname.rb (Pathname#realdirpath): new method. + [ruby-dev:36290] + Fri Jan 30 18:04:23 2009 NAKAMURA Usaku <usa@r...> * win32/win32.c (rb_w32_write): limit write size to 32KB if the file Index: lib/pathname.rb =================================================================== --- lib/pathname.rb (revision 21903) +++ lib/pathname.rb (revision 21904) @@ -76,9 +76,9 @@ # # === Core methods # -# These methods are effectively manipulating a String, because that's all a path -# is. Except for #mountpoint?, #children, and #realpath, they don't access the -# filesystem. +# These methods are effectively manipulating a String, because that's +# all a path is. Except for #mountpoint?, #children, #realdirpath +# and #realpath, they don't access the filesystem. # # - + # - #join @@ -90,6 +90,7 @@ # - #each_filename # - #cleanpath # - #realpath +# - #realdirpath # - #children # - #mountpoint? # @@ -411,7 +412,7 @@ end private :cleanpath_conservative - def realpath_rec(prefix, unresolved, h) + def realpath_rec(prefix, unresolved, h, strict, last = true) resolved = [] until unresolved.empty? n = unresolved.shift @@ -428,14 +429,20 @@ prefix, *resolved = h[path] end else - s = File.lstat(path) + begin + s = File.lstat(path) + rescue Errno::ENOENT => e + raise e if strict || !last || !unresolved.empty? + resolved << n + break + end if s.symlink? h[path] = :resolving link_prefix, link_names = split_names(File.readlink(path)) if link_prefix == '' - prefix, *resolved = h[path] = realpath_rec(prefix, resolved + link_names, h) + prefix, *resolved = h[path] = realpath_rec(prefix, resolved + link_names, h, strict, unresolved.empty?) else - prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h) + prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h, strict, unresolved.empty?) end else resolved << n @@ -448,23 +455,41 @@ end private :realpath_rec - # - # Returns a real (absolute) pathname of +self+ in the actual filesystem. - # The real pathname doesn't contain symlinks or useless dots. - # - # No arguments should be given; the old behaviour is *obsoleted*. - # - def realpath + def real_path_internal(strict = false) path = @path prefix, names = split_names(path) if prefix == '' prefix, names2 = split_names(Dir.pwd) names = names2 + names end - prefix, *names = realpath_rec(prefix, names, {}) + prefix, *names = realpath_rec(prefix, names, {}, strict) self.class.new(prepend_prefix(prefix, File.join(*names))) end + private :real_path_internal + # + # Returns the real (absolute) pathname of +self+ in the actual + # filesystem not containing symlinks or useless dots. + # + # All components of the pathname must exist when this method is + # called. + # + # No arguments should be given; the old behaviour is *obsoleted*. + # + def realpath + real_path_internal(true) + end + + # + # Returns the real (absolute) pathname of +self+ in the actual filesystem. + # The real pathname doesn't contain symlinks or useless dots. + # + # The last component of the real pathname can be nonexistent. + # + def realdirpath + real_path_internal(false) + end + # #parent returns the parent directory. # # This is same as <tt>self + '..'</tt>. Index: NEWS =================================================================== --- NEWS (revision 21903) +++ NEWS (revision 21904) @@ -85,6 +85,9 @@ socket option name and shutdown's how argument can be specified as a string/symbol. +* pathname + * realdirpath + === Compatibility issues (excluding feature bug fixes) * Enumerator#rewind Index: test/pathname/test_pathname.rb =================================================================== --- test/pathname/test_pathname.rb (revision 21903) +++ test/pathname/test_pathname.rb (revision 21904) @@ -278,24 +278,37 @@ } end + def has_symlink? + begin + File.symlink(nil, nil) + rescue NotImplementedError + return false + rescue TypeError + end + return true + end + def realpath(path) Pathname.new(path).realpath.to_s end def test_realpath - begin - File.symlink(nil, nil) - rescue NotImplementedError - return - rescue TypeError - end + return if !has_symlink? with_tmpchdir('rubytest-pathname') {|dir| + assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist") } File.symlink("not-exist-target", "#{dir}/not-exist") assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist") } File.symlink("loop", "#{dir}/loop") assert_raise(Errno::ELOOP) { realpath("#{dir}/loop") } + File.symlink("../#{File.basename(dir)}/./not-exist-target", "#{dir}/not-exist2") + assert_raise(Errno::ENOENT) { realpath("#{dir}/not-exist2") } + + File.open("#{dir}/exist-target", "w") {} + File.symlink("../#{File.basename(dir)}/./exist-target", "#{dir}/exist2") + assert_nothing_raised { realpath("#{dir}/exist2") } + File.symlink("loop-relative", "loop-relative") assert_raise(Errno::ELOOP) { realpath("#{dir}/loop-relative") } @@ -332,6 +345,28 @@ } end + def realdirpath(path) + Pathname.new(path).realdirpath.to_s + end + + def test_realdirpath + return if !has_symlink? + Dir.mktmpdir('rubytest-pathname') {|dir| + rdir = realpath(dir) + assert_equal("#{rdir}/not-exist", realdirpath("#{dir}/not-exist")) + assert_raise(Errno::ENOENT) { realdirpath("#{dir}/not-exist/not-exist-child") } + File.symlink("not-exist-target", "#{dir}/not-exist") + assert_equal("#{rdir}/not-exist-target", realdirpath("#{dir}/not-exist")) + File.symlink("../#{File.basename(dir)}/./not-exist-target", "#{dir}/not-exist2") + assert_equal("#{rdir}/not-exist-target", realdirpath("#{dir}/not-exist2")) + File.open("#{dir}/exist-target", "w") {} + File.symlink("../#{File.basename(dir)}/./exist-target", "#{dir}/exist") + assert_equal("#{rdir}/exist-target", realdirpath("#{dir}/exist")) + File.symlink("loop", "#{dir}/loop") + assert_raise(Errno::ELOOP) { realdirpath("#{dir}/loop") } + } + end + def descend(path) Pathname.new(path).enum_for(:descend).map {|v| v.to_s } end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/