diff options
Diffstat (limited to 'lib/rubygems')
55 files changed, 1198 insertions, 694 deletions
diff --git a/lib/rubygems/builder.rb b/lib/rubygems/builder.rb index 3061b752ce..9e7f262243 100644 --- a/lib/rubygems/builder.rb +++ b/lib/rubygems/builder.rb @@ -11,6 +11,7 @@ class Gem::Builder include Gem::UserInteraction + ## # Constructs a builder instance for the provided specification # @@ -33,7 +34,7 @@ class Gem::Builder @spec.validate @signer = sign write_package - say success + say success if Gem.configuration.verbose @spec.file_name end @@ -42,7 +43,7 @@ class Gem::Builder Successfully built RubyGem Name: #{@spec.name} Version: #{@spec.version} - File: #{@spec.full_name+'.gem'} + File: #{@spec.file_name} EOM end @@ -86,5 +87,6 @@ EOM end end end + end diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb index dab4366270..08692bdb70 100644 --- a/lib/rubygems/command.rb +++ b/lib/rubygems/command.rb @@ -160,8 +160,8 @@ class Gem::Command end ## - # Get the single gem name from the command line. Fail if there is no gem - # name or if there is more than one gem name given. + # Get a single gem name from the command line. Fail if there is no gem name + # or if there is more than one gem name given. def get_one_gem_name args = options[:args] @@ -248,11 +248,12 @@ class Gem::Command # Invoke the command with the given list of arguments. def invoke(*args) - handle_options(args) - if options[:help] + handle_options args + + if options[:help] then show_help - elsif @when_invoked - @when_invoked.call(options) + elsif @when_invoked then + @when_invoked.call options else execute end @@ -362,37 +363,39 @@ class Gem::Command def create_option_parser @parser = OptionParser.new - @parser.separator("") + @parser.separator nil regular_options = @option_groups.delete :options configure_options "", regular_options @option_groups.sort_by { |n,_| n.to_s }.each do |group_name, option_list| + @parser.separator nil configure_options group_name, option_list end + @parser.separator nil configure_options "Common", Gem::Command.common_options - @parser.separator("") unless arguments.empty? - @parser.separator(" Arguments:") + @parser.separator nil + @parser.separator " Arguments:" arguments.split(/\n/).each do |arg_desc| - @parser.separator(" #{arg_desc}") + @parser.separator " #{arg_desc}" end - @parser.separator("") end - @parser.separator(" Summary:") + @parser.separator nil + @parser.separator " Summary:" wrap(@summary, 80 - 4).split("\n").each do |line| - @parser.separator(" #{line.strip}") + @parser.separator " #{line.strip}" end if description then formatted = description.split("\n\n").map do |chunk| - wrap(chunk, 80 - 4) - end.join("\n") + wrap chunk, 80 - 4 + end.join "\n" - @parser.separator "" + @parser.separator nil @parser.separator " Description:" formatted.split("\n").each do |line| @parser.separator " #{line.rstrip}" @@ -400,10 +403,10 @@ class Gem::Command end unless defaults_str.empty? - @parser.separator("") - @parser.separator(" Defaults:") + @parser.separator nil + @parser.separator " Defaults:" defaults_str.split(/\n/).each do |line| - @parser.separator(" #{line}") + @parser.separator " #{line}" end end end @@ -471,33 +474,33 @@ class Gem::Command # :stopdoc: - HELP = %{ - RubyGems is a sophisticated package manager for Ruby. This is a - basic help message containing pointers to more information. - - Usage: - gem -h/--help - gem -v/--version - gem command [arguments...] [options...] - - Examples: - gem install rake - gem list --local - gem build package.gemspec - gem help install - - Further help: - gem help commands list all 'gem' commands - gem help examples show some examples of usage - gem help platforms show information about platforms - gem help <COMMAND> show help on COMMAND - (e.g. 'gem help install') - gem server present a web page at - http://localhost:8808/ - with info about installed gems - Further information: - http://rubygems.rubyforge.org - }.gsub(/^ /, '') + HELP = <<-HELP +RubyGems is a sophisticated package manager for Ruby. This is a +basic help message containing pointers to more information. + + Usage: + gem -h/--help + gem -v/--version + gem command [arguments...] [options...] + + Examples: + gem install rake + gem list --local + gem build package.gemspec + gem help install + + Further help: + gem help commands list all 'gem' commands + gem help examples show some examples of usage + gem help platforms show information about platforms + gem help <COMMAND> show help on COMMAND + (e.g. 'gem help install') + gem server present a web page at + http://localhost:8808/ + with info about installed gems + Further information: + http://rubygems.rubyforge.org + HELP # :startdoc: diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb index cd1fd6b568..11344f1fb6 100644 --- a/lib/rubygems/command_manager.rb +++ b/lib/rubygems/command_manager.rb @@ -58,7 +58,9 @@ class Gem::CommandManager register_command :lock register_command :mirror register_command :outdated + register_command :owner register_command :pristine + register_command :push register_command :query register_command :rdoc register_command :search diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb index 44b269bb11..61e3b59fd7 100644 --- a/lib/rubygems/commands/dependency_command.rb +++ b/lib/rubygems/commands/dependency_command.rb @@ -15,6 +15,7 @@ class Gem::Commands::DependencyCommand < Gem::Command add_version_option add_platform_option + add_prerelease_option add_option('-R', '--[no-]reverse-dependencies', 'Include reverse dependencies in the output') do @@ -59,6 +60,7 @@ class Gem::Commands::DependencyCommand < Gem::Command end dependency = Gem::Dependency.new pattern, options[:version] + dependency.prerelease = options[:prerelease] if options[:reverse_dependencies] and remote? and not local? then alert_error 'Only reverse dependencies for local gems are supported.' @@ -75,7 +77,10 @@ class Gem::Commands::DependencyCommand < Gem::Command fetcher = Gem::SpecFetcher.fetcher begin - fetcher.find_matching(dependency).each do |spec_tuple, source_uri| + specs_and_sources = fetcher.find_matching(dependency, false, true, + dependency.prerelease?) + + specs_and_sources.each do |spec_tuple, source_uri| spec = fetcher.fetch_spec spec_tuple, URI.parse(source_uri) source_indexes[source_uri].add_spec spec @@ -120,8 +125,8 @@ class Gem::Commands::DependencyCommand < Gem::Command if options[:pipe_format] then specs.values.sort_by { |_, spec| spec }.each do |_, spec| unless spec.dependencies.empty? - spec.dependencies.each do |dep| - say "#{dep.name} --version '#{dep.version_requirements}'" + spec.dependencies.sort_by { |dep| dep.name }.each do |dep| + say "#{dep.name} --version '#{dep.requirement}'" end end end @@ -147,7 +152,7 @@ class Gem::Commands::DependencyCommand < Gem::Command response = '' response << ' ' * level + "Gem #{spec.full_name}\n" unless spec.dependencies.empty? then - spec.dependencies.each do |dep| + spec.dependencies.sort_by { |dep| dep.name }.each do |dep| response << ' ' * level + " #{dep}\n" end end @@ -163,7 +168,7 @@ class Gem::Commands::DependencyCommand < Gem::Command dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep if spec.name == dep.name and - dep.version_requirements.satisfied_by?(spec.version) then + dep.requirement.satisfied_by?(spec.version) then result << [sp.full_name, dep] end end diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb index e672da54f0..f3550cae28 100644 --- a/lib/rubygems/commands/environment_command.rb +++ b/lib/rubygems/commands/environment_command.rb @@ -118,7 +118,7 @@ lib/rubygems/defaults/operating_system.rb end else - fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]" + raise Gem::CommandLineError, "Unknown enviroment option [#{arg}]" end say out true diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb index 76c9924e6b..b12e1b4a5d 100644 --- a/lib/rubygems/commands/fetch_command.rb +++ b/lib/rubygems/commands/fetch_command.rb @@ -17,6 +17,7 @@ class Gem::Commands::FetchCommand < Gem::Command add_version_option add_platform_option + add_prerelease_option end def arguments # :nodoc: @@ -39,12 +40,12 @@ class Gem::Commands::FetchCommand < Gem::Command gem_names.each do |gem_name| dep = Gem::Dependency.new gem_name, version + dep.prerelease = options[:prerelease] - specs_and_sources = Gem::SpecFetcher.fetcher.fetch dep, all + specs_and_sources = Gem::SpecFetcher.fetcher.fetch(dep, false, true, + dep.prerelease?) - specs_and_sources.sort_by { |spec,| spec.version } - - spec, source_uri = specs_and_sources.last + spec, source_uri = specs_and_sources.sort_by { |s,| s.version }.last if spec.nil? then alert_error "Could not find #{gem_name} in any repository" @@ -52,7 +53,7 @@ class Gem::Commands::FetchCommand < Gem::Command end path = Gem::RemoteFetcher.fetcher.download spec, source_uri - FileUtils.mv path, "#{spec.full_name}.gem" + FileUtils.mv path, spec.file_name say "Downloaded #{spec.full_name}" end diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb index af2d8bd739..df6b3e5f2c 100644 --- a/lib/rubygems/commands/install_command.rb +++ b/lib/rubygems/commands/install_command.rb @@ -32,6 +32,7 @@ class Gem::Commands::InstallCommand < Gem::Command add_local_remote_options add_platform_option add_version_option + add_prerelease_option "to be installed. (Only for listed gems)" end def arguments # :nodoc: diff --git a/lib/rubygems/commands/mirror_command.rb b/lib/rubygems/commands/mirror_command.rb index 959b8eaec3..7cb8583326 100644 --- a/lib/rubygems/commands/mirror_command.rb +++ b/lib/rubygems/commands/mirror_command.rb @@ -51,7 +51,7 @@ Multiple sources and destinations may be specified. Dir.mkdir gems_dir end - sourceindex_data = '' + source_index_data = '' say "fetching: #{get_from}/Marshal.#{Gem.marshal_version}.Z" @@ -70,18 +70,18 @@ Multiple sources and destinations may be specified. end open File.join(get_from.to_s, "Marshal.#{Gem.marshal_version}.Z"), "rb" do |y| - sourceindex_data = Zlib::Inflate.inflate y.read + source_index_data = Zlib::Inflate.inflate y.read open File.join(save_to, "Marshal.#{Gem.marshal_version}"), "wb" do |out| - out.write sourceindex_data + out.write source_index_data end end - sourceindex = Marshal.load(sourceindex_data) + source_index = Marshal.load source_index_data - progress = ui.progress_reporter sourceindex.size, - "Fetching #{sourceindex.size} gems" - sourceindex.each do |fullname, gem| - gem_file = "#{fullname}.gem" + progress = ui.progress_reporter source_index.size, + "Fetching #{source_index.size} gems" + source_index.each do |fullname, gem| + gem_file = gem.file_name gem_dest = File.join gems_dir, gem_file unless File.exist? gem_dest then diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb new file mode 100644 index 0000000000..e88734e8fb --- /dev/null +++ b/lib/rubygems/commands/owner_command.rb @@ -0,0 +1,75 @@ +require 'rubygems/command' +require 'rubygems/local_remote_options' +require 'rubygems/gemcutter_utilities' + +class Gem::Commands::OwnerCommand < Gem::Command + include Gem::LocalRemoteOptions + include Gem::GemcutterUtilities + + def description # :nodoc: + 'Manage gem owners on RubyGems.org.' + end + + def arguments # :nodoc: + "GEM gem to manage owners for" + end + + def initialize + super 'owner', description + add_proxy_option + defaults.merge! :add => [], :remove => [] + + add_option '-a', '--add EMAIL', 'Add an owner' do |value, options| + options[:add] << value + end + + add_option '-r', '--remove EMAIL', 'Remove an owner' do |value, options| + options[:remove] << value + end + end + + def execute + sign_in + name = get_one_gem_name + + add_owners name, options[:add] + remove_owners name, options[:remove] + show_owners name + end + + def show_owners name + response = rubygems_api_request :get, "api/v1/gems/#{name}/owners.yaml" do |request| + request.add_field "Authorization", Gem.configuration.rubygems_api_key + end + + with_response response do |resp| + owners = YAML.load resp.body + + say "Owners for gem: #{name}" + owners.each do |owner| + say "- #{owner['email']}" + end + end + end + + def add_owners name, owners + manage_owners :post, name, owners + end + + def remove_owners name, owners + manage_owners :delete, name, owners + end + + def manage_owners method, name, owners + owners.each do |owner| + response = rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request| + request.set_form_data 'email' => owner + request.add_field "Authorization", Gem.configuration.rubygems_api_key + end + + with_response response + end + end + +end + diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb index d47fe54edd..ef11129d2c 100644 --- a/lib/rubygems/commands/pristine_command.rb +++ b/lib/rubygems/commands/pristine_command.rb @@ -74,7 +74,7 @@ revert the gem. say "Restoring gem(s) to pristine condition..." specs.each do |spec| - gem = Dir[File.join(Gem.dir, 'cache', "#{spec.full_name}.gem")].first + gem = Dir[File.join(Gem.dir, 'cache', spec.file_name)].first if gem.nil? then alert_error "Cached gem for #{spec.full_name} not found, use `gem install` to restore" diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb new file mode 100644 index 0000000000..cabcd3d94d --- /dev/null +++ b/lib/rubygems/commands/push_command.rb @@ -0,0 +1,45 @@ +require 'rubygems/command' +require 'rubygems/local_remote_options' +require 'rubygems/gemcutter_utilities' + +class Gem::Commands::PushCommand < Gem::Command + include Gem::LocalRemoteOptions + include Gem::GemcutterUtilities + + def description # :nodoc: + 'Push a gem up to RubyGems.org' + end + + def arguments # :nodoc: + "GEM built gem to push up" + end + + def usage # :nodoc: + "#{program_name} GEM" + end + + def initialize + super 'push', description + add_proxy_option + end + + def execute + sign_in + send_gem get_one_gem_name + end + + def send_gem name + say "Pushing gem to RubyGems.org..." + + response = rubygems_api_request :post, "api/v1/gems" do |request| + request.body = Gem.read_binary name + request.add_field "Content-Length", request.body.size + request.add_field "Content-Type", "application/octet-stream" + request.add_field "Authorization", Gem.configuration.rubygems_api_key + end + + with_response response + end + +end + diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb index 88f7f0b54e..7dd0a4a0d6 100644 --- a/lib/rubygems/commands/query_command.rb +++ b/lib/rubygems/commands/query_command.rb @@ -45,7 +45,7 @@ class Gem::Commands::QueryCommand < Gem::Command options[:all] = value end - add_option( '--prerelease', + add_option( '--[no-]prerelease', 'Display prerelease versions') do |value, options| options[:prerelease] = value end @@ -111,6 +111,9 @@ class Gem::Commands::QueryCommand < Gem::Command begin fetcher = Gem::SpecFetcher.fetcher spec_tuples = fetcher.find_matching dep, all, false, prerelease + + spec_tuples += fetcher.find_matching dep, false, false, true if + prerelease and all rescue Gem::RemoteFetcher::FetchError => e if prerelease then raise Gem::OperationNotSupportedError, diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb index 92f215705a..ea0f3ad592 100644 --- a/lib/rubygems/commands/rdoc_command.rb +++ b/lib/rubygems/commands/rdoc_command.rb @@ -8,7 +8,7 @@ class Gem::Commands::RdocCommand < Gem::Command def initialize super 'rdoc', 'Generates RDoc for pre-installed gems', :version => Gem::Requirement.default, - :include_rdoc => true, :include_ri => true + :include_rdoc => true, :include_ri => true, :overwrite => false add_option('--all', 'Generate RDoc/RI documentation for all', @@ -17,15 +17,20 @@ class Gem::Commands::RdocCommand < Gem::Command end add_option('--[no-]rdoc', - 'Include RDoc generated documents') do |value, options| + 'Generate RDoc HTML') do |value, options| options[:include_rdoc] = value end add_option('--[no-]ri', - 'Include RI generated documents') do |value, options| + 'Generate RI data') do |value, options| options[:include_ri] = value end + add_option('--[no-]overwrite', + 'Overwrite installed documents') do |value, options| + options[:overwrite] = value + end + add_version_option end @@ -34,7 +39,14 @@ class Gem::Commands::RdocCommand < Gem::Command end def defaults_str # :nodoc: - "--version '#{Gem::Requirement.default}' --rdoc --ri" + "--version '#{Gem::Requirement.default}' --rdoc --ri --no-overwrite" + end + + def description # :nodoc: + <<-DESC +The rdoc command builds RDoc and RI documentation for installed gems. Use +--overwrite to force rebuilding of documentation. + DESC end def usage # :nodoc: @@ -53,24 +65,27 @@ class Gem::Commands::RdocCommand < Gem::Command end if specs.empty? - fail "Failed to find gem #{gem_name} to generate RDoc for #{options[:version]}" + raise "Failed to find gem #{gem_name} to generate RDoc for #{options[:version]}" end if options[:include_ri] - specs.each do |spec| - Gem::DocManager.new(spec).generate_ri + specs.sort.each do |spec| + doc = Gem::DocManager.new(spec) + doc.generate_ri if options[:overwrite] || !doc.ri_installed? end Gem::DocManager.update_ri_cache end if options[:include_rdoc] - specs.each do |spec| - Gem::DocManager.new(spec).generate_rdoc + specs.sort.each do |spec| + doc = Gem::DocManager.new(spec) + doc.generate_rdoc if options[:overwrite] || !doc.rdoc_installed? end end true end + end diff --git a/lib/rubygems/commands/server_command.rb b/lib/rubygems/commands/server_command.rb index f8b990d304..6bc58367e9 100644 --- a/lib/rubygems/commands/server_command.rb +++ b/lib/rubygems/commands/server_command.rb @@ -36,6 +36,12 @@ class Gem::Commands::ServerCommand < Gem::Command add_option '--[no-]daemon', 'run as a daemon' do |daemon, options| options[:daemon] = daemon end + + add_option '-b', '--bind=HOST,HOST', + 'addresses to bind', Array do |address, options| + options[:addresses] ||= [] + options[:addresses].push(*address) + end end def defaults_str # :nodoc: diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index 5bd5ebd468..f5f5185f86 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -2,7 +2,6 @@ require 'rubygems/command' require 'fileutils' require 'rbconfig' require 'tmpdir' -require 'pathname' ## # Installs RubyGems itself. This command is ordinarily only available from a @@ -117,6 +116,8 @@ By default, this RubyGems will install gem as: say "RubyGems #{Gem::VERSION} installed" + uninstall_old_gemcutter + install_rdoc say @@ -359,5 +360,14 @@ abort "#{deprecation_message}" r.document args end + def uninstall_old_gemcutter + require 'rubygems/uninstaller' + + ui = Gem::Uninstaller.new('gemcutter', :all => true, :ignore => true, + :version => '< 0.4') + ui.uninstall + rescue Gem::InstallError + end + end diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb index 075dc04203..84a53b107a 100644 --- a/lib/rubygems/commands/unpack_command.rb +++ b/lib/rubygems/commands/unpack_command.rb @@ -41,7 +41,7 @@ class Gem::Commands::UnpackCommand < Gem::Command path = get_path name, options[:version] if path then - basename = File.basename(path).sub(/\.gem$/, '') + basename = File.basename(path, '.gem') target_dir = File.expand_path File.join(options[:target], basename) FileUtils.mkdir_p target_dir Gem::Installer.new(path, :unpack => true).unpack target_dir @@ -79,7 +79,7 @@ class Gem::Commands::UnpackCommand < Gem::Command # We expect to find (basename).gem in the 'cache' directory. # Furthermore, the name match must be exact (ignoring case). if gemname =~ /^#{selected.name}$/i - filename = selected.full_name + '.gem' + filename = selected.file_name path = nil Gem.path.find do |gem_dir| diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb index 9974b2b54b..4b0439df1f 100644 --- a/lib/rubygems/commands/update_command.rb +++ b/lib/rubygems/commands/update_command.rb @@ -28,8 +28,8 @@ class Gem::Commands::UpdateCommand < Gem::Command end add_local_remote_options - add_platform_option + add_prerelease_option "as update targets" end def arguments # :nodoc: @@ -51,7 +51,7 @@ class Gem::Commands::UpdateCommand < Gem::Command say "Updating RubyGems" unless options[:args].empty? then - fail "No gem names are allowed with the --system option" + raise "No gem names are allowed with the --system option" end rubygems_update = Gem::Specification.new diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb index 7c2d188324..b785f15660 100644 --- a/lib/rubygems/commands/which_command.rb +++ b/lib/rubygems/commands/which_command.rb @@ -27,13 +27,11 @@ class Gem::Commands::WhichCommand < Gem::Command "--no-gems-first --no-all" end - def usage # :nodoc: - "#{program_name} FILE [FILE ...]" - end - def execute searcher = Gem::GemPathSearcher.new + found = false + options[:args].each do |arg| dirs = $LOAD_PATH spec = searcher.find arg @@ -44,19 +42,19 @@ class Gem::Commands::WhichCommand < Gem::Command else dirs = $LOAD_PATH + gem_paths(spec) end - - say "(checking gem #{spec.full_name} for #{arg})" if - Gem.configuration.verbose and $stdout.tty? end paths = find_paths arg, dirs if paths.empty? then - say "Can't find ruby library file or shared library #{arg}" + alert_error "Can't find ruby library file or shared library #{arg}" else say paths + found = true end end + + terminate_interaction 1 unless found end def find_paths(package_name, dirs) @@ -80,7 +78,7 @@ class Gem::Commands::WhichCommand < Gem::Command end def usage # :nodoc: - "#{program_name} FILE [...]" + "#{program_name} FILE [FILE ...]" end end diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb index 3562805c62..3498d06540 100644 --- a/lib/rubygems/config_file.rb +++ b/lib/rubygems/config_file.rb @@ -4,10 +4,26 @@ # See LICENSE.txt for permissions. #++ -require 'yaml' - -# Store the gem command options specified in the configuration file. The -# config file object acts much like a hash. +## +# Gem::ConfigFile RubyGems options and gem command options from ~/.gemrc. +# +# ~/.gemrc is a YAML file that uses strings to match gem command arguments and +# symbols to match RubyGems options. +# +# Gem command arguments use a String key that matches the command name and +# allow you to specify default arguments: +# +# install: --no-rdoc --no-ri +# update: --no-rdoc --no-ri +# +# You can use <tt>gem:</tt> to set default arguments for all commands. +# +# RubyGems options use symbol keys. Valid options are: +# +# +:backtrace+:: See #backtrace +# +:benchmark+:: See #benchmark +# +:sources+:: Sets Gem::sources +# +:verbose+:: See #verbose class Gem::ConfigFile @@ -46,49 +62,74 @@ class Gem::ConfigFile SYSTEM_WIDE_CONFIG_FILE = File.join system_config_path, 'gemrc' + ## # List of arguments supplied to the config file object. + attr_reader :args - # Where to look for gems + ## + # Where to look for gems (deprecated) + attr_accessor :path + ## + # Where to install gems (deprecated) + attr_accessor :home + ## # True if we print backtraces on errors. + attr_writer :backtrace + ## # True if we are benchmarking this run. + attr_accessor :benchmark - # Bulk threshold value. If the number of missing gems are above - # this threshold value, then a bulk download technique is used. + ## + # Bulk threshold value. If the number of missing gems are above this + # threshold value, then a bulk download technique is used. (deprecated) + attr_accessor :bulk_threshold + ## # Verbose level of output: # * false -- No output # * true -- Normal output # * :loud -- Extra output + attr_accessor :verbose + ## # True if we want to update the SourceInfoCache every time, false otherwise + attr_accessor :update_sources + ## + # API key for RubyGems.org + + attr_reader :rubygems_api_key + + ## # Create the config file object. +args+ is the list of arguments # from the command line. # # The following command line options are handled early here rather # than later at the time most command options are processed. # - # * --config-file and --config-file==NAME -- Obviously these need - # to be handled by the ConfigFile object to ensure we get the - # right config file. - # - # * --backtrace -- Backtrace needs to be turned on early so that - # errors before normal option parsing can be properly handled. + # <tt>--config-file</tt>, <tt>--config-file==NAME</tt>:: + # Obviously these need to be handled by the ConfigFile object to ensure we + # get the right config file. # - # * --debug -- Enable Ruby level debug messages. Handled early - # for the same reason as --backtrace. + # <tt>--backtrace</tt>:: + # Backtrace needs to be turned on early so that errors before normal + # option parsing can be properly handled. # + # <tt>--debug</tt>:: + # Enable Ruby level debug messages. Handled early for the same reason as + # --backtrace. + def initialize(arg_list) @config_file_name = nil need_config_file_name = false @@ -125,21 +166,53 @@ class Gem::ConfigFile @hash = @hash.merge user_config # HACK these override command-line args, which is bad - @backtrace = @hash[:backtrace] if @hash.key? :backtrace - @benchmark = @hash[:benchmark] if @hash.key? :benchmark - @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold - Gem.sources = @hash[:sources] if @hash.key? :sources - @verbose = @hash[:verbose] if @hash.key? :verbose - @update_sources = @hash[:update_sources] if @hash.key? :update_sources - @path = @hash[:gempath] if @hash.key? :gempath - @home = @hash[:gemhome] if @hash.key? :gemhome + @backtrace = @hash[:backtrace] if @hash.key? :backtrace + @benchmark = @hash[:benchmark] if @hash.key? :benchmark + @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold + @home = @hash[:gemhome] if @hash.key? :gemhome + @path = @hash[:gempath] if @hash.key? :gempath + @update_sources = @hash[:update_sources] if @hash.key? :update_sources + @verbose = @hash[:verbose] if @hash.key? :verbose + + load_rubygems_api_key + Gem.sources = @hash[:sources] if @hash.key? :sources handle_arguments arg_list end + ## + # Location of RubyGems.org credentials + + def credentials_path + File.join(Gem.user_home, '.gem', 'credentials') + end + + def load_rubygems_api_key + api_key_hash = File.exists?(credentials_path) ? load_file(credentials_path) : @hash + + @rubygems_api_key = api_key_hash[:rubygems_api_key] if api_key_hash.key? :rubygems_api_key + end + + def rubygems_api_key=(api_key) + config = load_file(credentials_path).merge(:rubygems_api_key => api_key) + + dirname = File.dirname(credentials_path) + Dir.mkdir(dirname) unless File.exists?(dirname) + + require 'yaml' + + File.open(credentials_path, 'w') do |f| + f.write config.to_yaml + end + + @rubygems_api_key = api_key + end + def load_file(filename) + return {} unless filename and File.exists?(filename) begin - YAML.load(File.read(filename)) if filename and File.exist?(filename) + require 'yaml' + YAML.load(File.read(filename)) rescue ArgumentError warn "Failed to load #{config_file_name}" rescue Errno::EACCES @@ -233,8 +306,9 @@ class Gem::ConfigFile # Writes out this config file, replacing its source. def write - File.open config_file_name, 'w' do |fp| - fp.write self.to_yaml + require 'yaml' + open config_file_name, 'w' do |io| + io.write to_yaml end end diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb index c9680104c7..c3c4737892 100644 --- a/lib/rubygems/defaults.rb +++ b/lib/rubygems/defaults.rb @@ -9,7 +9,7 @@ module Gem # An Array of the default sources that come with RubyGems def self.default_sources - %w[http://gems.rubyforge.org/] + %w[http://rubygems.org/] end ## diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index d142265c5d..351991067d 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -1,14 +1,22 @@ -#-- -# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. -# All rights reserved. -# See LICENSE.txt for permissions. -#++ +require "rubygems/requirement" ## -# The Dependency class holds a Gem name and a Gem::Requirement +# The Dependency class holds a Gem name and a Gem::Requirement. class Gem::Dependency + # :stopdoc: + @warned_version_requirement = false + + def self.warned_version_requirement + @warned_version_requirement + end + + def self.warned_version_requirement= value + @warned_version_requirement = value + end + # :startdoc: + ## # Valid dependency types. #-- @@ -16,9 +24,9 @@ class Gem::Dependency # Gem::Specification::CURRENT_SPECIFICATION_VERSION as well. TYPES = [ - :development, - :runtime, - ] + :development, + :runtime, + ] ## # Dependency name or regular expression. @@ -26,122 +34,172 @@ class Gem::Dependency attr_accessor :name ## - # Dependency type. + # Allows you to force this dependency to be a prerelease. - attr_reader :type + attr_writer :prerelease ## - # Dependent versions. - - attr_writer :version_requirements - - ## - # Orders dependencies by name only. + # Dependency type. - def <=>(other) - [@name] <=> [other.name] - end + attr_reader :type ## - # Constructs a dependency with +name+ and +requirements+. + # Constructs a dependency with +name+ and +requirements+. The last + # argument can optionally be the dependency type, which defaults to + # <tt>:runtime</tt>. - def initialize(name, version_requirements, type=:runtime) - @name = name + def initialize name, *requirements + type = Symbol === requirements.last ? requirements.pop : :runtime + requirements = requirements.first if 1 == requirements.length # unpack unless TYPES.include? type - raise ArgumentError, "Valid types are #{TYPES.inspect}, not #{@type.inspect}" + raise ArgumentError, "Valid types are #{TYPES.inspect}, " + + "not #{@type.inspect}" end - @type = type + @name = name + @requirement = Gem::Requirement.create requirements + @type = type + @prerelease = false - @version_requirements = Gem::Requirement.create version_requirements - @version_requirement = nil # Avoid warnings. - end + # This is for Marshal backwards compatability. See the comments in + # +requirement+ for the dirty details. - def version_requirements - normalize if defined? @version_requirement and @version_requirement - @version_requirements + @version_requirements = @requirement end - def requirement_list - version_requirements.as_list - end + ## + # What does this dependency require? - alias requirements_list requirement_list + ## + # A dependency's hash is the XOR of the hashes of +name+, +type+, + # and +requirement+. - def normalize - ver = @version_requirement.instance_variable_get :@version - @version_requirements = Gem::Requirement.new([ver]) - @version_requirement = nil + def hash # :nodoc: + name.hash ^ type.hash ^ requirement.hash end - def to_s # :nodoc: - "#{name} (#{version_requirements}, #{@type || :runtime})" + def inspect # :nodoc: + "<%s type=%p name=%p requirements=%p>" % + [self.class, @type, @name, requirement.to_s] + end + + ## + # Does this dependency require a prerelease? + + def prerelease? + @prerelease || requirement.prerelease? end def pretty_print(q) # :nodoc: q.group 1, 'Gem::Dependency.new(', ')' do - q.pp @name + q.pp name q.text ',' q.breakable - q.pp @version_requirements + q.pp requirement q.text ',' q.breakable - q.pp @type + q.pp type end end - def ==(other) # :nodoc: - self.class === other && - self.name == other.name && - self.type == other.type && - self.version_requirements == other.version_requirements + def requirement + return @requirement if defined?(@requirement) and @requirement + + # @version_requirements and @version_requirement are legacy ivar + # names, and supported here because older gems need to keep + # working and Dependency doesn't implement marshal_dump and + # marshal_load. In a happier world, this would be an + # attr_accessor. The horrifying instance_variable_get you see + # below is also the legacy of some old restructurings. + # + # Note also that because of backwards compatibility (loading new + # gems in an old RubyGems installation), we can't add explicit + # marshaling to this class until we want to make a big + # break. Maybe 2.0. + # + # Children, define explicit marshal and unmarshal behavior for + # public classes. Marshal formats are part of your public API. + + if defined?(@version_requirement) && @version_requirement + version = @version_requirement.instance_variable_get :@version + @version_requirement = nil + @version_requirements = Gem::Requirement.new version + end + + @requirement = @version_requirements if defined?(@version_requirements) end ## - # Uses this dependency as a pattern to compare to +other+. This dependency - # will match if the name matches the other's name, and other has only an - # equal version requirement that satisfies this dependency. + # Rails subclasses Gem::Dependency and uses this method, so we'll hack + # around it. - def =~(other) - other = if self.class === other then - other - else - return false unless other.respond_to? :name and - other.respond_to? :version + alias __requirement requirement # :nodoc: - Gem::Dependency.new other.name, other.version - end + def requirements_list + requirement.as_list + end - pattern = @name - pattern = /\A#{Regexp.escape @name}\Z/ unless Regexp === pattern + def to_s # :nodoc: + "#{name} (#{requirement}, #{type})" + end - return false unless pattern =~ other.name + def version_requirements # :nodoc: + unless Gem::Dependency.warned_version_requirement then + warn "#{Gem.location_of_caller.join ':'}:Warning: " \ + "Gem::Dependency#version_requirements is deprecated " \ + "and will be removed on or after August 2010. " \ + "Use #requirement" - reqs = other.version_requirements.requirements + Gem::Dependency.warned_version_requirement = true + end - return false unless reqs.length == 1 - return false unless reqs.first.first == '=' + __requirement + end - version = reqs.first.last + alias_method :version_requirement, :version_requirements - version_requirements.satisfied_by? version + def == other # :nodoc: + Gem::Dependency === other && + self.name == other.name && + self.type == other.type && + self.requirement == other.requirement end ## - # A dependency's hash is the sum of the hash of the #name, #type and - # #version_requirements + # Dependencies are ordered by name. - def hash - name.hash + type.hash + version_requirements.hash + def <=> other + [@name] <=> [other.name] end - def inspect # :nodoc: - "<%s type=%p name=%p requirements=%p>" % [self.class, @type, @name, - version_requirements.to_s] + ## + # Uses this dependency as a pattern to compare to +other+. This + # dependency will match if the name matches the other's name, and + # other has only an equal version requirement that satisfies this + # dependency. + + def =~ other + unless Gem::Dependency === other + other = Gem::Dependency.new other.name, other.version rescue return false + end + + pattern = name + pattern = /\A#{Regexp.escape pattern}\Z/ unless Regexp === pattern + + return false unless pattern =~ other.name + + reqs = other.requirement.requirements + + return false unless reqs.length == 1 + return false unless reqs.first.first == '=' + + version = reqs.first.last + + requirement.satisfied_by? version end end diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb index beea212818..9d9aaba400 100644 --- a/lib/rubygems/dependency_installer.rb +++ b/lib/rubygems/dependency_installer.rb @@ -38,7 +38,7 @@ class Gem::DependencyInstaller # :format_executable:: See Gem::Installer#initialize. # :ignore_dependencies:: Don't install any dependencies. # :install_dir:: See Gem::Installer#install. - # :prerelease:: Allow prerelease versions + # :prerelease:: Allow prerelease versions. See #install. # :security_policy:: See Gem::Installer::new and Gem::Security. # :user_install:: See Gem::Installer.new # :wrappers:: See Gem::Installer::new @@ -89,14 +89,18 @@ class Gem::DependencyInstaller if @domain == :both or @domain == :remote then begin - requirements = dep.version_requirements.requirements.map do |req, ver| + requirements = dep.requirement.requirements.map do |req, ver| req end - all = !@prerelease && (requirements.length > 1 || + all = !dep.prerelease? && + # we only need latest if there's one requirement and it is + # guaranteed to match the newest specs + (requirements.length > 1 or (requirements.first != ">=" and requirements.first != ">")) - found = Gem::SpecFetcher.fetcher.fetch dep, all, true, @prerelease + found = Gem::SpecFetcher.fetcher.fetch dep, all, true, dep.prerelease? + gems_and_sources.push(*found) rescue Gem::RemoteFetcher::FetchError => e @@ -120,7 +124,7 @@ class Gem::DependencyInstaller def gather_dependencies specs = @specs_and_sources.map { |spec,_| spec } - dependency_list = Gem::DependencyList.new + dependency_list = Gem::DependencyList.new @development dependency_list.add(*specs) unless @ignore_dependencies then @@ -143,7 +147,7 @@ class Gem::DependencyInstaller @source_index.any? do |_, installed_spec| dep.name == installed_spec.name and - dep.version_requirements.satisfied_by? installed_spec.version + dep.requirement.satisfied_by? installed_spec.version end end @@ -164,7 +168,9 @@ class Gem::DependencyInstaller # +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 + def find_spec_by_name_and_version(gem_name, + version = Gem::Requirement.default, + prerelease = false) spec_and_source = nil glob = if File::ALT_SEPARATOR then @@ -189,6 +195,7 @@ class Gem::DependencyInstaller if spec_and_source.nil? then dep = Gem::Dependency.new gem_name, version + dep.prerelease = true if prerelease spec_and_sources = find_gems_with_sources(dep).reverse spec_and_source = spec_and_sources.find { |spec, source| @@ -205,13 +212,24 @@ class Gem::DependencyInstaller end ## - # Installs the gem and all its dependencies. Returns an Array of installed - # gems specifications. + # Installs the gem +dep_or_name+ and all its dependencies. Returns an Array + # of installed gem specifications. + # + # If the +:prerelease+ option is set and there is a prerelease for + # +dep_or_name+ the prerelease version will be installed. + # + # Unless explicitly specified as a prerelease dependency, prerelease gems + # that +dep_or_name+ depend on will not be installed. + # + # If c-1.a depends on b-1 and a-1.a and there is a gem b-1.a available then + # c-1.a, b-1 and a-1.a will be installed. b-1.a will need to be installed + # separately. def install dep_or_name, version = Gem::Requirement.default if String === dep_or_name then - find_spec_by_name_and_version dep_or_name, version + find_spec_by_name_and_version dep_or_name, version, @prerelease else + dep_or_name.prerelease = @prerelease @specs_and_sources = [find_gems_with_sources(dep_or_name).last] end diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb index a73a32d545..25d469d68c 100644 --- a/lib/rubygems/dependency_list.rb +++ b/lib/rubygems/dependency_list.rb @@ -6,23 +6,41 @@ require 'tsort' +## +# Gem::DependencyList is used for installing and uninstalling gems in the +# correct order to avoid conflicts. + class Gem::DependencyList include Enumerable include TSort - def self.from_source_index(src_index) - deps = new + ## + # Allows enabling/disabling use of development dependencies + + attr_accessor :development - src_index.each do |full_name, spec| - deps.add spec + ## + # Creates a DependencyList from a Gem::SourceIndex +source_index+ + + def self.from_source_index(source_index) + list = new + + source_index.each do |full_name, spec| + list.add spec end - deps + list end - def initialize + ## + # Creates a new DependencyList. If +development+ is true, development + # dependencies will be included. + + def initialize development = false @specs = [] + + @development = development end ## @@ -33,8 +51,9 @@ class Gem::DependencyList end ## - # Return a list of the specifications in the dependency list, sorted in - # order so that no spec in the list depends on a gem earlier in the list. + # Return a list of the gem specifications in the dependency list, sorted in + # order so that no gemspec in the list depends on a gemspec earlier in the + # list. # # This is useful when removing gems from a set of installed gems. By # removing them in the returned order, you don't get into as many dependency @@ -77,6 +96,10 @@ class Gem::DependencyList @specs.find { |spec| spec.full_name == full_name } end + def inspect # :nodoc: + "#<%s:0x%x %p>" % [self.class, object_id, map { |s| s.full_name }] + end + ## # Are all the dependencies in the list satisfied? @@ -89,10 +112,10 @@ class Gem::DependencyList end ## - # Is is ok to remove a gem from the dependency list? + # Is is ok to remove a gemspec from the dependency list? # # If removing the gemspec creates breaks a currently ok dependency, then it - # is NOT ok to remove the gem. + # is NOT ok to remove the gemspec. def ok_to_remove?(full_name) gem_to_remove = find_name full_name @@ -117,13 +140,16 @@ class Gem::DependencyList } end + ## + # Removes the gemspec matching +full_name+ from the dependency list + def remove_by_name(full_name) @specs.delete_if { |spec| spec.full_name == full_name } end ## # Return a hash of predecessors. <tt>result[spec]</tt> is an Array of - # gemspecs that have a dependency satisfied by the named spec. + # gemspecs that have a dependency satisfied by the named gemspec. def spec_predecessors result = Hash.new { |h,k| h[k] = [] } @@ -152,7 +178,10 @@ class Gem::DependencyList def tsort_each_child(node, &block) specs = @specs.sort.reverse - node.dependencies.each do |dep| + dependencies = node.runtime_dependencies + dependencies.push(*node.development_dependencies) if @development + + dependencies.each do |dep| specs.each do |spec| if spec.satisfies_requirement? dep then begin diff --git a/lib/rubygems/digest/md5.rb b/lib/rubygems/digest/md5.rb deleted file mode 100644 index 2bc8f9c138..0000000000 --- a/lib/rubygems/digest/md5.rb +++ /dev/null @@ -1,13 +0,0 @@ -#-- -# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. -# All rights reserved. -# See LICENSE.txt for permissions. -#++ - -require 'digest/md5' - -# :stopdoc: -module Gem - MD5 = Digest::MD5 -end -# :startdoc: diff --git a/lib/rubygems/digest/sha1.rb b/lib/rubygems/digest/sha1.rb deleted file mode 100644 index 2d91c0f45a..0000000000 --- a/lib/rubygems/digest/sha1.rb +++ /dev/null @@ -1,11 +0,0 @@ -#-- -# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. -# All rights reserved. -# See LICENSE.txt for permissions. -#++ - -require 'digest/sha1' - -module Gem - SHA1 = Digest::SHA1 -end diff --git a/lib/rubygems/digest/sha2.rb b/lib/rubygems/digest/sha2.rb deleted file mode 100644 index 91cb236165..0000000000 --- a/lib/rubygems/digest/sha2.rb +++ /dev/null @@ -1,11 +0,0 @@ -#-- -# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. -# All rights reserved. -# See LICENSE.txt for permissions. -#++ - -require 'digest/sha2' - -module Gem - SHA256 = Digest::SHA256 -end diff --git a/lib/rubygems/doc_manager.rb b/lib/rubygems/doc_manager.rb index e1c8e03b91..a4976ae10a 100644 --- a/lib/rubygems/doc_manager.rb +++ b/lib/rubygems/doc_manager.rb @@ -98,6 +98,13 @@ class Gem::DocManager end ## + # Is the RI documentation installed? + + def ri_installed? + File.exist?(File.join(@doc_dir, "ri")) + end + + ## # Generate the RI documents for this gem spec. # # Note that if both RI and RDoc documents are generated from the same diff --git a/lib/rubygems/format.rb b/lib/rubygems/format.rb index 80aae56215..82eaf9e77e 100644 --- a/lib/rubygems/format.rb +++ b/lib/rubygems/format.rb @@ -14,25 +14,22 @@ require 'rubygems/package' class Gem::Format - attr_accessor :spec, :file_entries, :gem_path + attr_accessor :spec + attr_accessor :file_entries + attr_accessor :gem_path extend Gem::UserInteraction ## - # Constructs an instance of a Format object, representing the gem's - # data structure. - # - # gem:: [String] The file name of the gem - # + # Constructs a Format representing the gem's data which came from +gem_path+ + def initialize(gem_path) @gem_path = gem_path end ## - # Reads the named gem file and returns a Format object, representing - # the data from the gem file - # - # file_path:: [String] Path to the gem file + # Reads the gem +file_path+ using +security_policy+ and returns a Format + # representing the data in the gem def self.from_file_by_path(file_path, security_policy = nil) format = nil @@ -41,25 +38,24 @@ class Gem::Format raise Gem::Exception, "Cannot load gem at [#{file_path}] in #{Dir.pwd}" end - # check for old version gem - if File.read(file_path, 20).include?("MD5SUM =") + start = File.read file_path, 20 + + if start.nil? or start.length < 20 then + nil + elsif start.include?("MD5SUM =") # old version gems require 'rubygems/old_format' - format = Gem::OldFormat.from_file_by_path(file_path) + Gem::OldFormat.from_file_by_path file_path else open file_path, Gem.binary_mode do |io| - format = from_io io, file_path, security_policy + from_io io, file_path, security_policy end end - - return format end ## - # Reads a gem from an io stream and returns a Format object, representing - # the data from the gem file - # - # io:: [IO] Stream from which to read the gem + # Reads a gem from +io+ at +gem_path+ using +security_policy+ and returns a + # Format representing the data from the gem def self.from_io(io, gem_path="(io)", security_policy = nil) format = new gem_path diff --git a/lib/rubygems/gem_openssl.rb b/lib/rubygems/gem_openssl.rb index bdaad8050a..7891d596c4 100644 --- a/lib/rubygems/gem_openssl.rb +++ b/lib/rubygems/gem_openssl.rb @@ -30,7 +30,7 @@ module Gem def ensure_ssl_available unless ssl_available? - fail Gem::Exception, "SSL is not installed on this system" + raise Gem::Exception, "SSL is not installed on this system" end end end diff --git a/lib/rubygems/gem_path_searcher.rb b/lib/rubygems/gem_path_searcher.rb index 2232a574e3..6ee3c078d5 100644 --- a/lib/rubygems/gem_path_searcher.rb +++ b/lib/rubygems/gem_path_searcher.rb @@ -1,9 +1,3 @@ -#-- -# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. -# All rights reserved. -# See LICENSE.txt for permissions. -#++ - ## # GemPathSearcher has the capability to find loadable files inside # gems. It generates data up front to speed up searches later. @@ -14,14 +8,16 @@ class Gem::GemPathSearcher # Initialise the data we need to make searches later. def initialize - # We want a record of all the installed gemspecs, in the order - # we wish to examine them. + # We want a record of all the installed gemspecs, in the order we wish to + # examine them. @gemspecs = init_gemspecs - # Map gem spec to glob of full require_path directories. - # Preparing this information may speed up searches later. + + # Map gem spec to glob of full require_path directories. Preparing this + # information may speed up searches later. @lib_dirs = {} + @gemspecs.each do |spec| - @lib_dirs[spec.object_id] = lib_dirs_for(spec) + @lib_dirs[spec.object_id] = lib_dirs_for spec end end @@ -72,6 +68,7 @@ class Gem::GemPathSearcher # Some of the intermediate results are cached in @lib_dirs for speed. def matching_files(spec, path) + return [] unless @lib_dirs[spec.object_id] # case no paths glob = File.join @lib_dirs[spec.object_id], "#{path}#{Gem.suffix_pattern}" Dir[glob].select { |f| File.file? f.untaint } end @@ -95,7 +92,8 @@ class Gem::GemPathSearcher # '/usr/local/lib/ruby/gems/1.8/gems/foobar-1.0/{lib,ext}' def lib_dirs_for(spec) - "#{spec.full_gem_path}/{#{spec.require_paths.join(',')}}" + "#{spec.full_gem_path}/{#{spec.require_paths.join(',')}}" if + spec.require_paths end end diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb new file mode 100644 index 0000000000..225bd704f9 --- /dev/null +++ b/lib/rubygems/gemcutter_utilities.rb @@ -0,0 +1,49 @@ +require 'net/http' +require 'rubygems/remote_fetcher' + +module Gem::GemcutterUtilities + + def sign_in + return if Gem.configuration.rubygems_api_key + + say "Enter your RubyGems.org credentials." + say "Don't have an account yet? Create one at http://rubygems.org/sign_up" + + email = ask " Email: " + password = ask_for_password "Password: " + say "\n" + + response = rubygems_api_request :get, "api/v1/api_key" 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 + + def rubygems_api_request(method, path, &block) + host = ENV['RUBYGEMS_HOST'] || 'https://rubygems.org' + uri = URI.parse "#{host}/#{path}" + + request_method = Net::HTTP.const_get method.to_s.capitalize + + Gem::RemoteFetcher.fetcher.request(uri, request_method, &block) + end + + def with_response(resp) + case resp + when Net::HTTPSuccess then + if block_given? then + yield resp + else + say resp.body + end + else + say resp.body + terminate_interaction 1 + end + end + +end diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb index aff8d8de17..baa582447d 100644 --- a/lib/rubygems/indexer.rb +++ b/lib/rubygems/indexer.rb @@ -58,7 +58,7 @@ class Gem::Indexer def initialize(directory, options = {}) unless ''.respond_to? :to_xs then - fail "Gem::Indexer requires that the XML Builder library be installed:" \ + raise "Gem::Indexer requires that the XML Builder library be installed:" \ "\n\tgem install builder" end @@ -350,7 +350,7 @@ class Gem::Indexer end index.sort_by { |_, spec| [-spec.date.to_i, spec] }.each do |_, spec| - gem_path = CGI.escapeHTML "http://#{@rss_gems_host}/gems/#{spec.full_name}.gem" + gem_path = CGI.escapeHTML "http://#{@rss_gems_host}/gems/#{spec.file_name}" size = File.stat(spec.loaded_from).size rescue next description = spec.description || spec.summary || '' diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb index a43a2a171c..c3fa2d5edd 100644 --- a/lib/rubygems/install_update_options.rb +++ b/lib/rubygems/install_update_options.rb @@ -95,8 +95,7 @@ module Gem::InstallUpdateOptions add_option(:"Install/Update", '--[no-]user-install', 'Install in user\'s home directory instead', - 'of GEM_HOME. Defaults to using home', - 'only if GEM_HOME is not writable.') do |value, options| + 'of GEM_HOME.') do |value, options| options[:user_install] = value end @@ -105,13 +104,6 @@ module Gem::InstallUpdateOptions "dependencies") do |value, options| options[:development] = true end - - add_option(:"Install/Update", "--prerelease", - "Install prerelease versions of a gem if", - "available. Defaults to skipping", - "prereleases.") do |value, options| - options[:prerelease] = true - end end ## diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index d6075dd0a2..50f8d20b33 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -5,7 +5,6 @@ #++ require 'fileutils' -require 'pathname' require 'rbconfig' require 'rubygems/format' @@ -58,17 +57,11 @@ class Gem::Installer attr_reader :spec - @home_install_warning = false @path_warning = false class << self ## - # True if we've warned about ~/.gems install - - attr_accessor :home_install_warning - - ## # True if we've warned about PATH not including Gem.bindir attr_accessor :path_warning @@ -112,7 +105,7 @@ class Gem::Installer @env_shebang = options[:env_shebang] @force = options[:force] gem_home = options[:install_dir] - @gem_home = Pathname.new(gem_home).expand_path + @gem_home = File.expand_path(gem_home) @ignore_dependencies = options[:ignore_dependencies] @format_executable = options[:format_executable] @security_policy = options[:security_policy] @@ -127,27 +120,6 @@ class Gem::Installer raise Gem::InstallError, "invalid gem format for #{@gem}" end - begin - FileUtils.mkdir_p @gem_home - rescue Errno::EACCES, Errno::ENOTDIR - # We'll divert to ~/.gems below - end - - if not File.writable? @gem_home or - # TODO: Shouldn't have to test for existence of bindir; tests need it. - (@gem_home.to_s == Gem.dir and File.exist? Gem.bindir and - not File.writable? Gem.bindir) then - if options[:user_install] == false then # You don't want to use ~ - raise Gem::FilePermissionError, @gem_home - elsif options[:user_install].nil? then - unless self.class.home_install_warning or options[:unpack] then - alert_warning "Installing to ~/.gem since #{@gem_home} and\n\t #{Gem.bindir} aren't both writable." - self.class.home_install_warning = true - end - end - options[:user_install] = true - end - if options[:user_install] and not options[:unpack] then @gem_home = Gem.user_dir @@ -158,12 +130,11 @@ class Gem::Installer self.class.path_warning = true end end - - FileUtils.mkdir_p @gem_home unless File.directory? @gem_home - # If it's still not writable, you've got issues. - raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home end + FileUtils.mkdir_p @gem_home + raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home + @spec = @format.spec @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint @@ -189,14 +160,15 @@ class Gem::Installer unless @force then if rrv = @spec.required_ruby_version then unless rrv.satisfied_by? Gem.ruby_version then - raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}" + raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}." end end if rrgv = @spec.required_rubygems_version then unless rrgv.satisfied_by? Gem::Version.new(Gem::RubyGemsVersion) then raise Gem::InstallError, - "#{@spec.name} requires RubyGems version #{rrgv}" + "#{@spec.name} requires RubyGems version #{rrgv}. " + + "Try 'gem update --system' to update RubyGems itself." end end @@ -235,8 +207,7 @@ class Gem::Installer say @spec.post_install_message unless @spec.post_install_message.nil? - @spec.loaded_from = File.join(@gem_home, 'specifications', - "#{@spec.full_name}.gemspec") + @spec.loaded_from = File.join(@gem_home, 'specifications', @spec.spec_name) @source_index.add_spec @spec @@ -268,7 +239,7 @@ class Gem::Installer # True if the gems in the source_index satisfy +dependency+. def installation_satisfies_dependency?(dependency) - @source_index.find_name(dependency.name, dependency.version_requirements).size > 0 + @source_index.find_name(dependency.name, dependency.requirement).size > 0 end ## @@ -287,8 +258,7 @@ class Gem::Installer def write_spec rubycode = @spec.to_ruby - file_name = File.join @gem_home, 'specifications', - "#{@spec.full_name}.gemspec" + file_name = File.join @gem_home, 'specifications', @spec.spec_name file_name.untaint @@ -522,7 +492,7 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')} # Ensures that files can't be installed outside the gem directory. def extract_files - expand_and_validate_gem_dir + @gem_dir = File.expand_path @gem_dir raise ArgumentError, "format required to extract from" if @format.nil? @@ -566,24 +536,5 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')} end end - private - - ## - # HACK Pathname is broken on windows. - - def absolute_path? pathname - pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i) - end - - def expand_and_validate_gem_dir - @gem_dir = Pathname.new(@gem_dir).expand_path - - unless absolute_path?(@gem_dir) then # HACK is this possible after #expand_path? - raise ArgumentError, "install directory %p not absolute" % @gem_dir - end - - @gem_dir = @gem_dir.to_s - end - end diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb index 9cb393b0c7..0fd64025ad 100644 --- a/lib/rubygems/package.rb +++ b/lib/rubygems/package.rb @@ -1,3 +1,4 @@ +# -*- coding: iso-8859-1 -*- #++ # Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier # See LICENSE.txt for additional licensing information. @@ -9,7 +10,6 @@ require 'stringio' require 'yaml' require 'zlib' -require 'rubygems/digest/md5' require 'rubygems/security' require 'rubygems/specification' diff --git a/lib/rubygems/package/f_sync_dir.rb b/lib/rubygems/package/f_sync_dir.rb index 1915ab3ecb..e15035ec37 100644 --- a/lib/rubygems/package/f_sync_dir.rb +++ b/lib/rubygems/package/f_sync_dir.rb @@ -1,3 +1,4 @@ +# -*- coding: iso-8859-1 -*- #++ # Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier # See LICENSE.txt for additional licensing information. diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb index 44c6695cf9..0689c319b9 100644 --- a/lib/rubygems/package/tar_header.rb +++ b/lib/rubygems/package/tar_header.rb @@ -1,3 +1,4 @@ +# -*- coding: iso-8859-1 -*- #++ # Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier # See LICENSE.txt for additional licensing information. diff --git a/lib/rubygems/package/tar_input.rb b/lib/rubygems/package/tar_input.rb index 9f901023b8..6c286585e9 100644 --- a/lib/rubygems/package/tar_input.rb +++ b/lib/rubygems/package/tar_input.rb @@ -1,3 +1,4 @@ +# -*- coding: iso-8859-1 -*- #++ # Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier # See LICENSE.txt for additional licensing information. @@ -199,7 +200,8 @@ class Gem::Package::TarInput # times. And that's the way it is. def zipped_stream(entry) - if defined? Rubinius then + if defined? Rubinius or defined? Maglev then + # these implementations have working Zlib zis = Zlib::GzipReader.new entry dis = zis.read is = StringIO.new(dis) diff --git a/lib/rubygems/package/tar_output.rb b/lib/rubygems/package/tar_output.rb index dfb74af9fc..98fa8b8fe1 100644 --- a/lib/rubygems/package/tar_output.rb +++ b/lib/rubygems/package/tar_output.rb @@ -1,3 +1,4 @@ +# -*- coding: iso-8859-1 -*- #++ # Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier # See LICENSE.txt for additional licensing information. diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb index 1a2fbaf678..b28714d124 100644 --- a/lib/rubygems/package/tar_reader.rb +++ b/lib/rubygems/package/tar_reader.rb @@ -1,3 +1,4 @@ +# -*- coding: iso-8859-1 -*- #++ # Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier # See LICENSE.txt for additional licensing information. diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb index 6f11529302..b5fc89e55e 100644 --- a/lib/rubygems/package/tar_writer.rb +++ b/lib/rubygems/package/tar_writer.rb @@ -1,3 +1,4 @@ +# -*- coding: iso-8859-1 -*- #++ # Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier # See LICENSE.txt for additional licensing information. diff --git a/lib/rubygems/package_task.rb b/lib/rubygems/package_task.rb index e80b1eceaf..bb0a60464e 100644 --- a/lib/rubygems/package_task.rb +++ b/lib/rubygems/package_task.rb @@ -97,22 +97,27 @@ class Gem::PackageTask < Rake::PackageTask def define super + task :package => [:gem] + + gem_file = gem_spec.file_name + gem_path = File.join package_dir, gem_file + desc "Build the gem file #{gem_file}" - task :gem => ["#{package_dir}/#{gem_file}"] - file "#{package_dir}/#{gem_file}" => [package_dir] + @gem_spec.files do - when_writing("Creating #{gem_spec.full_name}.gem") { + task :gem => [gem_path] + + trace = Rake.application.options.trace + Gem.configuration.verbose = trace + + file gem_path => [package_dir] + @gem_spec.files do + when_writing "Creating #{gem_spec.file_name}" do Gem::Builder.new(gem_spec).build - verbose(true) { - mv gem_file, "#{package_dir}/#{gem_file}" - } - } + verbose trace do + mv gem_file, gem_path + end + end end end - def gem_file - "#{@gem_spec.full_name}.gem" - end - end diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 945523e5b0..dbd4cef816 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -81,7 +81,7 @@ class Gem::RemoteFetcher cache_dir = File.join(Gem.user_dir, 'cache') end - gem_file_name = "#{spec.full_name}.gem" + gem_file_name = spec.file_name local_gem_path = File.join cache_dir, gem_file_name FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir @@ -138,12 +138,18 @@ class Gem::RemoteFetcher say "Using local gem #{local_gem_path}" if Gem.configuration.really_verbose when nil then # TODO test for local overriding cache + source_path = if Gem.win_platform? && source_uri.scheme && + !source_uri.path.include?(':') then + "#{source_uri.scheme}:#{source_uri.path}" + else + source_uri.path + end + + source_path = URI.unescape source_path + begin - if Gem.win_platform? && source_uri.scheme && !source_uri.path.include?(':') - FileUtils.cp URI.unescape(source_uri.scheme + ':' + source_uri.path), local_gem_path - else - FileUtils.cp URI.unescape(source_uri.path), local_gem_path - end + FileUtils.cp source_path, local_gem_path unless + File.expand_path(source_path) == File.expand_path(local_gem_path) rescue Errno::EACCES local_gem_path = source_uri.to_s end @@ -317,6 +323,8 @@ class Gem::RemoteFetcher request.add_field 'If-Modified-Since', last_modified.rfc2822 end + yield request if block_given? + connection = connection_for uri retried = false @@ -324,10 +332,16 @@ class Gem::RemoteFetcher begin @requests[connection.object_id] += 1 + + say "#{request.method} #{uri}" if + Gem.configuration.really_verbose response = connection.request request - say "#{request.method} #{response.code} #{response.message}: #{uri}" if + say "#{response.code} #{response.message}" if Gem.configuration.really_verbose + rescue Net::HTTPBadResponse + say "bad response" if Gem.configuration.really_verbose + reset connection raise FetchError.new('too many bad responses', uri) if bad_response @@ -337,7 +351,7 @@ class Gem::RemoteFetcher # HACK work around EOFError bug in Net::HTTP # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible # to install gems. - rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET + rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE requests = @requests[connection.object_id] say "connection reset after #{requests} requests, retrying" if Gem.configuration.really_verbose diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 471d84eb61..d9b510a76d 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -1,41 +1,33 @@ -#-- -# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. -# All rights reserved. -# See LICENSE.txt for permissions. -#++ +require "rubygems/version" ## -# Requirement version includes a prefaced comparator in addition -# to a version number. -# -# A Requirement object can actually contain multiple, er, -# requirements, as in (> 1.2, < 2.0). +# A Requirement is a set of one or more version restrictions. It supports a +# few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators. class Gem::Requirement - include Comparable - attr_reader :requirements - - OPS = { + OPS = { #:nodoc: "=" => lambda { |v, r| v == r }, "!=" => lambda { |v, r| v != r }, - ">" => lambda { |v, r| v > r }, - "<" => lambda { |v, r| v < r }, + ">" => lambda { |v, r| v > r }, + "<" => lambda { |v, r| v < r }, ">=" => lambda { |v, r| v >= r }, "<=" => lambda { |v, r| v <= r }, "~>" => lambda { |v, r| v = v.release; v >= r && v < r.bump } } - OP_RE = OPS.keys.map{ |k| Regexp.quote k }.join '|' + quoted = OPS.keys.map { |k| Regexp.quote k }.join "|" + PATTERN = /\A\s*(#{quoted})?\s*(#{Gem::Version::VERSION_PATTERN})\s*\z/ ## - # Factory method to create a Gem::Requirement object. Input may be a - # Version, a String, or nil. Intended to simplify client code. + # Factory method to create a Gem::Requirement object. Input may be + # a Version, a String, or nil. Intended to simplify client code. # - # If the input is "weird", the default version requirement is returned. + # If the input is "weird", the default version requirement is + # returned. - def self.create(input) + def self.create input case input when Gem::Requirement then input @@ -43,9 +35,9 @@ class Gem::Requirement new input else if input.respond_to? :to_str then - self.new [input.to_str] + new [input.to_str] else - self.default + default end end end @@ -58,113 +50,99 @@ class Gem::Requirement # "A default "version requirement" can surely _only_ be '> 0'." def self.default - self.new ['>= 0'] + new '>= 0' end ## - # Constructs a Requirement from +requirements+ which can be a String, a - # Gem::Version, or an Array of those. See #parse for details on the - # formatting of requirement strings. - - def initialize(requirements) - @requirements = case requirements - when Array then - requirements.map do |requirement| - parse(requirement) - end - else - [parse(requirements)] - end - @version = nil # Avoid warnings. - end + # Parse +obj+, returning an <tt>[op, version]</tt> pair. +obj+ can + # be a String or a Gem::Version. + # + # If +obj+ is a String, it can be either a full requirement + # specification, like <tt>">= 1.2"</tt>, or a simple version number, + # like <tt>"1.2"</tt>. + # + # parse("> 1.0") # => [">", "1.0"] + # parse("1.0") # => ["=", "1.0"] + # parse(Gem::Version.new("1.0")) # => ["=, "1.0"] - ## - # Marshal raw requirements, rather than the full object + def self.parse obj + return ["=", obj] if Gem::Version === obj - def marshal_dump # :nodoc: - [@requirements] + unless PATTERN =~ obj.to_s + raise ArgumentError, "Illformed requirement [#{obj.inspect}]" + end + + [$1 || "=", Gem::Version.new($2)] end ## - # Load custom marshal format + # An array of requirement pairs. The first element of the pair is + # the op, and the second is the Gem::Version. - def marshal_load(array) # :nodoc: - @requirements = array[0] - @version = nil + attr_reader :requirements #:nodoc: + + ## + # Constructs a requirement from +requirements+. Requirements can be + # Strings, Gem::Versions, or Arrays of those. +nil+ and duplicate + # requirements are ignored. An empty set of +requirements+ is the + # same as <tt>">= 0"</tt>. + + def initialize *requirements + requirements = requirements.flatten + requirements.compact! + requirements.uniq! + + requirements << ">= 0" if requirements.empty? + @requirements = requirements.map! { |r| self.class.parse r } end - def to_s # :nodoc: - as_list.join(", ") + def as_list # :nodoc: + requirements.map { |op, version| "#{op} #{version}" } end - def pretty_print(q) # :nodoc: - q.group 1, 'Gem::Requirement.new(', ')' do - q.pp as_list - end + def hash # :nodoc: + requirements.hash end - def as_list - normalize - @requirements.map do |op, version| "#{op} #{version}" end + def marshal_dump # :nodoc: + [@requirements] end - def normalize - return if not defined? @version or @version.nil? - @requirements = [parse(@version)] - @nums = nil - @version = nil - @op = nil + def marshal_load array # :nodoc: + @requirements = array[0] end - ## - # True if this requirement satisfied by the Gem::Version +version+. + def prerelease? + requirements.any? { |r| r.last.prerelease? } + end - def satisfied_by?(version) - normalize - @requirements.all? { |op, rv| satisfy?(op, version, rv) } + def pretty_print q # :nodoc: + q.group 1, 'Gem::Requirement.new(', ')' do + q.pp as_list + end end ## - # Is "+version+ +op+ +required_version+" satisfied? + # True if +version+ satisfies this Requirement. - def satisfy?(op, version, required_version) - OPS[op].call(version, required_version) - end - - def prerelease? - # TODO: why is @requirements a nested array? - @requirements.any?{ |r| r[1].prerelease? } + def satisfied_by? version + requirements.all? { |op, rv| OPS[op].call version, rv } end - ## - # Parse the version requirement obj returning the operator and version. - # - # The requirement can be a String or a Gem::Version. A String can be an - # operator (<, <=, =, >=, >, !=, ~>), a version number, or both, operator - # first. - - def parse(obj) - case obj - when /^\s*(#{OP_RE})\s*(#{Gem::Version::VERSION_PATTERN})\s*$/o then - [$1, Gem::Version.new($2)] - when /^\s*(#{Gem::Version::VERSION_PATTERN})\s*$/o then - ['=', Gem::Version.new($1)] - when /^\s*(#{OP_RE})\s*$/o then - [$1, Gem::Version.new('0')] - when Gem::Version then - ['=', obj] - else - fail ArgumentError, "Illformed requirement [#{obj.inspect}]" - end + def to_s # :nodoc: + as_list.join ", " end - def <=>(other) # :nodoc: + def <=> other # :nodoc: to_s <=> other.to_s end +end - def hash # :nodoc: - to_s.hash - end +# :stopdoc: +# Gem::Version::Requirement is used in a lot of old YAML specs. It's aliased +# here for backwards compatibility. I'd like to remove this, maybe in RubyGems +# 2.0. -end +::Gem::Version::Requirement = ::Gem::Requirement +# :startdoc: diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb index 44bee74abf..24c9e044e1 100644 --- a/lib/rubygems/server.rb +++ b/lib/rubygems/server.rb @@ -39,9 +39,9 @@ class Gem::Server SEARCH = <<-SEARCH <form class="headerSearch" name="headerSearchForm" method="get" action="/https/git.ruby-lang.org/rdoc"> <div id="search" style="float:right"> - <span>Filter/Search</span> - <input id="q" type="text" style="width:10em" name="q"/> - <button type="submit" style="display:none" /> + <label for="q">Filter/Search</label> + <input id="q" type="text" style="width:10em" name="q"> + <button type="submit" style="display:none"></button> </div> </form> SEARCH @@ -426,15 +426,17 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } RDOC_SEARCH def self.run(options) - new(options[:gemdir], options[:port], options[:daemon]).run + new(options[:gemdir], options[:port], options[:daemon], + options[:addresses]).run end - def initialize(gem_dir, port, daemon) + def initialize(gem_dir, port, daemon, addresses = nil) Socket.do_not_reverse_lookup = true @gem_dir = gem_dir @port = port @daemon = daemon + @addresses = addresses logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger @@ -498,6 +500,37 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } end end + ## + # Creates server sockets based on the addresses option. If no addresses + # were given a server socket for all interfaces is created. + + def listen addresses = @addresses + addresses = [nil] unless addresses + + listeners = 0 + + addresses.each do |address| + begin + @server.listen address, @port + @server.listeners[listeners..-1].each do |listener| + host, port = listener.addr.values_at 2, 1 + host = "[#{host}]" if host =~ /:/ # we don't reverse lookup + say "Server started at http://#{host}:#{port}" + end + + listeners = @server.listeners.length + rescue SystemCallError + next + end + end + + if @server.listeners.empty? then + say "Unable to start a server." + say "Check for running servers or your --bind and --port arguments" + terminate_interaction 1 + end + end + def quick(req, res) @source_index.refresh! @@ -566,7 +599,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } deps = spec.dependencies.map do |dep| { "name" => dep.name, "type" => dep.type, - "version" => dep.version_requirements.to_s, } + "version" => dep.requirement.to_s, } end deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] } @@ -602,7 +635,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } "only_one_executable" => true, "full_name" => "rubygems-#{Gem::RubyGemsVersion}", "has_deps" => false, - "homepage" => "http://rubygems.org/", + "homepage" => "http://docs.rubygems.org/", "name" => 'rubygems', "rdoc_installed" => true, "summary" => "RubyGems itself", @@ -716,9 +749,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } end def run - @server.listen nil, @port - - say "Starting gem server on http://localhost:#{@port}/" + listen WEBrick::Daemon.start if @daemon diff --git a/lib/rubygems/source_index.rb b/lib/rubygems/source_index.rb index 8caf7b2e35..ac5d8add1e 100644 --- a/lib/rubygems/source_index.rb +++ b/lib/rubygems/source_index.rb @@ -303,7 +303,7 @@ class Gem::SourceIndex version_requirement = platform_only || Gem::Requirement.default when Gem::Dependency then only_platform = platform_only - version_requirement = gem_pattern.version_requirements + version_requirement = gem_pattern.requirement gem_pattern = if Regexp === gem_pattern.name then gem_pattern.name elsif gem_pattern.name.empty? then diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb index a5f33183dd..6d1fa9f63d 100644 --- a/lib/rubygems/spec_fetcher.rb +++ b/lib/rubygems/spec_fetcher.rb @@ -197,7 +197,7 @@ class Gem::SpecFetcher if type == :all list.values.map do |gems| - gems.reject! { |g| g[1].prerelease? } + gems.reject! { |g| !g[1] || g[1].prerelease? } end end @@ -242,7 +242,7 @@ class Gem::SpecFetcher FileUtils.mkdir_p cache_dir open local_file, 'wb' do |io| - Marshal.dump specs, io + io << spec_dump end rescue end diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 3630b4adf5..472d60817b 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -17,7 +17,7 @@ class Date; end # for ruby_code if date.rb wasn't required # defined in a .gemspec file or a Rakefile, and looks like this: # # spec = Gem::Specification.new do |s| -# s.name = 'rfoo' +# s.name = 'example' # s.version = '1.0' # s.summary = 'Example gem specification' # ... @@ -409,15 +409,19 @@ class Gem::Specification # :startdoc: ## - # Specification constructor. Assigns the default values to the attributes - # and yields itself for further initialization. + # Specification constructor. Assigns the default values to the + # attributes and yields itself for further + # initialization. Optionally takes +name+ and +version+. - def initialize + def initialize name = nil, version = nil @new_platform = nil assign_defaults @loaded = false @loaded_from = nil + self.name = name if name + self.version = version if version + yield self if block_given? @@gather.call(self) if @@gather @@ -498,7 +502,7 @@ class Gem::Specification def self.load(filename) gemspec = nil - fail "NESTED Specification.load calls not allowed!" if @@gather + raise "NESTED Specification.load calls not allowed!" if @@gather @@gather = proc { |gs| gemspec = gs } data = File.read(filename) eval(data) @@ -598,10 +602,12 @@ class Gem::Specification end ## - # The default (generated) file name of the gem. + # The default (generated) file name of the gem. See also #spec_name. + # + # spec.file_name # => "example-1.0.gem" def file_name - full_name + ".gem" + full_name + '.gem' end ## @@ -620,7 +626,7 @@ class Gem::Specification def satisfies_requirement?(dependency) return @name == dependency.name && - dependency.version_requirements.satisfied_by?(@version) + dependency.requirement.satisfied_by?(@version) end ## @@ -630,6 +636,15 @@ class Gem::Specification [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1] end + ## + # The default name of the gemspec. See also #file_name + # + # spec.spec_name # => "example-1.0.gemspec" + + def spec_name + full_name + '.gemspec' + end + def <=>(other) # :nodoc: sort_obj <=> other.sort_obj end @@ -1033,14 +1048,18 @@ class Gem::Specification ## # :attr_accessor: rubygems_version # - # The version of RubyGems used to create this gem + # The version of RubyGems used to create this gem. + # + # Do not set this, it is set automatically when the gem is packaged. required_attribute :rubygems_version, Gem::RubyGemsVersion ## # :attr_accessor: specification_version # - # The Gem::Specification version of this gemspec + # The Gem::Specification version of this gemspec. + # + # Do not set this, it is set automatically when the gem is packaged. required_attribute :specification_version, CURRENT_SPECIFICATION_VERSION @@ -1062,6 +1081,8 @@ class Gem::Specification # :attr_accessor: date # # The date this gem was created + # + # Do not set this, it is set automatically when the gem is packaged. required_attribute :date, TODAY @@ -1069,13 +1090,20 @@ class Gem::Specification # :attr_accessor: summary # # A short summary of this gem's description. Displayed in `gem list -d`. + # + # The description should be more detailed than the summary. For example, + # you might wish to copy the entire README into the description. + # + # As of RubyGems 1.3.2 newlines are no longer stripped. required_attribute :summary ## # :attr_accessor: require_paths # - # Paths in the gem to add to $LOAD_PATH when this gem is activated + # Paths in the gem to add to $LOAD_PATH when this gem is activated. + # + # The default 'lib' is typically sufficient. required_attribute :require_paths, ['lib'] @@ -1085,6 +1113,13 @@ class Gem::Specification # :attr_accessor: email # # A contact email for this gem + # + # If you are providing multiple authors and multiple emails they should be + # in the same order such that: + # + # Hash[*spec.authors.zip(spec.emails).flatten] + # + # Gives a hash of author name to email address. attribute :email @@ -1122,6 +1157,8 @@ class Gem::Specification # :attr_accessor: default_executable # # The default executable for this gem. + # + # This is not used. attribute :default_executable @@ -1149,7 +1186,7 @@ class Gem::Specification ## # :attr_accessor: required_ruby_version # - # The ruby of version required by this gem + # The version of ruby required by this gem attribute :required_ruby_version, Gem::Requirement.default @@ -1164,6 +1201,9 @@ class Gem::Specification # :attr_accessor: platform # # The platform this gem runs on. See Gem::Platform for details. + # + # Setting this to any value other than Gem::Platform::RUBY or + # Gem::Platform::CURRENT is probably wrong. attribute :platform, Gem::Platform::RUBY @@ -1192,7 +1232,14 @@ class Gem::Specification ## # :attr_accessor: authors # - # The list of authors who wrote this gem + # The list of author names who wrote this gem. + # + # If you are providing multiple authors and multiple emails they should be + # in the same order such that: + # + # Hash[*spec.authors.zip(spec.emails).flatten] + # + # Gives a hash of author name to email address. array_attribute :authors @@ -1228,21 +1275,21 @@ class Gem::Specification ## # :attr_accessor: rdoc_options # - # An ARGV-style array of options to RDoc + # An ARGV style array of options to RDoc array_attribute :rdoc_options ## # :attr_accessor: extra_rdoc_files # - # Extra files to add to RDoc + # Extra files to add to RDoc such as README or doc/examples.txt array_attribute :extra_rdoc_files ## # :attr_accessor: executables # - # Executables included in the gem + # Executables included in the gem. array_attribute :executables @@ -1266,6 +1313,9 @@ class Gem::Specification # :attr_reader: dependencies # # A list of Gem::Dependency objects this gem depends on. + # + # Use #add_dependency or #add_development_dependency to add dependencies to + # a gem. array_attribute :dependencies diff --git a/lib/rubygems/test_utilities.rb b/lib/rubygems/test_utilities.rb index 751c13441b..9a4759cb99 100644 --- a/lib/rubygems/test_utilities.rb +++ b/lib/rubygems/test_utilities.rb @@ -23,6 +23,7 @@ require 'rubygems/remote_fetcher' class Gem::FakeFetcher attr_reader :data + attr_reader :last_request attr_accessor :paths def initialize @@ -30,16 +31,20 @@ class Gem::FakeFetcher @paths = [] end - def fetch_path path, mtime = nil + def find_data(path) path = path.to_s @paths << path - raise ArgumentError, 'need full URI' unless path =~ %r'^http://' + raise ArgumentError, 'need full URI' unless path =~ %r'^https?://' unless @data.key? path then raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path) end - data = @data[path] + @data[path] + end + + def fetch_path path, mtime = nil + data = find_data(path) if data.respond_to?(:call) then data.call @@ -52,6 +57,30 @@ class Gem::FakeFetcher end end + # Thanks, FakeWeb! + def open_uri_or_path(path) + data = find_data(path) + body, code, msg = data + + response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg) + response.instance_variable_set(:@body, body) + response.instance_variable_set(:@read, true) + response + end + + def request(uri, request_class, last_modified = nil) + data = find_data(uri) + body, code, msg = data + + @last_request = request_class.new uri.request_uri + yield @last_request if block_given? + + response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg) + response.instance_variable_set(:@body, body) + response.instance_variable_set(:@read, true) + response + end + def fetch_size(path) path = path.to_s @paths << path @@ -68,7 +97,7 @@ class Gem::FakeFetcher end def download spec, source_uri, install_dir = Gem.dir - name = "#{spec.full_name}.gem" + name = spec.file_name path = File.join(install_dir, 'cache', name) Gem.ensure_gem_subdirectories install_dir diff --git a/lib/rubygems/timer.rb b/lib/rubygems/timer.rb deleted file mode 100644 index 028e640230..0000000000 --- a/lib/rubygems/timer.rb +++ /dev/null @@ -1,28 +0,0 @@ -# -# This file defines a $log variable for logging, and a time() method for -# recording timing information. -# -#-- -# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. -# All rights reserved. -# See LICENSE.txt for permissions. -#++ - -require 'rubygems' - -file, lineno = Gem.location_of_caller - -warn "#{file}:#{lineno}:Warning: RubyGems' lib/rubygems/timer.rb deprecated and will be removed on or after June 2009." - -$log = Object.new - -# :stopdoc: -def $log.debug(message) - Gem.debug message -end - -def time(msg, width=25, &block) - Gem.time(msg, width, &block) -end -# :startdoc: - diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb index 4bb777a2a2..1dea232634 100644 --- a/lib/rubygems/uninstaller.rb +++ b/lib/rubygems/uninstaller.rb @@ -202,7 +202,7 @@ class Gem::Uninstaller spec.name, spec.version, spec.original_platform].join '-' spec_dir = File.join spec.installation_path, 'specifications' - gemspec = File.join spec_dir, "#{spec.full_name}.gemspec" + gemspec = File.join spec_dir, spec.spec_name unless File.exist? gemspec then gemspec = File.join spec_dir, "#{original_platform_name}.gemspec" @@ -211,7 +211,7 @@ class Gem::Uninstaller FileUtils.rm_rf gemspec cache_dir = File.join spec.installation_path, 'cache' - gem = File.join cache_dir, "#{spec.full_name}.gem" + gem = File.join cache_dir, spec.file_name unless File.exist? gem then gem = File.join cache_dir, "#{original_platform_name}.gem" @@ -251,7 +251,7 @@ class Gem::Uninstaller spec.dependent_gems.each do |gem,dep,satlist| msg << ("#{gem.name}-#{gem.version} depends on " + - "[#{dep.name} (#{dep.version_requirements})]") + "[#{dep.name} (#{dep.requirement})]") end msg << 'If you remove this gems, one or more dependencies will not be met.' msg << 'Continue with Uninstall?' diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb index 6a80c7c0bc..1ab948dcb6 100644 --- a/lib/rubygems/user_interaction.rb +++ b/lib/rubygems/user_interaction.rb @@ -112,6 +112,7 @@ module Gem::UserInteraction :alert_error, :alert_warning, :ask, + :ask_for_password, :ask_yes_no, :choose_from_list, :say, @@ -218,6 +219,50 @@ class Gem::StreamUI end ## + # Ask for a password. Does not echo response to terminal. + + def ask_for_password(question) + return nil if not @ins.tty? + + @outs.print(question + " ") + @outs.flush + + Gem.win_platform? ? ask_for_password_on_windows : ask_for_password_on_unix + end + + ## + # Asks for a password that works on windows. Ripped from the Heroku gem. + + def ask_for_password_on_windows + require "Win32API" + char = nil + password = '' + + while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do + break if char == 10 || char == 13 # received carriage return or newline + if char == 127 || char == 8 # backspace and delete + password.slice!(-1, 1) + else + password << char.chr + end + end + + puts + password + end + + ## + # Asks for a password that works on unix + + def ask_for_password_on_unix + system "stty -echo" + password = @ins.gets + password.chomp! if password + system "stty echo" + password + end + + ## # Display a statement. def say(statement="") diff --git a/lib/rubygems/validator.rb b/lib/rubygems/validator.rb index c8384f1586..38ee62fd6d 100644 --- a/lib/rubygems/validator.rb +++ b/lib/rubygems/validator.rb @@ -93,9 +93,8 @@ class Gem::Validator next unless gems.include? gem_spec.name unless gems.empty? install_dir = gem_spec.installation_path - gem_path = File.join(install_dir, "cache", gem_spec.full_name) + ".gem" - spec_path = File.join(install_dir, "specifications", - gem_spec.full_name) + ".gemspec" + gem_path = File.join install_dir, "cache", gem_spec.file_name + spec_path = File.join install_dir, "specifications", gem_spec.spec_name gem_directory = gem_spec.full_gem_path unless File.directory? gem_directory then diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb index f959429846..77403ff32b 100644 --- a/lib/rubygems/version.rb +++ b/lib/rubygems/version.rb @@ -1,9 +1,3 @@ -#-- -# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. -# All rights reserved. -# See LICENSE.txt for permissions. -#++ - ## # The Version class processes string versions into comparable # values. A version string should normally be a series of numbers @@ -24,72 +18,153 @@ # 2. 1.0.b # 3. 1.0.a # 4. 0.9 +# +# == How Software Changes +# +# Users expect to be able to specify a version constraint that gives them +# some reasonable expectation that new versions of a library will work with +# their software if the version constraint is true, and not work with their +# software if the version constraint is false. In other words, the perfect +# system will accept all compatible versions of the library and reject all +# incompatible versions. +# +# Libraries change in 3 ways (well, more than 3, but stay focused here!). +# +# 1. The change may be an implementation detail only and have no effect on +# the client software. +# 2. The change may add new features, but do so in a way that client software +# written to an earlier version is still compatible. +# 3. The change may change the public interface of the library in such a way +# that old software is no longer compatible. +# +# Some examples are appropriate at this point. Suppose I have a Stack class +# that supports a <tt>push</tt> and a <tt>pop</tt> method. +# +# === Examples of Category 1 changes: +# +# * Switch from an array based implementation to a linked-list based +# implementation. +# * Provide an automatic (and transparent) backing store for large stacks. +# +# === Examples of Category 2 changes might be: +# +# * Add a <tt>depth</tt> method to return the current depth of the stack. +# * Add a <tt>top</tt> method that returns the current top of stack (without +# changing the stack). +# * Change <tt>push</tt> so that it returns the item pushed (previously it +# had no usable return value). +# +# === Examples of Category 3 changes might be: +# +# * Changes <tt>pop</tt> so that it no longer returns a value (you must use +# <tt>top</tt> to get the top of the stack). +# * Rename the methods to <tt>push_item</tt> and <tt>pop_item</tt>. +# +# == RubyGems Rational Versioning +# +# * Versions shall be represented by three non-negative integers, separated +# by periods (e.g. 3.1.4). The first integers is the "major" version +# number, the second integer is the "minor" version number, and the third +# integer is the "build" number. +# +# * A category 1 change (implementation detail) will increment the build +# number. +# +# * A category 2 change (backwards compatible) will increment the minor +# version number and reset the build number. +# +# * A category 3 change (incompatible) will increment the major build number +# and reset the minor and build numbers. +# +# * Any "public" release of a gem should have a different version. Normally +# that means incrementing the build number. This means a developer can +# generate builds all day long for himself, but as soon as he/she makes a +# public release, the version must be updated. +# +# === Examples +# +# Let's work through a project lifecycle using our Stack example from above. +# +# Version 0.0.1:: The initial Stack class is release. +# Version 0.0.2:: Switched to a linked=list implementation because it is +# cooler. +# Version 0.1.0:: Added a <tt>depth</tt> method. +# Version 1.0.0:: Added <tt>top</tt> and made <tt>pop</tt> return nil +# (<tt>pop</tt> used to return the old top item). +# Version 1.1.0:: <tt>push</tt> now returns the value pushed (it used it +# return nil). +# Version 1.1.1:: Fixed a bug in the linked list implementation. +# Version 1.1.2:: Fixed a bug introduced in the last fix. +# +# Client A needs a stack with basic push/pop capability. He writes to the +# original interface (no <tt>top</tt>), so his version constraint looks +# like: +# +# gem 'stack', '~> 0.0' +# +# Essentially, any version is OK with Client A. An incompatible change to +# the library will cause him grief, but he is willing to take the chance (we +# call Client A optimistic). +# +# Client B is just like Client A except for two things: (1) He uses the +# <tt>depth</tt> method and (2) he is worried about future +# incompatibilities, so he writes his version constraint like this: +# +# gem 'stack', '~> 0.1' +# +# The <tt>depth</tt> method was introduced in version 0.1.0, so that version +# or anything later is fine, as long as the version stays below version 1.0 +# where incompatibilities are introduced. We call Client B pessimistic +# because he is worried about incompatible future changes (it is OK to be +# pessimistic!). +# +# == Preventing Version Catastrophe: +# +# From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.html +# +# Let's say you're depending on the fnord gem version 2.y.z. If you +# specify your dependency as ">= 2.0.0" then, you're good, right? What +# happens if fnord 3.0 comes out and it isn't backwards compatible +# with 2.y.z? Your stuff will break as a result of using ">=". The +# better route is to specify your dependency with a "spermy" version +# specifier. They're a tad confusing, so here is how the dependency +# specifiers work: +# +# Specification From ... To (exclusive) +# ">= 3.0" 3.0 ... ∞ +# "~> 3.0" 3.0 ... 4.0 +# "~> 3.0.0" 3.0.0 ... 3.1 +# "~> 3.5" 3.5 ... 4.0 +# "~> 3.5.0" 3.5.0 ... 3.6 class Gem::Version - - class Part - include Comparable - - attr_reader :value - - def initialize(value) - @value = (value =~ /\A\d+\z/) ? value.to_i : value - end - - def to_s - self.value.to_s - end - - def inspect - @value - end - - def alpha? - String === value - end - - def numeric? - Fixnum === value - end - - def <=>(other) - if self.numeric? && other.alpha? then - 1 - elsif self.alpha? && other.numeric? then - -1 - else - self.value <=> other.value - end - end - - def succ - self.class.new(self.value.succ) - end - end - include Comparable - VERSION_PATTERN = '[0-9]+(\.[0-9a-z]+)*' + VERSION_PATTERN = '[0-9]+(\.[0-9a-zA-Z]+)*' # :nodoc: + ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})*\s*\z/ # :nodoc: + + ## + # A string representation of this Version. attr_reader :version + alias to_s version - def self.correct?(version) - pattern = /\A\s*(#{VERSION_PATTERN})*\s*\z/ + ## + # True if the +version+ string matches RubyGems' requirements. - version.is_a? Integer or - version =~ pattern or - version.to_s =~ pattern + def self.correct? version + version.to_s =~ ANCHORED_VERSION_PATTERN end ## - # Factory method to create a Version object. Input may be a Version or a - # String. Intended to simplify client code. + # Factory method to create a Version object. Input may be a Version + # or a String. Intended to simplify client code. # # ver1 = Version.create('1.3.17') # -> (Version object) # ver2 = Version.create(ver1) # -> (ver1) # ver3 = Version.create(nil) # -> nil - def self.create(input) + def self.create input if input.respond_to? :version then input elsif input.nil? then @@ -103,149 +178,129 @@ class Gem::Version # Constructs a Version from the +version+ string. A version string is a # series of digits or ASCII letters separated by dots. - def initialize(version) + def initialize version raise ArgumentError, "Malformed version number string #{version}" unless self.class.correct?(version) - self.version = version - end + @version = version.to_s + @version.strip! - def inspect # :nodoc: - "#<#{self.class} #{@version.inspect}>" + segments # prime @segments end ## - # Dump only the raw version string, not the complete object + # Return a new version object where the next to the last revision + # number is one greater (e.g., 5.3.1 => 5.4). + # + # Pre-release (alpha) parts, e.g, 5.3.1.b2 => 5.4, are ignored. - def marshal_dump - [@version] + def bump + segments = self.segments.dup + segments.pop while segments.any? { |s| String === s } + segments.pop if segments.size > 1 + + segments[-1] = segments[-1].succ + self.class.new segments.join(".") end ## - # Load custom marshal format + # A Version is only eql? to another version if it's specified to the + # same precision. Version "1.0" is not the same as version "1". - def marshal_load(array) - self.version = array[0] + def eql? other + self.class === other and segments == other.segments end - def parts - @parts ||= normalize + def hash # :nodoc: + segments.hash end - ## - # Strip ignored trailing zeros. - - def normalize - parts_arr = parse_parts_from_version_string - if parts_arr.length != 1 - parts_arr.pop while parts_arr.last && parts_arr.last.value == 0 - parts_arr = [Part.new(0)] if parts_arr.empty? - end - parts_arr + def inspect # :nodoc: + "#<#{self.class} #{version.inspect}>" end ## - # Returns the text representation of the version + # Dump only the raw version string, not the complete object. It's a + # string for backwards (RubyGems 1.3.5 and earlier) compatibility. - def to_s - @version + def marshal_dump + [version] end - def to_yaml_properties - ['@version'] - end + ## + # Load custom marshal format. It's a string for backwards (RubyGems + # 1.3.5 and earlier) compatibility. - def version=(version) - @version = version.to_s.strip - normalize + def marshal_load array + initialize array[0] end - ## - # A version is considered a prerelease if any part contains a letter. + # A version is considered a prerelease if it contains a letter. def prerelease? - parts.any? { |part| part.alpha? } - end - - ## - # The release for this version (e.g. 1.2.0.a -> 1.2.0) - # Non-prerelease versions return themselves - def release - return self unless prerelease? - rel_parts = parts.dup - rel_parts.pop while rel_parts.any? { |part| part.alpha? } - self.class.new(rel_parts.join('.')) + @prerelease ||= segments.any? { |s| String === s } end - def yaml_initialize(tag, values) - self.version = values['version'] + def pretty_print q # :nodoc: + q.text "Gem::Version.new(#{version.inspect})" end - ## - # Compares this version with +other+ returning -1, 0, or 1 if the other - # version is larger, the same, or smaller than this one. + # The release for this version (e.g. 1.2.0.a -> 1.2.0). + # Non-prerelease versions return themselves. - def <=>(other) - return nil unless self.class === other - return 1 unless other - mine, theirs = balance(self.parts.dup, other.parts.dup) - mine <=> theirs - end + def release + return self unless prerelease? - def balance(a, b) - a << Part.new(0) while a.size < b.size - b << Part.new(0) while b.size < a.size - [a, b] + segments = self.segments.dup + segments.pop while segments.any? { |s| String === s } + self.class.new segments.join('.') end - ## - # A Version is only eql? to another version if it has the same version - # string. "1.0" is not the same version as "1". + def segments # :nodoc: - def eql?(other) - self.class === other and @version == other.version - end + # @segments is lazy so it can pick up @version values that come + # from old marshaled versions, which don't go through + # marshal_load. +segments+ is called in +initialize+ to "prime + # the pump" in normal cases. - def hash # :nodoc: - @version.hash + @segments ||= @version.scan(/[0-9a-z]+/i).map do |s| + /^\d+$/ =~ s ? s.to_i : s + end end ## - # Return a new version object where the next to the last revision number is - # one greater. (e.g. 5.3.1 => 5.4) - # - # Pre-release (alpha) parts are ignored. (e.g 5.3.1.b2 => 5.4) + # A recommended version for use with a ~> Requirement. - def bump - parts = parse_parts_from_version_string - parts.pop while parts.any? { |part| part.alpha? } - parts.pop if parts.size > 1 - parts[-1] = parts[-1].succ - self.class.new(parts.join(".")) - end + def spermy_recommendation + segments = self.segments.dup - def parse_parts_from_version_string # :nodoc: - @version.to_s.scan(/[0-9a-z]+/i).map { |s| Part.new(s) } - end + segments.pop while segments.any? { |s| String === s } + segments.pop while segments.size > 2 + segments.push 0 while segments.size < 2 - def pretty_print(q) # :nodoc: - q.text "Gem::Version.new(#{@version.inspect})" + "~> #{segments.join(".")}" end - #:stopdoc: + ## + # Compares this version with +other+ returning -1, 0, or 1 if the other + # version is larger, the same, or smaller than this one. - require 'rubygems/requirement' + def <=> other + return 1 unless other # HACK: comparable with nil? why? + return nil unless self.class === other - ## - # Gem::Requirement's original definition is nested in Version. - # Although an inappropriate place, current gems specs reference the nested - # class name explicitly. To remain compatible with old software loading - # gemspecs, we leave a copy of original definition in Version, but define an - # alias Gem::Requirement for use everywhere else. + lhsize = segments.size + rhsize = other.segments.size + limit = (lhsize > rhsize ? lhsize : rhsize) - 1 - Requirement = ::Gem::Requirement + 0.upto(limit) do |i| + lhs, rhs = segments[i] || 0, other.segments[i] || 0 - # :startdoc: + return -1 if String === lhs && Numeric === rhs + return 1 if Numeric === lhs && String === rhs + return lhs <=> rhs if lhs != rhs + end + return 0 + end end - diff --git a/lib/rubygems/version_option.rb b/lib/rubygems/version_option.rb index a69b732f78..a3de4dc9e7 100644 --- a/lib/rubygems/version_option.rb +++ b/lib/rubygems/version_option.rb @@ -6,10 +6,14 @@ require 'rubygems' +## # Mixin methods for --version and --platform Gem::Command options. + module Gem::VersionOption + ## # Add the --platform option to the option parser. + def add_platform_option(task = command, *wrap) OptionParser.accept Gem::Platform do |value| if value == Gem::Platform::RUBY then @@ -31,7 +35,19 @@ module Gem::VersionOption end end + ## + # Add the --prerelease option to the option parser. + + def add_prerelease_option(*wrap) + add_option("--[no-]prerelease", + "Allow prerelease versions of a gem", *wrap) do |value, options| + options[:prerelease] = value + end + end + + ## # Add the --version option to the option parser. + def add_version_option(task = command, *wrap) OptionParser.accept Gem::Requirement do |value| Gem::Requirement.new value |