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)