diff --git a/.document b/.document index 82ca602bfb74fe..753d6f9892456c 100644 --- a/.document +++ b/.document @@ -42,6 +42,9 @@ lib # and some of the ext/ directory (which has its own .document file) ext +# For `prism`, ruby code is in lib and c in the prism folder +prism + # rdoc files NEWS.md diff --git a/dir.c b/dir.c index 25ed59c668fab6..fc2e6b77152145 100644 --- a/dir.c +++ b/dir.c @@ -504,6 +504,20 @@ fnmatch( } VALUE rb_cDir; +static VALUE sym_directory, sym_link, sym_file, sym_unknown; + +#ifdef DT_BLK +static VALUE sym_block_device; +#endif +#ifdef DT_CHR +static VALUE sym_character_device; +#endif +#ifdef DT_FIFO +static VALUE sym_fifo; +#endif +#ifdef DT_SOCK +static VALUE sym_socket; +#endif struct dir_data { DIR *dir; @@ -905,14 +919,61 @@ dir_read(VALUE dir) } } -static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE), VALUE, int); +static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE, unsigned char), VALUE, int); static VALUE -dir_yield(VALUE arg, VALUE path) +dir_yield(VALUE arg, VALUE path, unsigned char dtype) { return rb_yield(path); } +static VALUE +dir_yield_with_type(VALUE arg, VALUE path, unsigned char dtype) +{ + VALUE type; + switch (dtype) { +#ifdef DT_BLK + case DT_BLK: + type = sym_block_device; + break; +#endif +#ifdef DT_CHR + case DT_CHR: + type = sym_character_device; + break; +#endif + case DT_DIR: + type = sym_directory; + break; +#ifdef DT_FIFO + case DT_FIFO: + type = sym_fifo; + break; +#endif + case DT_LNK: + type = sym_link; + break; + case DT_REG: + type = sym_file; + break; +#ifdef DT_SOCK + case DT_SOCK: + type = sym_socket; + break; +#endif + default: + type = sym_unknown; + break; + } + + if (NIL_P(arg)) { + return rb_yield_values(2, path, type); + } + else { + return rb_ary_push(arg, rb_assoc_new(path, type)); + } +} + /* * call-seq: * each {|entry_name| ... } -> self @@ -940,7 +1001,7 @@ dir_each(VALUE dir) } static VALUE -dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_only) +dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE, unsigned char), VALUE arg, int children_only) { struct dir_data *dirp; struct dirent *dp; @@ -966,7 +1027,7 @@ dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_o else #endif path = rb_external_str_new_with_enc(name, namlen, dirp->enc); - (*each)(arg, path); + (*each)(arg, path, dp->d_type); } return dir; } @@ -3470,11 +3531,17 @@ dir_foreach(int argc, VALUE *argv, VALUE io) return Qnil; } +static VALUE +dir_entry_ary_push(VALUE ary, VALUE entry, unsigned char ftype) +{ + return rb_ary_push(ary, entry); +} + static VALUE dir_collect(VALUE dir) { VALUE ary = rb_ary_new(); - dir_each_entry(dir, rb_ary_push, ary, FALSE); + dir_each_entry(dir, dir_entry_ary_push, ary, FALSE); return ary; } @@ -3569,10 +3636,35 @@ static VALUE dir_collect_children(VALUE dir) { VALUE ary = rb_ary_new(); - dir_each_entry(dir, rb_ary_push, ary, TRUE); + dir_each_entry(dir, dir_entry_ary_push, ary, TRUE); return ary; } +/* + * call-seq: + * children -> array + * + * Returns an array of the entry names in +self+ along with their type + * except for '.' and '..': + * + * dir = Dir.new('/example') + * dir.scan # => [["config.h", :file], ["lib", :directory], ["main.rb", :file]] + * + */ +static VALUE +dir_scan_children(VALUE dir) +{ + if (rb_block_given_p()) { + dir_each_entry(dir, dir_yield_with_type, Qnil, TRUE); + return Qnil; + } + else { + VALUE ary = rb_ary_new(); + dir_each_entry(dir, dir_yield_with_type, ary, TRUE); + return ary; + } +} + /* * call-seq: * Dir.children(dirpath) -> array @@ -3601,6 +3693,40 @@ dir_s_children(int argc, VALUE *argv, VALUE io) return rb_ensure(dir_collect_children, dir, dir_close, dir); } +/* + * call-seq: + * Dir.scan(dirpath) {|entry_name, entry_type| ... } -> nil + * Dir.scan(dirpath, encoding: 'UTF-8') {|entry_name, entry_type| ... } -> nil + * Dir.scan(dirpath) -> [[entry_name, entry_type], ...] + * Dir.scan(dirpath, encoding: 'UTF-8') -> [[entry_name, entry_type], ...] + * + * Yields or returns an array of the entry names in the directory at +dirpath+ + * associated with their type, except for '.' and '..'; + * sets the given encoding onto each returned entry name. + * + * The type symbol is one of: + * ``:file'', ``:directory'', + * ``:characterSpecial'', ``:blockSpecial'', + * ``:fifo'', ``:link'', + * or ``:socket'': + * + * Dir.children('/example') # => [["config.h", :file], ["lib", :directory], ["main.rb", :file]] + * Dir.children('/example').first.first.encoding + * # => # + * Dir.children('/example', encoding: 'US-ASCII').first.encoding + * # => # + * + * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding]. + * + * Raises an exception if the directory does not exist. + */ +static VALUE +dir_s_scan(int argc, VALUE *argv, VALUE klass) +{ + VALUE dir = dir_open_dir(argc, argv); + return rb_ensure(dir_scan_children, dir, dir_close, dir); +} + static int fnmatch_brace(const char *pattern, VALUE val, void *enc) { @@ -3804,6 +3930,24 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname) void Init_Dir(void) { + sym_directory = ID2SYM(rb_intern("directory")); + sym_link = ID2SYM(rb_intern("link")); + sym_file = ID2SYM(rb_intern("file")); + sym_unknown = ID2SYM(rb_intern("unknown")); + +#ifdef DT_BLK + sym_block_device = ID2SYM(rb_intern("blockSpecial")); +#endif +#ifdef DT_CHR + sym_character_device = ID2SYM(rb_intern("characterSpecial")); +#endif +#ifdef DT_FIFO + sym_fifo = ID2SYM(rb_intern("fifo")); +#endif +#ifdef DT_SOCK + sym_socket = ID2SYM(rb_intern("socket")); +#endif + rb_gc_register_address(&chdir_lock.path); rb_gc_register_address(&chdir_lock.thread); @@ -3817,6 +3961,7 @@ Init_Dir(void) rb_define_singleton_method(rb_cDir, "entries", dir_entries, -1); rb_define_singleton_method(rb_cDir, "each_child", dir_s_each_child, -1); rb_define_singleton_method(rb_cDir, "children", dir_s_children, -1); + rb_define_singleton_method(rb_cDir, "scan", dir_s_scan, -1); rb_define_method(rb_cDir,"fileno", dir_fileno, 0); rb_define_method(rb_cDir,"path", dir_path, 0); @@ -3826,6 +3971,7 @@ Init_Dir(void) rb_define_method(rb_cDir,"each", dir_each, 0); rb_define_method(rb_cDir,"each_child", dir_each_child_m, 0); rb_define_method(rb_cDir,"children", dir_collect_children, 0); + rb_define_method(rb_cDir,"scan", dir_scan_children, 0); rb_define_method(rb_cDir,"rewind", dir_rewind, 0); rb_define_method(rb_cDir,"tell", dir_tell, 0); rb_define_method(rb_cDir,"seek", dir_seek, 1); diff --git a/enc/make_encmake.rb b/enc/make_encmake.rb index 4be5c428880d7e..e5ed4eb4b56ba4 100755 --- a/enc/make_encmake.rb +++ b/enc/make_encmake.rb @@ -1,7 +1,11 @@ #! ./miniruby dir = File.expand_path("../..", __FILE__) -$:.unshift(Dir.pwd, "#{dir}/tool/lib", "#{dir}/lib") +# The source lib directory provides the standard library for miniruby. +# Don't add it when running with baseruby to avoid loading both +# baseruby's cgi/escape.so and source cgi/escape.rb via erb. +$:.unshift("#{dir}/lib") unless defined?(CROSS_COMPILING) +$:.unshift(Dir.pwd, "#{dir}/tool/lib") if $".grep(/mkmf/).empty? $" << "mkmf.rb" load File.expand_path("lib/mkmf.rb", dir) diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index d5cfda1be949d9..4832916ce6ea2f 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -689,8 +689,8 @@ rb_gc_impl_start(void *objspace_ptr, bool full_mark, bool immediate_mark, bool i bool rb_gc_impl_during_gc_p(void *objspace_ptr) { - // TODO - return false; + struct objspace *objspace = objspace_ptr; + return objspace->world_stopped; } static void diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index e1e030ffc8992f..e679010e97dc05 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -504,7 +504,12 @@ def download_cache_path(spec) return unless remote = spec.remote return unless cache_slug = remote.cache_slug - Bundler.user_cache.join("gems", cache_slug) + if Gem.respond_to?(:global_gem_cache_path) + Pathname.new(Gem.global_gem_cache_path).join(cache_slug) + else + # Fall back to old location for older RubyGems versions + Bundler.user_cache.join("gems", cache_slug) + end end def extension_cache_slug(spec) diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb index e8d2ce1b192ceb..3d5cbfcddc0edd 100644 --- a/lib/prism/lex_compat.rb +++ b/lib/prism/lex_compat.rb @@ -768,21 +768,24 @@ def result source.byte_offset(line, column) end - # Add :on_sp tokens - tokens = insert_on_sp(tokens, source, result.data_loc, bom, eof_token) + tokens = post_process_tokens(tokens, source, result.data_loc, bom, eof_token) Result.new(tokens, result.comments, result.magic_comments, result.data_loc, result.errors, result.warnings, source) end private - def insert_on_sp(tokens, source, data_loc, bom, eof_token) + def post_process_tokens(tokens, source, data_loc, bom, eof_token) new_tokens = [] prev_token_state = Translation::Ripper::Lexer::State[Translation::Ripper::EXPR_BEG] prev_token_end = bom ? 3 : 0 tokens.each do |token| + # Skip missing heredoc ends. + next if token[1] == :on_heredoc_end && token[2] == "" + + # Add :on_sp tokens. line, column = token[0] start_offset = source.byte_offset(line, column) diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 2139264629c422..490c81821dafd7 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -351,6 +351,8 @@ def self.plugindir(install_dir = Gem.dir) def self.clear_paths @paths = nil @user_home = nil + @cache_home = nil + @data_home = nil Gem::Specification.reset Gem::Security.reset if defined?(Gem::Security) end diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb index e58a83f6b75a61..f2c90cce3cb76a 100644 --- a/lib/rubygems/config_file.rb +++ b/lib/rubygems/config_file.rb @@ -49,6 +49,7 @@ class Gem::ConfigFile DEFAULT_IPV4_FALLBACK_ENABLED = false # TODO: Use false as default value for this option in RubyGems 4.0 DEFAULT_INSTALL_EXTENSION_IN_LIB = true + DEFAULT_GLOBAL_GEM_CACHE = false ## # For Ruby packagers to set configuration defaults. Set in @@ -155,6 +156,12 @@ class Gem::ConfigFile attr_accessor :ipv4_fallback_enabled + ## + # Use a global cache for .gem files shared across all Ruby installations. + # When enabled, gems are cached to ~/.cache/gem/gems (or XDG_CACHE_HOME/gem/gems). + + attr_accessor :global_gem_cache + ## # Path name of directory or file of openssl client certificate, used for remote https connection with client authentication @@ -192,6 +199,7 @@ def initialize(args) @cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS @install_extension_in_lib = DEFAULT_INSTALL_EXTENSION_IN_LIB @ipv4_fallback_enabled = ENV["IPV4_FALLBACK_ENABLED"] == "true" || DEFAULT_IPV4_FALLBACK_ENABLED + @global_gem_cache = ENV["RUBYGEMS_GLOBAL_GEM_CACHE"] == "true" || DEFAULT_GLOBAL_GEM_CACHE operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS) platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS) @@ -213,8 +221,8 @@ def initialize(args) @hash.transform_keys! do |k| # gemhome and gempath are not working with symbol keys if %w[backtrace bulk_threshold verbose update_sources cert_expiration_length_days - install_extension_in_lib ipv4_fallback_enabled sources disable_default_gem_server - ssl_verify_mode ssl_ca_cert ssl_client_cert].include?(k) + install_extension_in_lib ipv4_fallback_enabled global_gem_cache sources + disable_default_gem_server ssl_verify_mode ssl_ca_cert ssl_client_cert].include?(k) k.to_sym else k @@ -230,6 +238,7 @@ def initialize(args) @cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days @install_extension_in_lib = @hash[:install_extension_in_lib] if @hash.key? :install_extension_in_lib @ipv4_fallback_enabled = @hash[:ipv4_fallback_enabled] if @hash.key? :ipv4_fallback_enabled + @global_gem_cache = @hash[:global_gem_cache] if @hash.key? :global_gem_cache @home = @hash[:gemhome] if @hash.key? :gemhome @path = @hash[:gempath] if @hash.key? :gempath diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb index 90f09fc1917481..2247c49c81ec4c 100644 --- a/lib/rubygems/defaults.rb +++ b/lib/rubygems/defaults.rb @@ -148,6 +148,15 @@ def self.cache_home @cache_home ||= ENV["XDG_CACHE_HOME"] || File.join(Gem.user_home, ".cache") end + ## + # The path to the global gem cache directory. + # This is used when global_gem_cache is enabled to share .gem files + # across all Ruby installations. + + def self.global_gem_cache_path + File.join(cache_home, "gem", "gems") + end + ## # The path to standard location of the user's data directory. diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 151c6fd4d8bbdf..ea8d3f9d1f1029 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -111,9 +111,13 @@ def download_to_cache(dependency) # always replaced. def download(spec, source_uri, install_dir = Gem.dir) + gem_file_name = File.basename spec.cache_file + install_cache_dir = File.join install_dir, "cache" cache_dir = - if Dir.pwd == install_dir # see fetch_command + if Gem.configuration.global_gem_cache + Gem.global_gem_cache_path + elsif Dir.pwd == install_dir # see fetch_command install_dir elsif File.writable?(install_cache_dir) || (File.writable?(install_dir) && !File.exist?(install_cache_dir)) install_cache_dir @@ -121,7 +125,6 @@ def download(spec, source_uri, install_dir = Gem.dir) File.join Gem.user_dir, "cache" end - gem_file_name = File.basename spec.cache_file local_gem_path = File.join cache_dir, gem_file_name require "fileutils" diff --git a/prism/srcs.mk.in b/prism/srcs.mk.in index cc263fd1b4ef3f..d9dcd9b802b7e7 100644 --- a/prism/srcs.mk.in +++ b/prism/srcs.mk.in @@ -1,4 +1,5 @@ <% # -*- ruby -*- +# :stopdoc: require_relative 'templates/template' script = File.basename(__FILE__) diff --git a/prism/templates/template.rb b/prism/templates/template.rb index 0c695fade5a008..28ac475cdf6eb1 100755 --- a/prism/templates/template.rb +++ b/prism/templates/template.rb @@ -6,7 +6,7 @@ require "yaml" module Prism - module Template + module Template # :nodoc: all SERIALIZE_ONLY_SEMANTICS_FIELDS = ENV.fetch("PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS", false) REMOVE_ON_ERROR_TYPES = SERIALIZE_ONLY_SEMANTICS_FIELDS CHECK_FIELD_KIND = ENV.fetch("CHECK_FIELD_KIND", false) diff --git a/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb index 0a7daf173c8b0e..77ab94609ee566 100644 --- a/spec/bundler/install/global_cache_spec.rb +++ b/spec/bundler/install/global_cache_spec.rb @@ -8,7 +8,13 @@ let(:source2) { "http://gemserver.example.org" } def cache_base - home(".bundle", "cache", "gems") + # Use the unified global gem cache path if available (from RubyGems), + # otherwise fall back to the Bundler-specific cache location + if Gem.respond_to?(:global_gem_cache_path) + Pathname.new(Gem.global_gem_cache_path) + else + home(".bundle", "cache", "gems") + end end def source_global_cache(*segments) diff --git a/spec/ruby/core/dir/fixtures/common.rb b/spec/ruby/core/dir/fixtures/common.rb index 848656c9b9a184..cfec91f68fa04d 100644 --- a/spec/ruby/core/dir/fixtures/common.rb +++ b/spec/ruby/core/dir/fixtures/common.rb @@ -115,6 +115,7 @@ def self.mock_dir_links end def self.create_mock_dirs + delete_mock_dirs mock_dir_files.each do |name| file = File.join mock_dir, name mkdir_p File.dirname(file) @@ -172,24 +173,28 @@ def self.rmdir_dirs(create = true) end end + def self.expected_paths_with_type + [ + [".", :directory], + ["..", :directory], + [".dotfile", :file], + [".dotsubdir", :directory], + ["brace", :directory], + ["deeply", :directory], + ["dir", :directory], + ["dir_filename_ordering", :file], + ["file_one.ext", :file], + ["file_two.ext", :file], + ["nested", :directory], + ["nondotfile", :file], + ["special", :directory], + ["subdir_one", :directory], + ["subdir_two", :directory], + ] + end + def self.expected_paths - %w[ - . - .. - .dotfile - .dotsubdir - brace - deeply - dir - dir_filename_ordering - file_one.ext - file_two.ext - nested - nondotfile - special - subdir_one - subdir_two - ] + expected_paths_with_type.map(&:first) end def self.expected_glob_paths diff --git a/spec/ruby/core/dir/scan_spec.rb b/spec/ruby/core/dir/scan_spec.rb new file mode 100644 index 00000000000000..a34eedf13bce4f --- /dev/null +++ b/spec/ruby/core/dir/scan_spec.rb @@ -0,0 +1,224 @@ +# encoding: utf-8 + +require_relative '../../spec_helper' +require_relative 'fixtures/common' +require_relative '../file/fixtures/file_types' + +ruby_version_is "4.1" do + describe "Dir.scan" do + before :all do + FileSpecs.configure_types + end + + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + end + + it "returns an Array of filename and type pairs in an existing directory including dotfiles" do + a = Dir.scan(DirSpecs.mock_dir).sort + + a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]] + + a = Dir.scan("#{DirSpecs.mock_dir}/deeply/nested").sort + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "yields filename and type in an existing directory including dotfiles" do + a = [] + Dir.scan(DirSpecs.mock_dir) do |n, t| + a << [n, t] + end + a.sort! + a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]] + + a = [] + Dir.scan("#{DirSpecs.mock_dir}/deeply/nested") do |n, t| + a << [n, t] + end + a.sort! + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(DirSpecs.mock_dir) + Dir.scan(p) + end + + it "accepts an options Hash" do + a = Dir.scan("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").sort + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "returns children names encoded with the filesystem encoding by default" do + # This spec depends on the locale not being US-ASCII because if it is, the + # children that are not ascii_only? will be BINARY encoded. + children = Dir.scan(File.join(DirSpecs.mock_dir, 'special')).sort + encoding = Encoding.find("filesystem") + encoding = Encoding::BINARY if encoding == Encoding::US_ASCII + platform_is_not :windows do + children.should include(["こんにちは.txt".dup.force_encoding(encoding), :file]) + end + children.first.first.encoding.should equal(Encoding.find("filesystem")) + end + + it "returns children names encoded with the specified encoding" do + dir = File.join(DirSpecs.mock_dir, 'special') + children = Dir.scan(dir, encoding: "euc-jp").sort + children.first.first.encoding.should equal(Encoding::EUC_JP) + end + + it "returns children names transcoded to the default internal encoding" do + Encoding.default_internal = Encoding::EUC_KR + children = Dir.scan(File.join(DirSpecs.mock_dir, 'special')).sort + children.first.first.encoding.should equal(Encoding::EUC_KR) + end + + it "raises a SystemCallError if called with a nonexistent directory" do + -> { Dir.scan DirSpecs.nonexistent }.should raise_error(SystemCallError) + end + + it "handles symlink" do + FileSpecs.symlink do |path| + Dir.scan(File.dirname(path)).map(&:last).should include(:link) + end + end + + platform_is_not :windows do + it "handles socket" do + FileSpecs.socket do |path| + Dir.scan(File.dirname(path)).map(&:last).should include(:socket) + end + end + + it "handles FIFO" do + FileSpecs.fifo do |path| + Dir.scan(File.dirname(path)).map(&:last).should include(:fifo) + end + end + + it "handles character devices" do + FileSpecs.character_device do |path| + Dir.scan(File.dirname(path)).map(&:last).should include(:characterSpecial) + end + end + end + + platform_is_not :freebsd, :windows do + with_block_device do + it "handles block devices" do + FileSpecs.block_device do |path| + Dir.scan(File.dirname(path)).map(&:last).should include(:blockSpecial) + end + end + end + end + end + + describe "Dir#scan" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + @dir.close if @dir + end + + it "returns an Array of filenames in an existing directory including dotfiles" do + @dir = Dir.new(DirSpecs.mock_dir) + a = @dir.scan.sort + @dir.close + + a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]] + + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") + a = @dir.scan.sort + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "yields filename and type in an existing directory including dotfiles" do + @dir = Dir.new(DirSpecs.mock_dir) + a = [] + @dir.scan do |n, t| + a << [n, t] + end + a.sort! + a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]] + + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") + a = [] + @dir.scan do |n, t| + a << [n, t] + end + a.sort! + a.should == [[".dotfile.ext", :file], ["directory", :directory]] + end + + it "accepts an encoding keyword for the encoding of the entries" do + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8") + dirs = @dir.to_a.sort + dirs.each { |d| d.encoding.should == Encoding::UTF_8 } + end + + it "returns children names encoded with the filesystem encoding by default" do + # This spec depends on the locale not being US-ASCII because if it is, the + # children that are not ascii_only? will be BINARY encoded. + @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) + children = @dir.scan.sort + encoding = Encoding.find("filesystem") + encoding = Encoding::BINARY if encoding == Encoding::US_ASCII + platform_is_not :windows do + children.should include(["こんにちは.txt".dup.force_encoding(encoding), :file]) + end + children.first.first.encoding.should equal(Encoding.find("filesystem")) + end + + it "returns children names encoded with the specified encoding" do + path = File.join(DirSpecs.mock_dir, 'special') + @dir = Dir.new(path, encoding: "euc-jp") + children = @dir.children.sort + children.first.encoding.should equal(Encoding::EUC_JP) + end + + it "returns children names transcoded to the default internal encoding" do + Encoding.default_internal = Encoding::EUC_KR + @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) + children = @dir.scan.sort + children.first.first.encoding.should equal(Encoding::EUC_KR) + end + + it "returns the same result when called repeatedly" do + @dir = Dir.open DirSpecs.mock_dir + + a = [] + @dir.each {|dir| a << dir} + + b = [] + @dir.each {|dir| b << dir} + + a.sort.should == b.sort + a.sort.should == DirSpecs.expected_paths + end + end +end diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index d7c4173e8e399c..d5f6ac64afcf2c 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -39,14 +39,6 @@ def setup JSON end - def silence - v = $VERBOSE - $VERBOSE = nil - yield - ensure - $VERBOSE = v - end - def test_generate json = generate(@hash) assert_equal(parse(@json2), parse(json)) diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 1b875422a1adc5..54a6bbbd6173d0 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -138,7 +138,8 @@ def test_parse_bignum bignum = Integer('1234567890' * 50) assert_equal(bignum, JSON.parse(bignum.to_s)) - assert_equal(bignum.to_f, JSON.parse(bignum.to_s + ".0")) + bignum_float = EnvUtil.suppress_warning { bignum.to_f } + assert_equal(bignum_float, EnvUtil.suppress_warning { JSON.parse(bignum.to_s + ".0") }) end def test_parse_bigdecimals diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 15f535f3d6a157..39cb9395ab680e 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -81,6 +81,16 @@ class RipperTest < TestCase define_method("#{fixture.test_name}_lex") { assert_ripper_lex(fixture.read) } end + def test_lex_ignored_missing_heredoc_end + ["", "-", "~"].each do |type| + source = "<<#{type}FOO\n" + assert_ripper_lex(source) + + source = "<<#{type}'FOO'\n" + assert_ripper_lex(source) + end + end + module Events attr_reader :events diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb index 4230eda4d399ff..79bf5f582c942c 100644 --- a/test/rubygems/test_gem_config_file.rb +++ b/test/rubygems/test_gem_config_file.rb @@ -83,6 +83,33 @@ def test_initialize_ipv4_fallback_enabled_env util_config_file %W[--config-file #{@temp_conf}] assert_equal true, @cfg.ipv4_fallback_enabled + ensure + ENV.delete("IPV4_FALLBACK_ENABLED") + end + + def test_initialize_global_gem_cache_default + util_config_file %W[--config-file #{@temp_conf}] + + assert_equal false, @cfg.global_gem_cache + end + + def test_initialize_global_gem_cache_env + ENV["RUBYGEMS_GLOBAL_GEM_CACHE"] = "true" + util_config_file %W[--config-file #{@temp_conf}] + + assert_equal true, @cfg.global_gem_cache + ensure + ENV.delete("RUBYGEMS_GLOBAL_GEM_CACHE") + end + + def test_initialize_global_gem_cache_gemrc + File.open @temp_conf, "w" do |fp| + fp.puts "global_gem_cache: true" + end + + util_config_file %W[--config-file #{@temp_conf}] + + assert_equal true, @cfg.global_gem_cache end def test_initialize_handle_arguments_config_file diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index 9badd75b427169..922819315224aa 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -575,6 +575,74 @@ def test_yaml_error_on_size end end + def test_download_with_global_gem_cache + # Use a temp directory to safely test global cache behavior + test_cache_dir = File.join(@tempdir, "global_gem_cache_test") + + Gem.stub :global_gem_cache_path, test_cache_dir do + Gem.configuration.global_gem_cache = true + + # Use the real RemoteFetcher with stubbed fetch_path + fetcher = Gem::RemoteFetcher.fetcher + def fetcher.fetch_path(uri, *rest) + File.binread File.join(@test_gem_dir, "a-1.gem") + end + fetcher.instance_variable_set(:@test_gem_dir, File.dirname(@a1_gem)) + + # With global cache enabled, gem goes directly to global cache + global_cache_gem = File.join(test_cache_dir, @a1.file_name) + assert_equal global_cache_gem, fetcher.download(@a1, "http://gems.example.com") + assert File.exist?(global_cache_gem), "Gem should be in global cache" + end + ensure + Gem.configuration.global_gem_cache = false + end + + def test_download_uses_global_gem_cache + # Use a temp directory to safely test global cache behavior + test_cache_dir = File.join(@tempdir, "global_gem_cache_test") + + Gem.stub :global_gem_cache_path, test_cache_dir do + Gem.configuration.global_gem_cache = true + + # Pre-populate global cache + FileUtils.mkdir_p test_cache_dir + global_cache_gem = File.join(test_cache_dir, @a1.file_name) + FileUtils.cp @a1_gem, global_cache_gem + + fetcher = Gem::RemoteFetcher.fetcher + + # Should return global cache path without downloading + result = fetcher.download(@a1, "http://gems.example.com") + assert_equal global_cache_gem, result + end + ensure + Gem.configuration.global_gem_cache = false + end + + def test_download_without_global_gem_cache + # Use a temp directory to safely test global cache behavior + test_cache_dir = File.join(@tempdir, "global_gem_cache_test") + + Gem.stub :global_gem_cache_path, test_cache_dir do + Gem.configuration.global_gem_cache = false + + # Use the real RemoteFetcher with stubbed fetch_path + fetcher = Gem::RemoteFetcher.fetcher + def fetcher.fetch_path(uri, *rest) + File.binread File.join(@test_gem_dir, "a-1.gem") + end + fetcher.instance_variable_set(:@test_gem_dir, File.dirname(@a1_gem)) + + a1_cache_gem = @a1.cache_file + assert_equal a1_cache_gem, fetcher.download(@a1, "http://gems.example.com") + + # Verify gem was NOT copied to global cache + global_cache_gem = File.join(test_cache_dir, @a1.file_name) + refute File.exist?(global_cache_gem), "Gem should not be copied to global cache when disabled" + end + end + private def assert_error(exception_class = Exception)