diff --git a/ext/pathname/pathname.c b/ext/pathname/pathname.c
index 10d055f..73be963 100644
--- a/ext/pathname/pathname.c
+++ b/ext/pathname/pathname.c
@@ -3,6 +3,7 @@
static VALUE rb_cPathname;
static ID id_at_path;
static ID id_sub;
+static ID id_realdirpath;
static VALUE
get_strpath(VALUE obj)
@@ -83,6 +84,22 @@ path_sub(int argc, VALUE *argv, VALUE self)
return rb_class_new_instance(1, &str, rb_obj_class(self));
}
+/*
+ * Returns the real (absolute) pathname of +self+ in the actual filesystem.
+ *
+ * Does not contain symlinks or useless dots, +..+ and +.+.
+ *
+ * The last component of the real pathname can be nonexistent.
+ */
+static VALUE
+path_realdirpath(int argc, VALUE *argv, VALUE self)
+{
+ VALUE basedir, str;
+ rb_scan_args(argc, argv, "01", &basedir);
+ str = rb_funcall(rb_cFile, id_realdirpath, 2, get_strpath(self), basedir);
+ return rb_class_new_instance(1, &str, rb_obj_class(self));
+}
+
static void init_ids(void);
void
@@ -102,6 +119,7 @@ InitVM_pathname(void)
rb_cPathname = rb_define_class("Pathname", rb_cObject);
rb_define_method(rb_cPathname, "<=>", path_cmp, 1);
rb_define_method(rb_cPathname, "sub", path_sub, -1);
+ rb_define_method(rb_cPathname, "realdirpath", path_realdirpath, -1);
}
void
@@ -110,4 +128,5 @@ init_ids(void)
#undef rb_intern
id_at_path = rb_intern("@path");
id_sub = rb_intern("sub");
+ id_realdirpath = rb_intern("realdirpath");
}
diff --git a/lib/pathname.rb b/lib/pathname.rb
index 03edc42..404be1a 100644
--- a/lib/pathname.rb
+++ b/lib/pathname.rb
@@ -146,6 +146,12 @@ module ::Kernel
# === File property and manipulation methods
#
# These methods are a facade for File:
+# - #each_line(*args, &block)
+# - #read(*args)
+# - #binread(*args)
+# - #readlines(*args)
+# - #write(*args)
+# - #binwrite(*args)
# - #atime
# - #birthtime
# - #ctime
@@ -186,14 +192,8 @@ module ::Kernel
#
# === IO
#
-# These methods are a facade for IO:
-# - #each_line(*args, &block)
-# - #read(*args)
-# - #binread(*args)
-# - #readlines(*args)
+# This method is a facade for IO:
# - #sysopen(*args)
-# - #write(*args)
-# - #binwrite(*args)
#
# === Utilities
#
@@ -214,6 +214,7 @@ module ::Kernel
#
class Pathname
+ # The version string.
VERSION = "0.4.0"
# :stopdoc:
@@ -239,6 +240,9 @@ class Pathname
#
def initialize(path)
path = path.to_path if path.respond_to? :to_path
+
+ raise TypeError unless path.is_a?(String) # Compatibility for C version
+
if path.include?("\0")
raise ArgumentError, "pathname contains \\0: #{path.inspect}"
end
@@ -338,14 +342,42 @@ def sub_ext(repl)
end
if File.dirname('A:') == 'A:.' # DOSish drive letter
- ABSOLUTE_PATH = /\A(?:[A-Za-z]:|#{SEPARATOR_PAT})/o
+ ABSOLUTE_PATH = /\A(?:[A-Za-z]:|#{SEPARATOR_PAT})/
else
- ABSOLUTE_PATH = /\A#{SEPARATOR_PAT}/o
+ ABSOLUTE_PATH = /\A#{SEPARATOR_PAT}/
end
private_constant :ABSOLUTE_PATH
# :startdoc:
+ # Creates a full path, including any intermediate directories that don't yet
+ # exist.
+ #
+ # See FileUtils.mkpath and FileUtils.mkdir_p
+ def mkpath(mode: nil)
+ path = @path == '/' ? @path : @path.chomp('/')
+
+ stack = []
+ until File.directory?(path) || File.dirname(path) == path
+ stack.push path
+ path = File.dirname(path)
+ end
+
+ stack.reverse_each do |dir|
+ dir = dir == '/' ? dir : dir.chomp('/')
+ if mode
+ Dir.mkdir dir, mode
+ File.chmod mode, dir
+ else
+ Dir.mkdir dir
+ end
+ rescue SystemCallError
+ raise unless File.directory?(dir)
+ end
+
+ self
+ end
+
# chop_basename(path) -> [pre-basename, basename] or nil
def chop_basename(path) # :nodoc:
base = File.basename(path)
@@ -853,6 +885,11 @@ def relative_path_from(base_directory)
end
class Pathname # * IO *
+ # See IO.sysopen.
+ def sysopen(...) IO.sysopen(@path, ...) end
+end
+
+class Pathname # * File *
#
# #each_line iterates over the line in the file. It yields a String object
# for each line.
@@ -860,34 +897,27 @@ class Pathname # * IO *
# This method has existed since 1.8.1.
#
def each_line(...) # :yield: line
- IO.foreach(@path, ...)
+ File.foreach(@path, ...)
end
- # See IO.read. Returns all data from the file, or the first +N+ bytes
+ # See File.read. Returns all data from the file, or the first +N+ bytes
# if specified.
- def read(...) IO.read(@path, ...) end
+ def read(...) File.read(@path, ...) end
- # See IO.binread. Returns all the bytes from the file, or the first +N+
+ # See File.binread. Returns all the bytes from the file, or the first +N+
# if specified.
- def binread(...) IO.binread(@path, ...) end
+ def binread(...) File.binread(@path, ...) end
- # See IO.readlines. Returns all the lines from the file.
- def readlines(...) IO.readlines(@path, ...) end
-
- # See IO.sysopen.
- def sysopen(...) IO.sysopen(@path, ...) end
+ # See File.readlines. Returns all the lines from the file.
+ def readlines(...) File.readlines(@path, ...) end
# Writes +contents+ to the file. See File.write.
- def write(...) IO.write(@path, ...) end
+ def write(...) File.write(@path, ...) end
# Writes +contents+ to the file, opening it in binary mode.
#
# See File.binwrite.
- def binwrite(...) IO.binwrite(@path, ...) end
-end
-
-
-class Pathname # * File *
+ def binwrite(...) File.binwrite(@path, ...) end
# See File.atime. Returns last access time.
def atime() File.atime(@path) end
@@ -1157,16 +1187,6 @@ def find(ignore_error: true) # :yield: pathname
class Pathname # * FileUtils *
- # Creates a full path, including any intermediate directories that don't yet
- # exist.
- #
- # See FileUtils.mkpath and FileUtils.mkdir_p
- def mkpath(mode: nil)
- require 'fileutils'
- FileUtils.mkpath(@path, mode: mode)
- self
- end
-
# Recursively deletes a directory, including all directories beneath it.
#
# See FileUtils.rm_rf
@@ -1216,8 +1236,12 @@ module Kernel
#
# This method is available since 1.8.5.
def Pathname(path) # :doc:
+ Kernel.Pathname(path)
+ end
+ private :Pathname
+
+ def self.Pathname(path) # Compatibility for C version
return path if Pathname === path
Pathname.new(path)
end
- private :Pathname
end