ruby-changes:30862
From: naruse <ko1@a...>
Date: Sat, 14 Sep 2013 17:59:35 +0900 (JST)
Subject: [ruby-changes:30862] naruse:r42941 (trunk): Revert r42938 "* lib/rubygems: Update to RubyGems 2.1.3"
naruse 2013-09-14 17:59:02 +0900 (Sat, 14 Sep 2013) New Revision: 42941 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=42941 Log: Revert r42938 "* lib/rubygems: Update to RubyGems 2.1.3" It breaks build. http://u64.rubyci.org/~chkbuild/ruby-trunk/log/20130913T200302Z.diff.html.gz Modified files: trunk/ChangeLog trunk/lib/rubygems/available_set.rb trunk/lib/rubygems/command_manager.rb trunk/lib/rubygems/commands/build_command.rb trunk/lib/rubygems/commands/cert_command.rb trunk/lib/rubygems/commands/check_command.rb trunk/lib/rubygems/commands/cleanup_command.rb trunk/lib/rubygems/commands/contents_command.rb trunk/lib/rubygems/commands/dependency_command.rb trunk/lib/rubygems/commands/environment_command.rb trunk/lib/rubygems/commands/fetch_command.rb trunk/lib/rubygems/commands/help_command.rb trunk/lib/rubygems/commands/install_command.rb trunk/lib/rubygems/commands/list_command.rb trunk/lib/rubygems/commands/mirror_command.rb trunk/lib/rubygems/commands/outdated_command.rb trunk/lib/rubygems/commands/owner_command.rb trunk/lib/rubygems/commands/pristine_command.rb trunk/lib/rubygems/commands/push_command.rb trunk/lib/rubygems/commands/query_command.rb trunk/lib/rubygems/commands/rdoc_command.rb trunk/lib/rubygems/commands/search_command.rb trunk/lib/rubygems/commands/sources_command.rb trunk/lib/rubygems/commands/specification_command.rb trunk/lib/rubygems/commands/stale_command.rb trunk/lib/rubygems/commands/uninstall_command.rb trunk/lib/rubygems/commands/unpack_command.rb trunk/lib/rubygems/commands/update_command.rb trunk/lib/rubygems/commands/which_command.rb trunk/lib/rubygems/commands/yank_command.rb trunk/lib/rubygems/config_file.rb trunk/lib/rubygems/core_ext/kernel_require.rb trunk/lib/rubygems/defaults.rb trunk/lib/rubygems/dependency.rb trunk/lib/rubygems/dependency_installer.rb trunk/lib/rubygems/dependency_resolver.rb trunk/lib/rubygems/exceptions.rb trunk/lib/rubygems/ext/builder.rb trunk/lib/rubygems/gem_runner.rb trunk/lib/rubygems/gemcutter_utilities.rb trunk/lib/rubygems/install_update_options.rb trunk/lib/rubygems/installer.rb trunk/lib/rubygems/name_tuple.rb trunk/lib/rubygems/package/tar_test_case.rb trunk/lib/rubygems/package/tar_writer.rb trunk/lib/rubygems/package.rb trunk/lib/rubygems/path_support.rb trunk/lib/rubygems/platform.rb trunk/lib/rubygems/psych_additions.rb trunk/lib/rubygems/remote_fetcher.rb trunk/lib/rubygems/request_set.rb trunk/lib/rubygems/security/policy.rb trunk/lib/rubygems/security/signer.rb trunk/lib/rubygems/security.rb trunk/lib/rubygems/server.rb trunk/lib/rubygems/source.rb trunk/lib/rubygems/source_local.rb trunk/lib/rubygems/source_specific_file.rb trunk/lib/rubygems/spec_fetcher.rb trunk/lib/rubygems/specification.rb trunk/lib/rubygems/test_case.rb trunk/lib/rubygems/uninstaller.rb trunk/lib/rubygems/version.rb trunk/lib/rubygems/version_option.rb trunk/lib/rubygems.rb trunk/test/rubygems/ca_cert.pem trunk/test/rubygems/test_gem.rb trunk/test/rubygems/test_gem_command_manager.rb trunk/test/rubygems/test_gem_commands_cert_command.rb trunk/test/rubygems/test_gem_commands_cleanup_command.rb trunk/test/rubygems/test_gem_commands_contents_command.rb trunk/test/rubygems/test_gem_commands_environment_command.rb trunk/test/rubygems/test_gem_commands_fetch_command.rb trunk/test/rubygems/test_gem_commands_help_command.rb trunk/test/rubygems/test_gem_commands_install_command.rb trunk/test/rubygems/test_gem_commands_owner_command.rb trunk/test/rubygems/test_gem_commands_pristine_command.rb trunk/test/rubygems/test_gem_commands_sources_command.rb trunk/test/rubygems/test_gem_commands_uninstall_command.rb trunk/test/rubygems/test_gem_commands_update_command.rb trunk/test/rubygems/test_gem_config_file.rb trunk/test/rubygems/test_gem_dependency_installer.rb trunk/test/rubygems/test_gem_dependency_resolver.rb trunk/test/rubygems/test_gem_ext_builder.rb trunk/test/rubygems/test_gem_ext_ext_conf_builder.rb trunk/test/rubygems/test_gem_gem_runner.rb trunk/test/rubygems/test_gem_gemcutter_utilities.rb trunk/test/rubygems/test_gem_install_update_options.rb trunk/test/rubygems/test_gem_installer.rb trunk/test/rubygems/test_gem_name_tuple.rb trunk/test/rubygems/test_gem_package.rb trunk/test/rubygems/test_gem_package_old.rb trunk/test/rubygems/test_gem_package_tar_reader.rb trunk/test/rubygems/test_gem_package_tar_reader_entry.rb trunk/test/rubygems/test_gem_package_tar_writer.rb trunk/test/rubygems/test_gem_path_support.rb trunk/test/rubygems/test_gem_platform.rb trunk/test/rubygems/test_gem_remote_fetcher.rb trunk/test/rubygems/test_gem_security.rb trunk/test/rubygems/test_gem_security_policy.rb trunk/test/rubygems/test_gem_security_signer.rb trunk/test/rubygems/test_gem_security_trust_dir.rb trunk/test/rubygems/test_gem_source.rb trunk/test/rubygems/test_gem_source_local.rb trunk/test/rubygems/test_gem_source_specific_file.rb trunk/test/rubygems/test_gem_spec_fetcher.rb trunk/test/rubygems/test_gem_specification.rb trunk/test/rubygems/test_gem_uninstaller.rb trunk/test/rubygems/test_gem_version.rb trunk/test/rubygems/test_gem_version_option.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 42940) +++ ChangeLog (revision 42941) @@ -8,7 +8,13 @@ Sat Sep 14 06:08:10 2013 Eric Hodel <d https://github.com/ruby/ruby/blob/trunk/ChangeLog#L8 constants. (These won't show up in RDoc until a new RDoc is imported.) -Sat Sep 14 04:57:51 2013 Eric Hodel <drbrain@s...> +Thu Sep 12 14:58:58 2013 NARUSE, Yui <naruse@r...> + + * lib/uri/generic.rb (URI::Generic.find_proxy): return nil if + http_proxy environment variable is empty string. + [ruby-core:57140] [Bug #8898] + +Fri Sep 13 10:40:28 2013 Eric Hodel <drbrain@s...> * lib/rubygems: Update to RubyGems 2.1.3 @@ -26,13 +32,6 @@ Sat Sep 14 04:57:51 2013 Eric Hodel <d https://github.com/ruby/ruby/blob/trunk/ChangeLog#L32 * NEWS: Updated for RubyGems 2.1.3 - -Thu Sep 12 14:58:58 2013 NARUSE, Yui <naruse@r...> - - * lib/uri/generic.rb (URI::Generic.find_proxy): return nil if - http_proxy environment variable is empty string. - [ruby-core:57140] [Bug #8898] - Thu Sep 12 22:40:03 2013 Nobuyoshi Nakada <nobu@r...> * configure.in (RUBY_CHECK_SIGNEDNESS): macro to check signedness of a Index: lib/rubygems/path_support.rb =================================================================== --- lib/rubygems/path_support.rb (revision 42940) +++ lib/rubygems/path_support.rb (revision 42941) @@ -13,6 +13,10 @@ class Gem::PathSupport https://github.com/ruby/ruby/blob/trunk/lib/rubygems/path_support.rb#L13 attr_reader :path ## + # Directory with spec cache + attr_reader :spec_cache_dir # :nodoc: + + ## # # Constructor. Takes a single argument which is to be treated like a # hashtable, or defaults to ENV, the system environment. @@ -28,6 +32,12 @@ class Gem::PathSupport https://github.com/ruby/ruby/blob/trunk/lib/rubygems/path_support.rb#L32 end self.path = env["GEM_PATH"] || ENV["GEM_PATH"] + + @spec_cache_dir = + env["GEM_SPEC_CACHE"] || ENV["GEM_SPEC_CACHE"] || + Gem.default_spec_cache_dir + + @spec_cache_dir = @spec_cache_dir.dup.untaint end private Index: lib/rubygems/gemcutter_utilities.rb =================================================================== --- lib/rubygems/gemcutter_utilities.rb (revision 42940) +++ lib/rubygems/gemcutter_utilities.rb (revision 42941) @@ -1,11 +1,17 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/gemcutter_utilities.rb#L1 require 'rubygems/remote_fetcher' +## +# Utility methods for using the RubyGems API. + module Gem::GemcutterUtilities + # TODO: move to Gem::Command OptionParser.accept Symbol do |value| value.to_sym end + attr_writer :host + ## # Add the --key option @@ -17,6 +23,9 @@ module Gem::GemcutterUtilities https://github.com/ruby/ruby/blob/trunk/lib/rubygems/gemcutter_utilities.rb#L23 end end + ## + # The API key from the command options or from the user's configuration. + def api_key if options[:key] then verify_api_key options[:key] @@ -27,35 +36,10 @@ module Gem::GemcutterUtilities https://github.com/ruby/ruby/blob/trunk/lib/rubygems/gemcutter_utilities.rb#L36 end end - def sign_in sign_in_host = self.host - return if Gem.configuration.rubygems_api_key - - pretty_host = if Gem::DEFAULT_HOST == sign_in_host then - 'RubyGems.org' - else - sign_in_host - end - - say "Enter your #{pretty_host} credentials." - say "Don't have an account yet? " + - "Create one at #{sign_in_host}/sign_up" - - email = ask " Email: " - password = ask_for_password "Password: " - say "\n" - - response = rubygems_api_request(:get, "api/v1/api_key", - sign_in_host) do |request| - request.basic_auth email, password - end - - with_response response do |resp| - say "Signed in." - Gem.configuration.rubygems_api_key = resp.body - end - end + ## + # The host to connect to either from the RUBYGEMS_HOST environment variable + # or from the user's configuration - attr_writer :host def host configured_host = Gem.host unless Gem.configuration.disable_default_gem_server @@ -70,6 +54,9 @@ module Gem::GemcutterUtilities https://github.com/ruby/ruby/blob/trunk/lib/rubygems/gemcutter_utilities.rb#L54 end end + ## + # Creates an RubyGems API to +host+ and +path+ with the given HTTP +method+. + def rubygems_api_request(method, path, host = nil, &block) require 'net/http' @@ -86,23 +73,43 @@ module Gem::GemcutterUtilities https://github.com/ruby/ruby/blob/trunk/lib/rubygems/gemcutter_utilities.rb#L73 Gem::RemoteFetcher.fetcher.request(uri, request_method, &block) end - def with_response resp, error_prefix = nil - case resp - when Net::HTTPSuccess then - if block_given? then - yield resp - else - say resp.body - end - else - message = resp.body - message = "#{error_prefix}: #{message}" if error_prefix + ## + # Signs in with the RubyGems API at +sign_in_host+ and sets the rubygems API + # key. - say message - terminate_interaction 1 # TODO: question this + def sign_in sign_in_host = nil + sign_in_host ||= self.host + return if Gem.configuration.rubygems_api_key + + pretty_host = if Gem::DEFAULT_HOST == sign_in_host then + 'RubyGems.org' + else + sign_in_host + end + + say "Enter your #{pretty_host} credentials." + say "Don't have an account yet? " + + "Create one at #{sign_in_host}/sign_up" + + email = ask " Email: " + password = ask_for_password "Password: " + say "\n" + + response = rubygems_api_request(:get, "api/v1/api_key", + sign_in_host) do |request| + request.basic_auth email, password + end + + with_response response do |resp| + say "Signed in." + Gem.configuration.rubygems_api_key = resp.body end end + ## + # Retrieves the pre-configured API key +key+ or terminates interaction with + # an error. + def verify_api_key(key) if Gem.configuration.api_keys.key? key then Gem.configuration.api_keys[key] @@ -112,4 +119,29 @@ module Gem::GemcutterUtilities https://github.com/ruby/ruby/blob/trunk/lib/rubygems/gemcutter_utilities.rb#L119 end end + ## + # If +response+ is an HTTP Success (2XX) response, yields the response if a + # block was given or shows the response body to the user. + # + # If the response was not successful, shows an error to the user including + # the +error_prefix+ and the response body. + + def with_response response, error_prefix = nil + case response + when Net::HTTPSuccess then + if block_given? then + yield response + else + say response.body + end + else + message = response.body + message = "#{error_prefix}: #{message}" if error_prefix + + say message + terminate_interaction 1 # TODO: question this + end + end + end + Index: lib/rubygems/spec_fetcher.rb =================================================================== --- lib/rubygems/spec_fetcher.rb (revision 42940) +++ lib/rubygems/spec_fetcher.rb (revision 42941) @@ -38,7 +38,6 @@ class Gem::SpecFetcher https://github.com/ruby/ruby/blob/trunk/lib/rubygems/spec_fetcher.rb#L38 end def initialize - @dir = File.join Gem.user_home, '.gem', 'specs' @update_cache = File.stat(Gem.user_home).uid == Process.uid @specs = {} @@ -75,6 +74,12 @@ class Gem::SpecFetcher https://github.com/ruby/ruby/blob/trunk/lib/rubygems/spec_fetcher.rb#L74 list, errors = available_specs(type) list.each do |source, specs| + if dependency.name.is_a?(String) && specs.respond_to?(:bsearch) + start_index = (0 ... specs.length).bsearch{ |i| specs[i].name >= dependency.name } + end_index = (0 ... specs.length).bsearch{ |i| specs[i].name > dependency.name } + specs = specs[start_index ... end_index] if start_index && end_index + end + found[source] = specs.select do |tup| if dependency.match?(tup) if matching_platform and !Gem::Platform.match(tup.platform) @@ -195,8 +200,11 @@ class Gem::SpecFetcher https://github.com/ruby/ruby/blob/trunk/lib/rubygems/spec_fetcher.rb#L200 when :released tuples_for source, :released when :complete - tuples_for(source, :prerelease, true) + + names = + tuples_for(source, :prerelease, true) + tuples_for(source, :released) + + names.sort when :prerelease tuples_for(source, :prerelease) else @@ -215,15 +223,15 @@ class Gem::SpecFetcher https://github.com/ruby/ruby/blob/trunk/lib/rubygems/spec_fetcher.rb#L223 def tuples_for(source, type, gracefully_ignore=false) cache = @caches[type] - if gracefully_ignore + tuples = begin cache[source.uri] ||= source.load_specs(type) rescue Gem::RemoteFetcher::FetchError + raise unless gracefully_ignore [] end - else - cache[source.uri] ||= source.load_specs(type) - end + + tuples.sort_by { |tup| tup.name } end end Index: lib/rubygems/available_set.rb =================================================================== --- lib/rubygems/available_set.rb (revision 42940) +++ lib/rubygems/available_set.rb (revision 42941) @@ -1,4 +1,7 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/available_set.rb#L1 class Gem::AvailableSet + + include Enumerable + Tuple = Struct.new(:spec, :source) def initialize @@ -36,6 +39,28 @@ class Gem::AvailableSet https://github.com/ruby/ruby/blob/trunk/lib/rubygems/available_set.rb#L39 self end + ## + # Yields each Tuple in this AvailableSet + + def each + return enum_for __method__ unless block_given? + + @set.each do |tuple| + yield tuple + end + end + + ## + # Yields the Gem::Specification for each Tuple in this AvailableSet + + def each_spec + return enum_for __method__ unless block_given? + + each do |tuple| + yield tuple.spec + end + end + def empty? @set.empty? end @@ -66,6 +91,49 @@ class Gem::AvailableSet https://github.com/ruby/ruby/blob/trunk/lib/rubygems/available_set.rb#L91 f.source end + ## + # Converts this AvailableSet into a RequestSet that can be used to install + # gems. + # + # If +development+ is :none then no development dependencies are installed. + # Other options are :shallow for only direct development dependencies of the + # gems in this set or :all for all development dependencies. + + def to_request_set development = :none + request_set = Gem::RequestSet.new + request_set.development = :all == development + + each_spec do |spec| + request_set.always_install << spec + + request_set.gem spec.name, spec.version + request_set.import spec.development_dependencies if + :shallow == development + end + + request_set + end + + ## + # + # Used by the DependencyResolver, the protocol to use a AvailableSet as a + # search Set. + + def find_all(req) + dep = req.dependency + + match = @set.find_all do |t| + dep.matches_spec? t.spec + end + + match.map do |t| + Gem::DependencyResolver::InstalledSpecification.new(self, t.spec, t.source) + end + end + + def prefetch(reqs) + end + def pick_best! return self if empty? Index: lib/rubygems/dependency_installer.rb =================================================================== --- lib/rubygems/dependency_installer.rb (revision 42940) +++ lib/rubygems/dependency_installer.rb (revision 42941) @@ -1,11 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_installer.rb#L1 require 'rubygems' require 'rubygems/dependency_list' +require 'rubygems/dependency_resolver' require 'rubygems/package' require 'rubygems/installer' require 'rubygems/spec_fetcher' require 'rubygems/user_interaction' -require 'rubygems/source_local' -require 'rubygems/source_specific_file' +require 'rubygems/source' require 'rubygems/available_set' ## @@ -15,15 +15,7 @@ class Gem::DependencyInstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_installer.rb#L15 include Gem::UserInteraction - attr_reader :gems_to_install - attr_reader :installed_gems - - ## - # Documentation types. For use by the Gem.done_installing hook - - attr_reader :document - - DEFAULT_OPTIONS = { + DEFAULT_OPTIONS = { # :nodoc: :env_shebang => false, :document => %w[ri], :domain => :both, # HACK dup @@ -35,9 +27,31 @@ class Gem::DependencyInstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_installer.rb#L27 :wrappers => true, :build_args => nil, :build_docs_in_background => false, + :install_as_default => false }.freeze ## + # Documentation types. For use by the Gem.done_installing hook + + attr_reader :document + + ## + # Errors from SpecFetcher while searching for remote specifications + + attr_reader :errors + + ## + #-- + # TODO remove, no longer used + + attr_reader :gems_to_install # :nodoc: + + ## + # List of gems installed by #install in alphabetic order + + attr_reader :installed_gems + + ## # Creates a new installer instance. # # Options are: @@ -56,7 +70,8 @@ class Gem::DependencyInstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_installer.rb#L70 # :wrappers:: See Gem::Installer::new # :build_args:: See Gem::Installer::new - def initialize(options = {}) + def initialize options = {} + @only_install_dir = !!options[:install_dir] @install_dir = options[:install_dir] || Gem.dir if options[:install_dir] then @@ -82,6 +97,7 @@ class Gem::DependencyInstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_installer.rb#L97 @wrappers = options[:wrappers] @build_args = options[:build_args] @build_docs_in_background = options[:build_docs_in_background] + @install_as_default = options[:install_as_default] # Indicates that we should not try to update any deps unless # we absolutely must. @@ -93,13 +109,61 @@ class Gem::DependencyInstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_installer.rb#L109 @cache_dir = options[:cache_dir] || @install_dir - # Set with any errors that SpecFetcher finds while search through - # gemspecs for a dep @errors = nil end - attr_reader :errors + ## + #-- + # TODO remove, no longer used + + def add_found_dependencies to_do, dependency_list # :nodoc: + seen = {} + dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name } + + until to_do.empty? do + spec = to_do.shift + + # HACK why is spec nil? + next if spec.nil? or seen[spec.name] + seen[spec.name] = true + + deps = spec.runtime_dependencies + + if @development + if @dev_shallow + if @toplevel_specs.include? spec.full_name + deps |= spec.development_dependencies + end + else + deps |= spec.development_dependencies + end + end + + deps.each do |dep| + dependencies[dep.name] = dependencies[dep.name].merge dep + + if @minimal_deps + next if Gem::Specification.any? do |installed_spec| + dep.name == installed_spec.name and + dep.requirement.satisfied_by? installed_spec.version + end + end + + results = find_gems_with_sources(dep) + results.sorted.each do |t| + to_do.push t.spec + end + + results.remove_installed! dep + + @available << results + results.inject_into_list dependency_list + end + end + + dependency_list.remove_specs_unsatisfied_by dependencies + end ## # Creates an AvailableSet to install from based on +dep_or_name+ and # +version+ @@ -138,7 +202,7 @@ class Gem::DependencyInstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_installer.rb#L202 # sources. Gems are sorted with newer gems preferred over older gems, and # local gems preferred over remote gems. - def find_gems_with_sources(dep) + def find_gems_with_sources dep # :nodoc: set = Gem::AvailableSet.new if consider_local? @@ -179,10 +243,50 @@ class Gem::DependencyInstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_installer.rb#L243 end ## + # Finds a spec and the source_uri it came from for gem +gem_name+ and + # +version+. Returns an Array of specs and sources required for + # installation of the gem. + + def find_spec_by_name_and_version gem_name, + version = Gem::Requirement.default, + prerelease = false + set = Gem::AvailableSet.new + + if consider_local? + if gem_name =~ /\.gem$/ and File.file? gem_name then + src = Gem::Source::SpecificFile.new(gem_name) + set.add src.spec, src + else + local = Gem::Source::Local.new + + if s = local.find_gem(gem_name, version) + set.add s, local + end + end + end + + if set.empty? + dep = Gem::Dependency.new gem_name, version + dep.prerelease = true if prerelease + + set = find_gems_with_sources(dep) + set.match_platform! + end + + if set.empty? + raise Gem::SpecificGemNotFoundException.new(gem_name, version, @errors) + end + + @available = set + end + + ## # Gathers all dependencies necessary for the installation from local and # remote sources unless the ignore_dependencies was given. + #-- + # TODO remove, no longer used - def gather_dependencies + def gather_dependencies # :nodoc: specs = @available.all_specs # these gems were listed by the user, always install them @@ -214,93 +318,19 @@ class Gem::DependencyInstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/dependency_installer.rb#L318 @gems_to_install = dependency_list.dependency_order.reverse end - def add_found_dependencies to_do, dependency_list - seen = {} - dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name } - - until to_do.empty? do - spec = to_do.shift - - # HACK why is spec nil? - next if spec.nil? or seen[spec.name] - seen[spec.name] = true - - deps = spec.runtime_dependencies - - if @development - if @dev_shallow - if @toplevel_specs.include? spec.full_name - deps |= spec.development_dependencies - end - else - deps |= spec.development_dependencies - end - end - - deps.each do |dep| - dependencies[dep.name] = dependencies[dep.name].merge dep - - if @minimal_deps - next if Gem::Specification.any? do |installed_spec| - dep.name == installed_spec.name and - dep.requirement.satisfied_by? installed_spec.version - end - end - - results = find_gems_with_sources(dep) - - results.sorted.each do |t| - to_do.push t.spec - end - - results.remove_installed! dep - - @available << results - results.inject_into_list dependency_list - end - end - - dependency_list.remove_specs_unsatisfied_by dependencies - end - - ## - # Finds a spec and the source_uri it came from for gem +gem_name+ and - # +version+. Returns an Array of specs and sources required for - # (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/