1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
|
# frozen_string_literal: true
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
require_relative "optparse"
require_relative "requirement"
require_relative "user_interaction"
##
# Base class for all Gem commands. When creating a new gem command, define
# #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 do |value|
value.to_sym
end
##
# The name of the command.
attr_reader :command
##
# The options for the command.
attr_reader :options
##
# The default options for the command.
attr_accessor :defaults
##
# The name of the command for command-line invocation.
attr_accessor :program_name
##
# A short description of the command.
attr_accessor :summary
##
# Arguments used when building gems
def self.build_args
@build_args ||= []
end
def self.build_args=(value)
@build_args = value
end
def self.common_options
@common_options ||= []
end
def self.add_common_option(*args, &handler)
Gem::Command.common_options << [args, handler]
end
def self.extra_args
@extra_args ||= []
end
def self.extra_args=(value)
case value
when Array
@extra_args = value
when String
@extra_args = value.split(" ")
end
end
##
# Return an array of extra arguments for the command. The extra arguments
# come from the gem configuration file read at program startup.
def self.specific_extra_args(cmd)
specific_extra_args_hash[cmd]
end
##
# Add a list of extra arguments for the given command. +args+ may be an
# 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
specific_extra_args_hash[cmd] = args
end
##
# Accessor for the specific extra args hash (self initializing).
def self.specific_extra_args_hash
@specific_extra_args_hash ||= Hash.new do |h,k|
h[k] = Array.new
end
end
##
# Initializes a generic gem command named +command+. +summary+ is a short
# description displayed in `gem help commands`. +defaults+ are the default
# options. Defaults should be mirrored in #defaults_str, unless there are
# none.
#
# When defining a new command subclass, use add_option to add command-line
# switches.
#
# Unhandled arguments (gem names, files, etc.) are left in
# <tt>options[:args]</tt>.
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] = [] }
@deprecated_options = { command => {} }
@parser = nil
@when_invoked = nil
end
##
# True if +long+ begins with the characters from +short+.
def begins?(long, short)
return false if short.nil?
long[0, short.length] == short
end
##
# Override to provide command handling.
#
# #options will be filled in with your parsed options, unparsed options will
# be left in <tt>options[:args]</tt>.
#
# See also: #get_all_gem_names, #get_one_gem_name,
# #get_one_optional_argument
def execute
raise Gem::Exception, "generic command has no actions"
end
##
# Display to the user that a gem couldn't be found and reasons why
#--
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
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
##
# Get all gem names from the command line.
def get_all_gem_names
args = options[:args]
if args.nil? || args.empty?
raise Gem::CommandLineError,
"Please specify at least one gem name (e.g. gem build GEMNAME)"
end
args.select {|arg| arg !~ /^-/ }
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|
if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name
[$1, $2]
else
[name]
end
end
end
##
# 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]
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
raise Gem::CommandLineError,
"Too many gem names (#{args.join(', ')}); please specify only one"
end
args.first
end
##
# Get a single optional argument from the command line. If more than one
# argument is given, return only the first. Return nil if none are given.
def get_one_optional_argument
args = options[:args] || []
args.first
end
##
# Override to provide details of the arguments a command takes. It should
# return a left-justified string, one argument per line.
#
# For example:
#
# def usage
# "#{program_name} FILE [FILE ...]"
# end
#
# def arguments
# "FILE name of file to find"
# end
def arguments
""
end
##
# Override to display the default values of the command options. (similar to
# +arguments+, but displays the default values).
#
# For example:
#
# def defaults_str
# --no-gems-first --no-all
# end
def defaults_str
""
end
##
# Override to display a longer description of what this command does.
def description
nil
end
##
# Override to display the usage for an individual gem command.
#
# The text "[options]" is automatically appended to the usage text.
def usage
program_name
end
##
# Display the help message for the command.
def show_help
parser.program_name = usage
say parser
end
##
# 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
options[:build_args] = build_args
if options[:silent]
old_ui = self.ui
self.ui = ui = Gem::SilentUI.new
end
if options[:help]
show_help
elsif @when_invoked
@when_invoked.call options
else
execute
end
ensure
if ui
self.ui = old_ui
ui.close
end
end
##
# Call the given block when invoked.
#
# Normal command invocations just executes the +execute+ method of the
# command. Specifying an invocation block allows the test methods to
# override the normal action of a command to determine that it has been
# invoked correctly.
def when_invoked(&block)
@when_invoked = block
end
##
# Add a command-line option and handler to the command.
#
# 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.
#
# If the first argument of add_option is a Symbol, it's used to group
# options in output. See `gem help list` for an example.
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
##
# Remove previously defined command-line argument +name+.
def remove_option(name)
@option_groups.each do |_, option_list|
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|
if 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
end
##
# Merge a set of command options with the set of default options (without
# modifying the default option hash).
def merge_options(new_options)
@options = @defaults.clone
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
end
##
# Handle the given list of arguments by parsing them and recording the
# results.
def handle_options(args)
args = add_extra_args(args)
check_deprecated_options(args)
@options = Marshal.load Marshal.dump @defaults # deep copy
parser.parse!(args)
@options[:args] = args
end
##
# Adds extra args from ~/.gemrc
def add_extra_args(args)
result = []
s_extra = Gem::Command.specific_extra_args(@command)
extra = Gem::Command.extra_args + s_extra
until extra.empty? do
ex = []
ex << extra.shift
ex << extra.shift if extra.first.to_s =~ /^[^-]/ # rubocop:disable Performance/StartWith
result << ex if handles?(ex)
end
result.flatten!
result.concat(args)
result
end
def deprecated?
false
end
private
def option_is_deprecated?(option)
@deprecated_options[command].has_key?(option)
end
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.split("\n").each 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|
@parser.separator nil
configure_options group_name, option_list
end
end
##
# Adds a section with +title+ and +content+ to the parser help view. Used
# for adding command arguments and default arguments.
def add_parser_run_info(title, content)
return if content.empty?
@parser.separator nil
@parser.separator " #{title}:"
content.split(/\n/).each do |line|
@parser.separator " #{line}"
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|
@parser.separator " #{line.strip}"
end
end
##
# Create on demand parser.
def parser
create_option_parser if @parser.nil?
@parser
end
##
# Creates an option parser and fills it in with the help info for the
# command.
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? || option_list.empty?
header = header.to_s.empty? ? "" : "#{header} "
@parser.separator " #{header}Options:"
option_list.each do |args, handler|
@parser.on(*args) do |value|
handler.call(value, @options)
end
end
@parser.separator ""
end
##
# Wraps +text+ to +width+
def wrap(text, width) # :doc:
text.gsub(/(.{1,#{width}})( +|$\n?)|(.{1,#{width}})/, "\\1\\3\n")
end
# ----------------------------------------------------------------
# Add the options common to all commands.
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|
# Set us to "really verbose" so the progress meter works
if Gem.configuration.verbose && value
Gem.configuration.verbose = 1
else
Gem.configuration.verbose = value
end
end
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
end
add_common_option("--backtrace",
"Show stack backtrace on errors") do
end
add_common_option("--debug",
"Turn on Ruby debugging") do
end
add_common_option("--norc",
"Avoid loading any .gemrc file") do
end
# :stopdoc:
HELP = <<-HELP.freeze
RubyGems is a package manager for Ruby.
Usage:
gem -h/--help
gem -v/--version
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 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:
https://guides.rubygems.org
HELP
# :startdoc:
end
##
# \Commands will be placed in this namespace
module Gem::Commands
end
|