From 4761dc18b51ad1167f5eec4b08ce00eb684d7da2 Mon Sep 17 00:00:00 2001 From: moste00 Date: Mon, 6 Mar 2023 18:56:37 +0200 Subject: [PATCH] file continue file continue file spec . done renamed helper --- spec/ruby/core/file/dirname_spec.rb | 14 ++++++ spec/tags/core/file/dirname_tags.txt | 2 - src/main/ruby/truffleruby/core/file.rb | 43 +++++++------------ .../core/truffle/file_operations.rb | 29 +++++++++++++ 4 files changed, 59 insertions(+), 29 deletions(-) delete mode 100644 spec/tags/core/file/dirname_tags.txt diff --git a/spec/ruby/core/file/dirname_spec.rb b/spec/ruby/core/file/dirname_spec.rb index cf0f909f5937..730232223f71 100644 --- a/spec/ruby/core/file/dirname_spec.rb +++ b/spec/ruby/core/file/dirname_spec.rb @@ -25,6 +25,20 @@ it "raises ArgumentError if the level is negative" do -> {File.dirname('/home/jason', -1)}.should raise_error(ArgumentError) end + + it "returns the exact same object when passed 0 as the level" do + obj = "path" + File.dirname(obj,0).should .equal?(obj) + + obj = mock("to_path") + def obj.to_path + "path" + end + File.dirname(obj,0).should .equal?(obj) + + obj = Object.new + File.dirname(obj,0).should .equal?(obj) + end end it "returns a String" do diff --git a/spec/tags/core/file/dirname_tags.txt b/spec/tags/core/file/dirname_tags.txt deleted file mode 100644 index 3db43d88f94e..000000000000 --- a/spec/tags/core/file/dirname_tags.txt +++ /dev/null @@ -1,2 +0,0 @@ -fails:File.dirname returns all the components of filename except the last parts by the level -fails:File.dirname returns the same string if the level is 0 diff --git a/src/main/ruby/truffleruby/core/file.rb b/src/main/ruby/truffleruby/core/file.rb index c5731dd3accd..346107226186 100644 --- a/src/main/ruby/truffleruby/core/file.rb +++ b/src/main/ruby/truffleruby/core/file.rb @@ -415,37 +415,26 @@ def self.last_nonslash(path, start=nil) # the separator used on the local file system. # # File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work" - def self.dirname(path) - path = Truffle::Type.coerce_to_path(path) - - # edge case - return +'.' if path.empty? - - slash = '/' - - # pull off any /'s at the end to ignore - chunk_size = last_nonslash(path) - return +'/' unless chunk_size - - if pos = Primitive.find_string_reverse(path, slash, chunk_size) - return +'/' if pos == 0 - - path = path.byteslice(0, pos) - - return +'/' if path == '/' - - return path unless path.end_with? slash + def self.dirname(path,num_levels=1) + if num_levels < 0 + raise ArgumentError, "level can't be negative" + end + #This must happen before the type coercion to string below, because File.dirname accepts any objects which can respond to a :to_path message + #If path was such an object, File.dirname(obj,0) returns it as-is without conversion to string + #Tricky, but that's how Matz ruby behave + if num_levels == 0 + return path + end - # prune any trailing /'s - idx = last_nonslash(path, pos) + path = Truffle::Type.coerce_to_path(path) - # edge case, only /'s, return / - return +'/' unless idx + num_levels.times do + # edge case + return +'.' if path.empty? - return path.byteslice(0, idx - 1) + path = Truffle::FileOperations.remove_last_segment_from_path(path) end - - +'.' + path end ## diff --git a/src/main/ruby/truffleruby/core/truffle/file_operations.rb b/src/main/ruby/truffleruby/core/truffle/file_operations.rb index 8ff567137258..fc30bb4d0cc9 100644 --- a/src/main/ruby/truffleruby/core/truffle/file_operations.rb +++ b/src/main/ruby/truffleruby/core/truffle/file_operations.rb @@ -78,5 +78,34 @@ def self.expand_path(path, dir, expand_tilde) def self.exist?(path) Truffle::POSIX.truffleposix_stat_mode(path) > 0 end + + #Helper to File.dirname, extracts the directory of a path (doesn't need to handle the empty string) + #This was the original File.dirname, but ruby 3.1 made File.dirname take an optional number of times to do its logic + def self.remove_last_segment_from_path(path) + slash = '/' + # pull off any /'s at the end to ignore + chunk_size = File.last_nonslash(path) + return +'/' unless chunk_size + + if pos = Primitive.find_string_reverse(path, slash, chunk_size) + return +'/' if pos == 0 + + path = path.byteslice(0, pos) + + return +'/' if path == '/' + + return path unless path.end_with? slash + + # prune any trailing /'s + idx = File.last_nonslash(path, pos) + + # edge case, only /'s, return / + return +'/' unless idx + + return path.byteslice(0, idx - 1) + end + + +'.' + end end end