diff options
Diffstat (limited to 'lib/rubygems/command.rb')
| -rw-r--r-- | lib/rubygems/command.rb | 326 |
1 files changed, 232 insertions, 94 deletions
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb index 3491937358..d38363f293 100644 --- a/lib/rubygems/command.rb +++ b/lib/rubygems/command.rb @@ -1,23 +1,27 @@ +# frozen_string_literal: true + #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ -require 'optparse' -require 'rubygems/user_interaction' +require_relative "vendored_optparse" +require_relative "requirement" +require_relative "user_interaction" ## # Base class for all Gem commands. When creating a new gem command, define -# #new, #execute, #arguments, #defaults_str, #description and #usage +# #initialize, #execute, #arguments, #defaults_str, #description and #usage # (as appropriate). See the above mentioned methods for details. # # A very good example to look at is Gem::Commands::ContentsCommand class Gem::Command - include Gem::UserInteraction + Gem::OptionParser.accept Symbol, &:to_sym + ## # The name of the command. @@ -71,7 +75,7 @@ class Gem::Command when Array @extra_args = value when String - @extra_args = value.split + @extra_args = value.split(" ") end end @@ -88,7 +92,7 @@ class Gem::Command # array or a string to be split on white space. def self.add_specific_extra_args(cmd,args) - args = args.split(/\s+/) if args.kind_of? String + args = args.split(/\s+/) if args.is_a? String specific_extra_args_hash[cmd] = args end @@ -113,13 +117,14 @@ class Gem::Command # Unhandled arguments (gem names, files, etc.) are left in # <tt>options[:args]</tt>. - def initialize(command, summary=nil, defaults={}) + def initialize(command, summary = nil, defaults = {}) @command = command @summary = summary @program_name = "gem #{command}" @defaults = defaults @options = defaults.dup - @option_groups = Hash.new { |h,k| h[k] = [] } + @option_groups = Hash.new {|h,k| h[k] = [] } + @deprecated_options = { command => {} } @parser = nil @when_invoked = nil end @@ -146,14 +151,31 @@ class Gem::Command end ## - # # Display to the user that a gem couldn't be found and reasons why - def show_lookup_failure(gem_name, version, errors=nil) - if errors and !errors.empty? - alert_error "Could not find a valid gem '#{gem_name}' (#{version}), here is why:" - errors.each { |x| say " #{x.wordy}" } + #-- + + def show_lookup_failure(gem_name, version, errors, suppress_suggestions = false, required_by = nil) + gem = "'#{gem_name}' (#{version})" + msg = String.new "Could not find a valid gem #{gem}" + + if errors && !errors.empty? + msg << ", here is why:\n" + errors.each {|x| msg << " #{x.wordy}\n" } else - alert_error "Could not find a valid gem '#{gem_name}' (#{version}) in any repository" + if required_by && gem != required_by + msg << " (required by #{required_by}) in any repository" + else + msg << " in any repository" + end + end + + alert_error msg + + unless suppress_suggestions + suggestions = Gem::SpecFetcher.fetcher.suggest_gems_from_name(gem_name, :latest, 10) + unless suggestions.empty? + alert_error "Possible alternatives: #{suggestions.join(", ")}" + end end end @@ -163,12 +185,31 @@ class Gem::Command def get_all_gem_names args = options[:args] - if args.nil? or args.empty? then + if args.nil? || args.empty? raise Gem::CommandLineError, "Please specify at least one gem name (e.g. gem build GEMNAME)" end - gem_names = args.select { |arg| arg !~ /^-/ } + args.reject {|arg| arg.start_with?("-") } + end + + ## + # Get all [gem, version] from the command line. + # + # An argument in the form gem:ver is pull apart into the gen name and version, + # respectively. + def get_all_gem_names_and_versions + get_all_gem_names.map do |name| + extract_gem_name_and_version(name) + end + end + + def extract_gem_name_and_version(name) # :nodoc: + if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name + [$1, $2] + else + [name] + end end ## @@ -178,14 +219,14 @@ class Gem::Command def get_one_gem_name args = options[:args] - if args.nil? or args.empty? then + if args.nil? || args.empty? raise Gem::CommandLineError, "Please specify a gem name on the command line (e.g. gem build GEMNAME)" end - if args.size > 1 then + if args.size > 1 raise Gem::CommandLineError, - "Too many gem names (#{args.join(', ')}); please specify only one" + "Too many gem names (#{args.join(", ")}); please specify only one" end args.first @@ -260,15 +301,35 @@ class Gem::Command # Invoke the command with the given list of arguments. def invoke(*args) + invoke_with_build_args args, nil + end + + ## + # Invoke the command with the given list of normal arguments + # and additional build arguments. + + def invoke_with_build_args(args, build_args) handle_options args - if options[:help] then + options[:build_args] = build_args + + if options[:silent] + old_ui = ui + self.ui = ui = Gem::SilentUI.new + end + + if options[:help] show_help - elsif @when_invoked then + elsif @when_invoked @when_invoked.call options else execute end + ensure + if ui + self.ui = old_ui + ui.close + end end ## @@ -286,7 +347,7 @@ class Gem::Command ## # Add a command-line option and handler to the command. # - # See OptionParser#make_switch for an explanation of +opts+. + # See Gem::OptionParser#make_switch for an explanation of +opts+. # # +handler+ will be called with two values, the value of the argument and # the options hash. @@ -297,6 +358,8 @@ class Gem::Command def add_option(*opts, &handler) # :yields: value, options group_name = Symbol === opts.first ? opts.shift : :options + raise "Do not pass an empty string in opts" if opts.include?("") + @option_groups[group_name] << [opts, handler] end @@ -305,7 +368,49 @@ class Gem::Command def remove_option(name) @option_groups.each do |_, option_list| - option_list.reject! { |args, _| args.any? { |x| x =~ /^#{name}/ } } + option_list.reject! {|args, _| args.any? {|x| x.is_a?(String) && x =~ /^#{name}/ } } + end + end + + ## + # Mark a command-line option as deprecated, and optionally specify a + # deprecation horizon. + # + # Note that with the current implementation, every version of the option needs + # to be explicitly deprecated, so to deprecate an option defined as + # + # add_option('-t', '--[no-]test', 'Set test mode') do |value, options| + # # ... stuff ... + # end + # + # you would need to explicitly add a call to `deprecate_option` for every + # version of the option you want to deprecate, like + # + # deprecate_option('-t') + # deprecate_option('--test') + # deprecate_option('--no-test') + + def deprecate_option(name, version: nil, extra_msg: nil) + @deprecated_options[command].merge!({ name => { "rg_version_to_expire" => version, "extra_msg" => extra_msg } }) + end + + def check_deprecated_options(options) + options.each do |option| + next unless option_is_deprecated?(option) + deprecation = @deprecated_options[command][option] + version_to_expire = deprecation["rg_version_to_expire"] + + deprecate_option_msg = if version_to_expire + "The \"#{option}\" option has been deprecated and will be removed in Rubygems #{version_to_expire}." + else + "The \"#{option}\" option has been deprecated and will be removed in future versions of Rubygems." + end + + extra_msg = deprecation["extra_msg"] + + deprecate_option_msg += " #{extra_msg}" if extra_msg + + alert_warning(deprecate_option_msg) end end @@ -315,19 +420,17 @@ class Gem::Command def merge_options(new_options) @options = @defaults.clone - new_options.each do |k,v| @options[k] = v end + new_options.each {|k,v| @options[k] = v } end ## # True if the command handles the given argument list. def handles?(args) - begin - parser.parse!(args.dup) - return true - rescue - return false - end + parser.parse!(args.dup) + true + rescue StandardError + false end ## @@ -336,7 +439,8 @@ class Gem::Command def handle_options(args) args = add_extra_args(args) - @options = @defaults.clone + check_deprecated_options(args) + @options = Marshal.load Marshal.dump @defaults # deep copy parser.parse!(args) @options[:args] = args end @@ -353,7 +457,7 @@ class Gem::Command until extra.empty? do ex = [] ex << extra.shift - ex << extra.shift if extra.first.to_s =~ /^[^-]/ + ex << extra.shift if /^[^-]/.match?(extra.first.to_s) result << ex if handles?(ex) end @@ -362,81 +466,106 @@ class Gem::Command result end - private + def deprecated? + false + end - ## - # Create on demand parser. + private - def parser - create_option_parser if @parser.nil? - @parser + def option_is_deprecated?(option) + @deprecated_options[command].key?(option) end - def create_option_parser - @parser = OptionParser.new + def add_parser_description # :nodoc: + return unless description + + formatted = description.split("\n\n").map do |chunk| + wrap chunk, 80 - 4 + end.join "\n" @parser.separator nil + @parser.separator " Description:" + formatted.each_line do |line| + @parser.separator " #{line.rstrip}" + end + end + + def add_parser_options # :nodoc: + @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| + @option_groups.sort_by {|n,_| n.to_s }.each do |group_name, option_list| @parser.separator nil configure_options group_name, option_list end + end - @parser.separator nil - configure_options "Common", Gem::Command.common_options + ## + # Adds a section with +title+ and +content+ to the parser help view. Used + # for adding command arguments and default arguments. - unless arguments.empty? - @parser.separator nil - @parser.separator " Arguments:" - arguments.split(/\n/).each do |arg_desc| - @parser.separator " #{arg_desc}" - end + def add_parser_run_info(title, content) + return if content.empty? + + @parser.separator nil + @parser.separator " #{title}:" + content.each_line do |line| + @parser.separator " #{line.rstrip}" end + end + + def add_parser_summary # :nodoc: + return unless @summary @parser.separator nil @parser.separator " Summary:" - wrap(@summary, 80 - 4).split("\n").each do |line| + wrap(@summary, 80 - 4).each_line do |line| @parser.separator " #{line.strip}" end + end + + ## + # Create on demand parser. - if description then - formatted = description.split("\n\n").map do |chunk| - wrap chunk, 80 - 4 - end.join "\n" + def parser + create_option_parser if @parser.nil? + @parser + end - @parser.separator nil - @parser.separator " Description:" - formatted.split("\n").each do |line| - @parser.separator " #{line.rstrip}" - end - end + ## + # Creates an option parser and fills it in with the help info for the + # command. - unless defaults_str.empty? - @parser.separator nil - @parser.separator " Defaults:" - defaults_str.split(/\n/).each do |line| - @parser.separator " #{line}" - end - end + def create_option_parser + @parser = Gem::OptionParser.new + + add_parser_options + + @parser.separator nil + configure_options "Common", Gem::Command.common_options + + add_parser_run_info "Arguments", arguments + add_parser_summary + add_parser_description + add_parser_run_info "Defaults", defaults_str end def configure_options(header, option_list) - return if option_list.nil? or option_list.empty? + return if option_list.nil? || option_list.empty? - header = header.to_s.empty? ? '' : "#{header} " + header = header.to_s.empty? ? "" : "#{header} " @parser.separator " #{header}Options:" option_list.each do |args, handler| - dashes = args.select { |arg| arg =~ /^-/ } @parser.on(*args) do |value| handler.call(value, @options) end end - @parser.separator '' + @parser.separator "" end ## @@ -449,78 +578,87 @@ class Gem::Command # ---------------------------------------------------------------- # Add the options common to all commands. - add_common_option('-h', '--help', - 'Get help on this command') do |value, options| + add_common_option("-h", "--help", + "Get help on this command") do |_value, options| options[:help] = true end - add_common_option('-V', '--[no-]verbose', - 'Set the verbose level of output') do |value, options| + add_common_option("-V", "--[no-]verbose", + "Set the verbose level of output") do |value, _options| # Set us to "really verbose" so the progress meter works - if Gem.configuration.verbose and value then + if Gem.configuration.verbose && value Gem.configuration.verbose = 1 else Gem.configuration.verbose = value end end - add_common_option('-q', '--quiet', 'Silence commands') do |value, options| + add_common_option("-q", "--quiet", "Silence command progress meter") do |_value, _options| Gem.configuration.verbose = false end + add_common_option("--silent", + "Silence RubyGems output") do |_value, options| + options[:silent] = true + end + # Backtrace and config-file are added so they show up in the help # commands. Both options are actually handled before the other # options get parsed. - add_common_option('--config-file FILE', - 'Use this config file instead of default') do + add_common_option("--config-file FILE", + "Use this config file instead of default") do + end + + add_common_option("--backtrace", + "Show stack backtrace on errors") do end - add_common_option('--backtrace', - 'Show stack backtrace on errors') do + add_common_option("--debug", + "Turn on Ruby debugging") do end - add_common_option('--debug', - 'Turn on Ruby debugging') do + add_common_option("--norc", + "Avoid loading any .gemrc file") do end # :stopdoc: HELP = <<-HELP -RubyGems is a sophisticated package manager for Ruby. This is a -basic help message containing pointers to more information. +RubyGems is a package manager for Ruby. Usage: gem -h/--help gem -v/--version - gem command [arguments...] [options...] + gem [global options...] command [arguments...] [options...] + + Global options: + -C PATH run as if gem was started in <PATH> + instead of the current working directory Examples: gem install rake gem list --local gem build package.gemspec + gem push package-0.0.1.gem 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 gem_dependencies gem dependencies file guide + gem help platforms gem platforms guide 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 + https://guides.rubygems.org HELP # :startdoc: - end ## -# This is where Commands will be placed in the namespace +# \Commands will be placed in this namespace module Gem::Commands end - |
