stdlib/pathname.rb
# backtick_javascript: true
require 'corelib/comparable'
# Portions from Author:: Tanaka Akira <akr@m17n.org>
class Pathname
include Comparable
SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/
def initialize(path)
if Pathname === path
@path = path.path.to_s
elsif path.respond_to?(:to_path)
@path = path.to_path
elsif path.is_a?(String)
@path = path
elsif path.nil?
raise TypeError, 'no implicit conversion of nil into String'
else
raise TypeError, "no implicit conversion of #{path.class} into String"
end
raise ArgumentError if @path == "\0"
end
def self.pwd
new(Dir.pwd)
end
attr_reader :path
def ==(other)
other.path == @path
end
def absolute?
!relative?
end
def relative?
path = @path
while (r = chop_basename(path))
path, = r
end
path == ''
end
def chop_basename(path) # :nodoc:
base = File.basename(path)
# ruby uses /^#{SEPARATOR_PAT}?$/o but having issues with interpolation
if Regexp.new("^#{Pathname::SEPARATOR_PAT.source}?$") =~ base
return nil
else
return path[0, path.rindex(base)], base
end
end
def root?
@path == '/'
end
def parent
new_path = @path.sub(%r{/([^/]+/?$)}, '')
new_path = absolute? ? '/' : '.' if new_path == ''
Pathname.new(new_path)
end
def sub(*args)
Pathname.new(@path.sub(*args))
end
def cleanpath
`return Opal.normalize(#{@path})`
end
def to_path
@path
end
def hash
@path.hash
end
def expand_path
Pathname.new(File.expand_path(@path))
end
def +(other)
other = Pathname.new(other) unless Pathname === other
Pathname.new(plus(@path, other.to_s))
end
def plus(path1, path2) # -> path # :nodoc:
prefix2 = path2
index_list2 = []
basename_list2 = []
while (r2 = chop_basename(prefix2))
prefix2, basename2 = r2
index_list2.unshift prefix2.length
basename_list2.unshift basename2
end
return path2 if prefix2 != ''
prefix1 = path1
while true
while !basename_list2.empty? && basename_list2.first == '.'
index_list2.shift
basename_list2.shift
end
break unless (r1 = chop_basename(prefix1))
prefix1, basename1 = r1
next if basename1 == '.'
if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..'
prefix1 += basename1
break
end
index_list2.shift
basename_list2.shift
end
r1 = chop_basename(prefix1)
if !r1 && /#{SEPARATOR_PAT}/ =~ File.basename(prefix1)
while !basename_list2.empty? && basename_list2.first == '..'
index_list2.shift
basename_list2.shift
end
end
if !basename_list2.empty?
suffix2 = path2[index_list2.first..-1]
r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2
else
r1 ? prefix1 : File.dirname(prefix1)
end
end
def join(*args)
return self if args.empty?
result = args.pop
result = Pathname.new(result) unless Pathname === result
return result if result.absolute?
args.reverse_each do |arg|
arg = Pathname.new(arg) unless Pathname === arg
result = arg + result
return result if result.absolute?
end
self + result
end
def split
[dirname, basename]
end
def dirname
Pathname.new(File.dirname(@path))
end
def basename
Pathname.new(File.basename(@path))
end
def directory?
File.directory?(@path)
end
def extname
File.extname(@path)
end
def <=>(other)
path <=> other.path
end
SAME_PATHS = if File::FNM_SYSCASE.nonzero?
# Avoid #zero? here because #casecmp can return nil.
proc { |a, b| a.casecmp(b) == 0 }
else
proc { |a, b| a == b }
end
def relative_path_from(base_directory)
dest_directory = cleanpath.to_s
base_directory = base_directory.cleanpath.to_s
dest_prefix = dest_directory
dest_names = []
while (r = chop_basename(dest_prefix))
dest_prefix, basename = r
dest_names.unshift basename if basename != '.'
end
base_prefix = base_directory
base_names = []
while (r = chop_basename(base_prefix))
base_prefix, basename = r
base_names.unshift basename if basename != '.'
end
unless SAME_PATHS[dest_prefix, base_prefix]
raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
end
while !dest_names.empty? &&
!base_names.empty? &&
SAME_PATHS[dest_names.first, base_names.first]
dest_names.shift
base_names.shift
end
if base_names.include? '..'
raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
end
base_names.fill('..')
relpath_names = base_names + dest_names
if relpath_names.empty?
Pathname.new('.')
else
Pathname.new(File.join(*relpath_names))
end
end
def entries
Dir.entries(@path).map { |f| self.class.new(f) }
end
alias === ==
alias eql? ==
alias to_s to_path
alias to_str to_path
end
module Kernel
def Pathname(path)
Pathname.new(path)
end
end