Skip to content

Commit 3ab676c

Browse files
committed
Merge pull request #243 from JacobEvelyn/multiple-flags
Basic implementation of :multiple option for flags
2 parents f7f4371 + 94dc87e commit 3ab676c

File tree

6 files changed

+85
-16
lines changed

6 files changed

+85
-16
lines changed

lib/gli/commands/help_modules/options_formatter.rb

+4-6
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,10 @@ def format
2424

2525
def description_with_default(option)
2626
if option.kind_of? Flag
27-
required = if option.required?
28-
'required, '
29-
else
30-
''
31-
end
32-
String(option.description) + " (#{required}default: #{option.safe_default_value || 'none'})"
27+
required = option.required? ? 'required, ' : ''
28+
multiple = option.multiple? ? 'may be used more than once, ' : ''
29+
30+
String(option.description) + " (#{required}#{multiple}default: #{option.safe_default_value || 'none'})"
3331
else
3432
String(option.description) + (option.default_value ? " (default: enabled)" : "")
3533
end

lib/gli/dsl.rb

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def default_value(val); @next_default_value = val; end
7575
# +:arg_name+:: the arg name, instead of using #arg_name
7676
# +:must_match+:: A regexp that the flag's value must match or an array of allowable values
7777
# +:type+:: A Class (or object you passed to GLI::App#accept) to trigger type coversion
78+
# +:multiple+:: if true, flag may be used multiple times and values are stored in an array
7879
#
7980
# Example:
8081
#

lib/gli/flag.rb

+23-2
Original file line numberDiff line numberDiff line change
@@ -24,30 +24,51 @@ class Flag < CommandLineOption # :nodoc:
2424
# :must_match:: a regexp that the flag's value must match
2525
# :type:: a class to convert the value to
2626
# :required:: if true, this flag must be specified on the command line
27+
# :multiple:: if true, flag may be used multiple times and values are stored in an array
2728
# :mask:: if true, the default value of this flag will not be output in the help.
2829
# This is useful for password flags where you might not want to show it
2930
# on the command-line.
3031
def initialize(names,options)
3132
super(names,options)
3233
@argument_name = options[:arg_name] || "arg"
33-
@default_value = options[:default_value]
3434
@must_match = options[:must_match]
3535
@type = options[:type]
3636
@mask = options[:mask]
3737
@required = options[:required]
38+
@multiple = options[:multiple]
3839
end
3940

4041
# True if this flag is required on the command line
4142
def required?
4243
@required
4344
end
4445

46+
# True if the flag may be used multiple times.
47+
def multiple?
48+
@multiple
49+
end
4550

4651
def safe_default_value
4752
if @mask
4853
"********"
4954
else
50-
default_value
55+
# This uses @default_value instead of the `default_value` method because
56+
# this method is only used for display, and for flags that may be passed
57+
# multiple times, we want to display whatever is set in the code as the
58+
# the default, or the string "none" rather than displaying an empty
59+
# array.
60+
@default_value
61+
end
62+
end
63+
64+
# The default value for this flag. Uses the value passed if one is set;
65+
# otherwise uses `[]` if the flag support multiple arguments and `nil` if
66+
# it does not.
67+
def default_value
68+
if @default_value
69+
@default_value
70+
elsif @multiple
71+
[]
5172
end
5273
end
5374

lib/gli/option_parser_factory.rb

+9-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,15 @@ def self.setup_options(opts,tokens,options)
5858
tokens.each do |ignore,token|
5959
opts.on(*token.arguments_for_option_parser) do |arg|
6060
token.names_and_aliases.each do |name|
61-
options[name] = arg
62-
options[name.to_sym] = arg
61+
if token.kind_of?(Flag) && token.multiple?
62+
options[name] ||= []
63+
options[name.to_sym] ||= []
64+
options[name] << arg
65+
options[name.to_sym] << arg
66+
else
67+
options[name] = arg
68+
options[name.to_sym] = arg
69+
end
6370
end
6471
end
6572
end

test/tc_gli.rb

+42
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,48 @@ def test_that_we_can_add_new_casts_for_flags
717717
assert_equal 'crud',@baz.value
718718
end
719719

720+
def test_that_flags_can_be_used_multiple_times
721+
@app.reset
722+
@app.flag :flag, :multiple => true
723+
@app.command :foo do |c|
724+
c.action do |options, _, _|
725+
@flag = options[:flag]
726+
end
727+
end
728+
729+
assert_equal 0,@app.run(%w(--flag 1 --flag=2 --flag 3 foo)),@fake_stderr.to_s
730+
731+
assert_equal ['1','2','3'],@flag
732+
end
733+
734+
def test_that_multiple_use_flags_are_empty_arrays_by_default
735+
@app.reset
736+
@app.flag :flag, :multiple => true
737+
@app.command :foo do |c|
738+
c.action do |options, _, _|
739+
@flag = options[:flag]
740+
end
741+
end
742+
743+
assert_equal 0,@app.run(['foo']),@fake_stderr.to_s
744+
745+
assert_equal [],@flag
746+
end
747+
748+
def test_that_multiple_use_flags_can_take_other_defaults
749+
@app.reset
750+
@app.flag :flag, :multiple => true, :default_value => ['1']
751+
@app.command :foo do |c|
752+
c.action do |options, _, _|
753+
@flag = options[:flag]
754+
end
755+
end
756+
757+
assert_equal 0,@app.run(['foo']),@fake_stderr.to_s
758+
759+
assert_equal ['1'],@flag
760+
end
761+
720762
def test_that_we_mutate_ARGV_by_default
721763
@app.reset
722764
@app.flag :f

test/tc_help.rb

+6-6
Original file line numberDiff line numberDiff line change
@@ -302,20 +302,20 @@ def a_GLI_app(omit_options=false)
302302
unless omit_options
303303
flags.each do |(description,arg,flag_names)|
304304
desc description
305-
arg_name arg
306-
flag flag_names
305+
arg_name arg
306+
flag flag_names
307307
end
308308

309309
switches.each do |(description,switch_names)|
310310
desc description
311-
switch switch_names
311+
switch switch_names
312312
end
313313
end
314314

315315
commands.each do |(description,command_names)|
316316
desc description
317-
command command_names do |c|
318-
c.action {}
317+
command command_names do |c|
318+
c.action {}
319319
end
320320
end
321321
end
@@ -363,7 +363,7 @@ def refute_output_contained(string_or_regexp,desc='')
363363
def any_option
364364
('a'..'z').to_a[@option_index].tap { @option_index += 1 }
365365
end
366-
366+
367367
def any_long_option
368368
Faker::Lorem.words(10)[rand(10)]
369369
end

0 commit comments

Comments
 (0)