cri 2.11.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -71,8 +71,7 @@ module Cri
71
71
  end
72
72
 
73
73
  def message
74
- name = @definition[:long] ? '--' + @definition[:long] : '-' + @definition[:short]
75
- "invalid value #{value.inspect} for #{name} option"
74
+ "invalid value #{value.inspect} for #{@definition.formatted_name} option"
76
75
  end
77
76
  end
78
77
 
@@ -99,38 +98,26 @@ module Cri
99
98
  # @return [Hash] The already parsed options.
100
99
  attr_reader :options
101
100
 
102
- # @return [Array] The arguments that have already been parsed, including
103
- # the -- separator.
104
- attr_reader :raw_arguments
105
-
106
101
  # The options and arguments that have not yet been processed. If the
107
102
  # parser wasn’t stopped (using {#stop}), this list will be empty.
108
103
  #
109
104
  # @return [Array] The not yet parsed options and arguments.
110
105
  attr_reader :unprocessed_arguments_and_options
111
106
 
112
- # Parses the command-line arguments. See the instance `parse` method for
113
- # details.
114
- #
115
- # @param [Array<String>] arguments_and_options An array containing the
116
- # command-line arguments (will probably be `ARGS` for a root command)
117
- #
118
- # @param [Array<Hash>] definitions An array of option definitions
119
- #
120
- # @return [Cri::OptionParser] The option parser self
121
- def self.parse(arguments_and_options, definitions)
122
- new(arguments_and_options, definitions).run
123
- end
124
-
125
107
  # Creates a new parser with the given options/arguments and definitions.
126
108
  #
127
109
  # @param [Array<String>] arguments_and_options An array containing the
128
110
  # command-line arguments (will probably be `ARGS` for a root command)
129
111
  #
130
- # @param [Array<Hash>] definitions An array of option definitions
131
- def initialize(arguments_and_options, definitions)
112
+ # @param [Array<Cri::OptionDefinition>] option_defns An array of option
113
+ # definitions
114
+ #
115
+ # @param [Array<Cri::ParamDefinition>] param_defns An array of parameter
116
+ # definitions
117
+ def initialize(arguments_and_options, option_defns, param_defns)
132
118
  @unprocessed_arguments_and_options = arguments_and_options.dup
133
- @definitions = definitions
119
+ @option_defns = option_defns
120
+ @param_defns = param_defns
134
121
 
135
122
  @options = {}
136
123
  @raw_arguments = []
@@ -139,17 +126,6 @@ module Cri
139
126
  @no_more_options = false
140
127
  end
141
128
 
142
- # Returns the arguments that have already been parsed.
143
- #
144
- # If the parser was stopped before it finished, this will not contain all
145
- # options and `unprocessed_arguments_and_options` will contain what is
146
- # left to be processed.
147
- #
148
- # @return [Array] The already parsed arguments.
149
- def arguments
150
- @raw_arguments.reject { |a| a == '--' }.freeze
151
- end
152
-
153
129
  # @return [Boolean] true if the parser is running, false otherwise.
154
130
  def running?
155
131
  @running
@@ -200,10 +176,16 @@ module Cri
200
176
  @running = false
201
177
  end
202
178
 
179
+ # @return [Cri::ArgumentList] The list of arguments that have already been
180
+ # parsed, excluding the -- separator.
181
+ def arguments
182
+ ArgumentList.new(@raw_arguments, @param_defns)
183
+ end
184
+
203
185
  private
204
186
 
205
187
  def add_defaults
206
- @definitions.each { |d| add_default_option(d) }
188
+ @option_defns.each { |d| add_default_option(d) }
207
189
  end
208
190
 
209
191
  def handle_dashdash(elem)
@@ -222,20 +204,20 @@ module Cri
222
204
  end
223
205
 
224
206
  # Find definition
225
- definition = @definitions.find { |d| d[:long] == option_key }
226
- raise IllegalOptionError.new(option_key) if definition.nil?
207
+ option_defn = @option_defns.find { |d| d.long == option_key }
208
+ raise IllegalOptionError.new(option_key) if option_defn.nil?
227
209
 
228
- if %i[required optional].include?(definition[:argument])
210
+ if %i[required optional].include?(option_defn.argument)
229
211
  # Get option value if necessary
230
212
  if option_value.nil?
231
- option_value = find_option_value(definition, option_key)
213
+ option_value = find_option_value(option_defn, option_key)
232
214
  end
233
215
 
234
216
  # Store option
235
- add_option(definition, option_value)
217
+ add_option(option_defn, option_value)
236
218
  else
237
219
  # Store option
238
- add_option(definition, true)
220
+ add_option(option_defn, true)
239
221
  end
240
222
  end
241
223
 
@@ -246,28 +228,28 @@ module Cri
246
228
  # For each key
247
229
  option_keys.each do |option_key|
248
230
  # Find definition
249
- definition = @definitions.find { |d| d[:short] == option_key }
250
- raise IllegalOptionError.new(option_key) if definition.nil?
231
+ option_defn = @option_defns.find { |d| d.short == option_key }
232
+ raise IllegalOptionError.new(option_key) if option_defn.nil?
251
233
 
252
- if %i[required optional].include?(definition[:argument])
234
+ if %i[required optional].include?(option_defn.argument)
253
235
  # Get option value
254
- option_value = find_option_value(definition, option_key)
236
+ option_value = find_option_value(option_defn, option_key)
255
237
 
256
238
  # Store option
257
- add_option(definition, option_value)
239
+ add_option(option_defn, option_value)
258
240
  else
259
241
  # Store option
260
- add_option(definition, true)
242
+ add_option(option_defn, true)
261
243
  end
262
244
  end
263
245
  end
264
246
 
265
- def find_option_value(definition, option_key)
247
+ def find_option_value(option_defn, option_key)
266
248
  option_value = @unprocessed_arguments_and_options.shift
267
249
  if option_value.nil? || option_value =~ /^-/
268
- if definition[:argument] == :optional && definition[:default]
269
- option_value = definition[:default]
270
- elsif definition[:argument] == :required
250
+ if option_defn.argument == :optional && option_defn.default
251
+ option_value = option_defn.default
252
+ elsif option_defn.argument == :required
271
253
  raise OptionRequiresAnArgumentError.new(option_key)
272
254
  else
273
255
  @unprocessed_arguments_and_options.unshift(option_value)
@@ -277,12 +259,12 @@ module Cri
277
259
  option_value
278
260
  end
279
261
 
280
- def add_option(definition, value, transform: true)
281
- key = key_for(definition)
262
+ def add_option(option_defn, value, transform: true)
263
+ key = key_for(option_defn)
282
264
 
283
- value = transform ? transform_value(definition, value) : value
265
+ value = transform ? transform_value(option_defn, value) : value
284
266
 
285
- if definition[:multiple]
267
+ if option_defn.multiple
286
268
  options[key] ||= []
287
269
  options[key] << value
288
270
  else
@@ -292,32 +274,32 @@ module Cri
292
274
  delegate&.option_added(key, value, self)
293
275
  end
294
276
 
295
- def add_default_option(definition)
296
- key = key_for(definition)
277
+ def add_default_option(option_defn)
278
+ key = key_for(option_defn)
297
279
  return if options.key?(key)
298
280
 
299
- value = definition[:default]
281
+ value = option_defn.default
300
282
  return unless value
301
283
 
302
- add_option(definition, value, transform: false)
284
+ add_option(option_defn, value, transform: false)
303
285
  end
304
286
 
305
- def transform_value(definition, value)
306
- transformer = definition[:transform]
287
+ def transform_value(option_defn, value)
288
+ transformer = option_defn.transform
307
289
 
308
290
  if transformer
309
291
  begin
310
292
  transformer.call(value)
311
293
  rescue StandardError
312
- raise IllegalOptionValueError.new(definition, value)
294
+ raise IllegalOptionValueError.new(option_defn, value)
313
295
  end
314
296
  else
315
297
  value
316
298
  end
317
299
  end
318
300
 
319
- def key_for(definition)
320
- (definition[:long] || definition[:short]).to_sym
301
+ def key_for(option_defn)
302
+ (option_defn.long || option_defn.short).to_sym
321
303
  end
322
304
 
323
305
  def add_argument(value)
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cri
4
+ # The definition of a parameter.
5
+ class ParamDefinition
6
+ attr_reader :name
7
+
8
+ def initialize(name:)
9
+ @name = name
10
+ end
11
+ end
12
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Cri
4
4
  # The current Cri version.
5
- VERSION = '2.11.0'
5
+ VERSION = '2.12.0'
6
6
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'helper'
4
+
5
+ module Cri
6
+ class ArgumentListTestCase < Cri::TestCase
7
+ def test_empty
8
+ args = Cri::ArgumentList.new([], [])
9
+
10
+ assert_equal([], args.to_a)
11
+ assert(args.empty?)
12
+ assert_equal(0, args.size)
13
+ assert_equal(nil, args[0])
14
+ assert_equal(nil, args[:abc])
15
+ end
16
+
17
+ def test_no_param_defns
18
+ args = Cri::ArgumentList.new(%w[a b c], [])
19
+
20
+ assert_equal(%w[a b c], args.to_a)
21
+ refute(args.empty?)
22
+ assert_equal(3, args.size)
23
+ assert_equal('a', args[0])
24
+ assert_equal('b', args[1])
25
+ assert_equal('c', args[2])
26
+ assert_equal(nil, args[3])
27
+ assert_equal(nil, args[:abc])
28
+ end
29
+
30
+ def test_enum
31
+ args = Cri::ArgumentList.new(%w[a b c], [])
32
+
33
+ assert_equal(%w[A B C], args.map(&:upcase))
34
+ end
35
+
36
+ def test_no_method_error
37
+ args = Cri::ArgumentList.new(%w[a b c], [])
38
+
39
+ refute args.respond_to?(:oink)
40
+ assert_raises(NoMethodError, 'x') do
41
+ args.oink
42
+ end
43
+ end
44
+
45
+ def test_dash_dash
46
+ args = Cri::ArgumentList.new(%w[a -- b -- c], [])
47
+
48
+ assert_equal(%w[a b c], args.to_a)
49
+ end
50
+
51
+ def test_one_param_defn_matched
52
+ param_defns = [Cri::ParamDefinition.new(name: 'filename')]
53
+ args = Cri::ArgumentList.new(%w[notbad.jpg], param_defns)
54
+
55
+ assert_equal(['notbad.jpg'], args.to_a)
56
+ assert_equal(1, args.size)
57
+ assert_equal('notbad.jpg', args[0])
58
+ assert_equal('notbad.jpg', args[:filename])
59
+ end
60
+
61
+ def test_one_param_defn_too_many
62
+ param_defns = [Cri::ParamDefinition.new(name: 'filename')]
63
+
64
+ exception = assert_raises(Cri::ArgumentList::ArgumentCountMismatchError) do
65
+ Cri::ArgumentList.new(%w[notbad.jpg verybad.jpg], param_defns)
66
+ end
67
+ assert_equal('incorrect number of arguments given: expected 1, but got 2', exception.message)
68
+ end
69
+
70
+ def test_one_param_defn_too_few
71
+ param_defns = [Cri::ParamDefinition.new(name: 'filename')]
72
+
73
+ exception = assert_raises(Cri::ArgumentList::ArgumentCountMismatchError) do
74
+ Cri::ArgumentList.new(%w[], param_defns)
75
+ end
76
+ assert_equal('incorrect number of arguments given: expected 1, but got 0', exception.message)
77
+ end
78
+ end
79
+ end
@@ -479,8 +479,8 @@ module Cri
479
479
 
480
480
  # Check option definitions
481
481
  assert_equal 1, cmd.option_definitions.size
482
- opt_def = cmd.option_definitions.to_a[0]
483
- assert_equal 'help', opt_def[:long]
482
+ opt_defn = cmd.option_definitions.to_a[0]
483
+ assert_equal 'help', opt_defn.long
484
484
 
485
485
  # Check subcommand
486
486
  assert_equal 1, cmd.subcommands.size
@@ -697,5 +697,22 @@ module Cri
697
697
 
698
698
  assert_equal "opts={:aaa=>\"test\"} args=--test,value\n", out
699
699
  end
700
+
701
+ def test_wrong_number_of_args
702
+ cmd = Cri::Command.define do
703
+ name 'publish'
704
+ param :filename
705
+ end
706
+
707
+ out, err = capture_io_while do
708
+ err = assert_raises SystemExit do
709
+ cmd.run([])
710
+ end
711
+ assert_equal 1, err.status
712
+ end
713
+
714
+ assert_equal [], lines(out)
715
+ assert_equal ['publish: incorrect number of arguments given: expected 1, but got 0'], lines(err)
716
+ end
700
717
  end
701
718
  end
@@ -49,7 +49,7 @@ module Cri
49
49
  { short: 'f', long: 'fff', desc: 'opt f', argument: :forbidden, multiple: false, hidden: true, block: nil, default: nil, transform: nil },
50
50
  ],
51
51
  )
52
- actual_option_definitions = Set.new(command.option_definitions)
52
+ actual_option_definitions = Set.new(command.option_definitions.map(&:to_h))
53
53
  assert_equal expected_option_definitions, actual_option_definitions
54
54
  end
55
55
 
@@ -84,7 +84,7 @@ module Cri
84
84
  { short: nil, long: 'long', desc: 'long', argument: :forbidden, multiple: false, hidden: false, block: nil, default: nil, transform: nil },
85
85
  ],
86
86
  )
87
- actual_option_definitions = Set.new(command.option_definitions)
87
+ actual_option_definitions = Set.new(command.option_definitions.map(&:to_h))
88
88
  assert_equal expected_option_definitions, actual_option_definitions
89
89
  end
90
90
 
@@ -109,7 +109,7 @@ module Cri
109
109
  { short: 'o', long: 'optional', desc: 'opt', argument: :optional, multiple: true, hidden: false, block: nil, default: nil, transform: nil },
110
110
  ],
111
111
  )
112
- actual_option_definitions = Set.new(command.option_definitions)
112
+ actual_option_definitions = Set.new(command.option_definitions.map(&:to_h))
113
113
  assert_equal expected_option_definitions, actual_option_definitions
114
114
  end
115
115
 
@@ -134,7 +134,7 @@ module Cri
134
134
  { short: 'o', long: 'optional', desc: 'opt', argument: :optional, multiple: false, hidden: true, block: nil, default: nil, transform: nil },
135
135
  ],
136
136
  )
137
- actual_option_definitions = Set.new(command.option_definitions)
137
+ actual_option_definitions = Set.new(command.option_definitions.map(&:to_h))
138
138
  assert_equal expected_option_definitions, actual_option_definitions
139
139
  end
140
140
 
@@ -240,5 +240,33 @@ module Cri
240
240
  command.run(%w[certainly])
241
241
  assert_equal 'certainly', $did_it_work
242
242
  end
243
+
244
+ def test_params
245
+ # Define
246
+ dsl = Cri::CommandDSL.new
247
+ dsl.instance_eval do
248
+ name 'moo'
249
+ usage 'dunno whatever'
250
+ summary 'does stuff'
251
+ description 'This command does a lot of stuff.'
252
+
253
+ param :foo
254
+ param :bar
255
+ param :qux
256
+
257
+ run do |_opts, args|
258
+ $args_num = { foo: args[0], bar: args[1], qux: args[2] }
259
+ $args_sym = { foo: args[:foo], bar: args[:bar], qux: args[:qux] }
260
+ end
261
+ end
262
+ command = dsl.command
263
+
264
+ # Run
265
+ $args_num = '???'
266
+ $args_sym = '???'
267
+ command.run(%w[a b c])
268
+ assert_equal({ foo: 'a', bar: 'b', qux: 'c' }, $args_num)
269
+ assert_equal({ foo: 'a', bar: 'b', qux: 'c' }, $args_sym)
270
+ end
243
271
  end
244
272
  end
@@ -5,431 +5,432 @@ require 'helper'
5
5
  module Cri
6
6
  class OptionParserTestCase < Cri::TestCase
7
7
  def test_parse_without_options
8
- input = %w[foo bar baz]
9
- definitions = []
8
+ input = %w[foo bar baz]
9
+ opt_defns = []
10
10
 
11
- parser = Cri::OptionParser.parse(input, definitions)
11
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
12
12
 
13
13
  assert_equal({}, parser.options)
14
- assert_equal(%w[foo bar baz], parser.arguments)
14
+ assert_equal(%w[foo bar baz], parser.arguments.to_a)
15
15
  end
16
16
 
17
17
  def test_parse_with_invalid_option
18
- input = %w[foo -x]
19
- definitions = []
18
+ input = %w[foo -x]
19
+ opt_defns = []
20
20
 
21
21
  assert_raises(Cri::OptionParser::IllegalOptionError) do
22
- Cri::OptionParser.parse(input, definitions)
22
+ Cri::OptionParser.new(input, opt_defns, []).run
23
23
  end
24
24
  end
25
25
 
26
26
  def test_parse_with_unused_options
27
- input = %w[foo]
28
- definitions = [
27
+ input = %w[foo]
28
+ opt_defns = [
29
29
  { long: 'aaa', short: 'a', argument: :forbidden },
30
- ]
30
+ ].map { |hash| make_opt_defn(hash) }
31
31
 
32
- parser = Cri::OptionParser.parse(input, definitions)
32
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
33
33
 
34
34
  assert(!parser.options[:aaa])
35
35
  end
36
36
 
37
37
  def test_parse_with_long_valueless_option
38
- input = %w[foo --aaa bar]
39
- definitions = [
38
+ input = %w[foo --aaa bar]
39
+ opt_defns = [
40
40
  { long: 'aaa', short: 'a', argument: :forbidden },
41
- ]
41
+ ].map { |hash| make_opt_defn(hash) }
42
42
 
43
- parser = Cri::OptionParser.parse(input, definitions)
43
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
44
44
 
45
45
  assert(parser.options[:aaa])
46
- assert_equal(%w[foo bar], parser.arguments)
46
+ assert_equal(%w[foo bar], parser.arguments.to_a)
47
47
  end
48
48
 
49
49
  def test_parse_with_long_valueful_option
50
- input = %w[foo --aaa xxx bar]
51
- definitions = [
50
+ input = %w[foo --aaa xxx bar]
51
+ opt_defns = [
52
52
  { long: 'aaa', short: 'a', argument: :required },
53
- ]
53
+ ].map { |hash| make_opt_defn(hash) }
54
54
 
55
- parser = Cri::OptionParser.parse(input, definitions)
55
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
56
56
 
57
57
  assert_equal({ aaa: 'xxx' }, parser.options)
58
- assert_equal(%w[foo bar], parser.arguments)
58
+ assert_equal(%w[foo bar], parser.arguments.to_a)
59
59
  end
60
60
 
61
61
  def test_parse_with_long_valueful_equalsign_option
62
- input = %w[foo --aaa=xxx bar]
63
- definitions = [
62
+ input = %w[foo --aaa=xxx bar]
63
+ opt_defns = [
64
64
  { long: 'aaa', short: 'a', argument: :required },
65
- ]
65
+ ].map { |hash| make_opt_defn(hash) }
66
66
 
67
- parser = Cri::OptionParser.parse(input, definitions)
67
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
68
68
 
69
69
  assert_equal({ aaa: 'xxx' }, parser.options)
70
- assert_equal(%w[foo bar], parser.arguments)
70
+ assert_equal(%w[foo bar], parser.arguments.to_a)
71
71
  end
72
72
 
73
73
  def test_parse_with_long_valueful_option_with_missing_value
74
- input = %w[foo --aaa]
75
- definitions = [
74
+ input = %w[foo --aaa]
75
+ opt_defns = [
76
76
  { long: 'aaa', short: 'a', argument: :required },
77
- ]
77
+ ].map { |hash| make_opt_defn(hash) }
78
78
 
79
79
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
80
- Cri::OptionParser.parse(input, definitions)
80
+ Cri::OptionParser.new(input, opt_defns, []).run
81
81
  end
82
82
  end
83
83
 
84
84
  def test_parse_with_two_long_valueful_options
85
- input = %w[foo --all --port 2]
86
- definitions = [
85
+ input = %w[foo --all --port 2]
86
+ opt_defns = [
87
87
  { long: 'all', short: 'a', argument: :required },
88
88
  { long: 'port', short: 'p', argument: :required },
89
- ]
89
+ ].map { |hash| make_opt_defn(hash) }
90
90
 
91
91
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
92
- Cri::OptionParser.parse(input, definitions)
92
+ Cri::OptionParser.new(input, opt_defns, []).run
93
93
  end
94
94
  end
95
95
 
96
96
  def test_parse_with_long_valueless_option_with_optional_value
97
- input = %w[foo --aaa]
98
- definitions = [
97
+ input = %w[foo --aaa]
98
+ opt_defns = [
99
99
  { long: 'aaa', short: 'a', argument: :optional },
100
- ]
100
+ ].map { |hash| make_opt_defn(hash) }
101
101
 
102
- parser = Cri::OptionParser.parse(input, definitions)
102
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
103
103
 
104
104
  assert(parser.options[:aaa])
105
- assert_equal(['foo'], parser.arguments)
105
+ assert_equal(['foo'], parser.arguments.to_a)
106
106
  end
107
107
 
108
108
  def test_parse_with_long_valueful_option_with_optional_value
109
- input = %w[foo --aaa xxx]
110
- definitions = [
109
+ input = %w[foo --aaa xxx]
110
+ opt_defns = [
111
111
  { long: 'aaa', short: 'a', argument: :optional },
112
- ]
112
+ ].map { |hash| make_opt_defn(hash) }
113
113
 
114
- parser = Cri::OptionParser.parse(input, definitions)
114
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
115
115
 
116
116
  assert_equal({ aaa: 'xxx' }, parser.options)
117
- assert_equal(['foo'], parser.arguments)
117
+ assert_equal(['foo'], parser.arguments.to_a)
118
118
  end
119
119
 
120
120
  def test_parse_with_long_valueless_option_with_optional_value_and_more_options
121
- input = %w[foo --aaa -b -c]
122
- definitions = [
121
+ input = %w[foo --aaa -b -c]
122
+ opt_defns = [
123
123
  { long: 'aaa', short: 'a', argument: :optional },
124
124
  { long: 'bbb', short: 'b', argument: :forbidden },
125
125
  { long: 'ccc', short: 'c', argument: :forbidden },
126
- ]
126
+ ].map { |hash| make_opt_defn(hash) }
127
127
 
128
- parser = Cri::OptionParser.parse(input, definitions)
128
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
129
129
 
130
130
  assert(parser.options[:aaa])
131
131
  assert(parser.options[:bbb])
132
132
  assert(parser.options[:ccc])
133
- assert_equal(['foo'], parser.arguments)
133
+ assert_equal(['foo'], parser.arguments.to_a)
134
134
  end
135
135
 
136
136
  def test_parse_with_short_valueless_options
137
- input = %w[foo -a bar]
138
- definitions = [
137
+ input = %w[foo -a bar]
138
+ opt_defns = [
139
139
  { long: 'aaa', short: 'a', argument: :forbidden },
140
- ]
140
+ ].map { |hash| make_opt_defn(hash) }
141
141
 
142
- parser = Cri::OptionParser.parse(input, definitions)
142
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
143
143
 
144
144
  assert(parser.options[:aaa])
145
- assert_equal(%w[foo bar], parser.arguments)
145
+ assert_equal(%w[foo bar], parser.arguments.to_a)
146
146
  end
147
147
 
148
148
  def test_parse_with_short_valueful_option_with_missing_value
149
- input = %w[foo -a]
150
- definitions = [
149
+ input = %w[foo -a]
150
+ opt_defns = [
151
151
  { long: 'aaa', short: 'a', argument: :required },
152
- ]
152
+ ].map { |hash| make_opt_defn(hash) }
153
153
 
154
154
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
155
- Cri::OptionParser.parse(input, definitions)
155
+ Cri::OptionParser.new(input, opt_defns, []).run
156
156
  end
157
157
  end
158
158
 
159
159
  def test_parse_with_short_combined_valueless_options
160
- input = %w[foo -abc bar]
161
- definitions = [
160
+ input = %w[foo -abc bar]
161
+ opt_defns = [
162
162
  { long: 'aaa', short: 'a', argument: :forbidden },
163
163
  { long: 'bbb', short: 'b', argument: :forbidden },
164
164
  { long: 'ccc', short: 'c', argument: :forbidden },
165
- ]
165
+ ].map { |hash| make_opt_defn(hash) }
166
166
 
167
- parser = Cri::OptionParser.parse(input, definitions)
167
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
168
168
 
169
169
  assert(parser.options[:aaa])
170
170
  assert(parser.options[:bbb])
171
171
  assert(parser.options[:ccc])
172
- assert_equal(%w[foo bar], parser.arguments)
172
+ assert_equal(%w[foo bar], parser.arguments.to_a)
173
173
  end
174
174
 
175
175
  def test_parse_with_short_combined_valueful_options_with_missing_value
176
- input = %w[foo -abc bar qux]
177
- definitions = [
176
+ input = %w[foo -abc bar qux]
177
+ opt_defns = [
178
178
  { long: 'aaa', short: 'a', argument: :required },
179
179
  { long: 'bbb', short: 'b', argument: :forbidden },
180
180
  { long: 'ccc', short: 'c', argument: :forbidden },
181
- ]
181
+ ].map { |hash| make_opt_defn(hash) }
182
182
 
183
- parser = Cri::OptionParser.parse(input, definitions)
183
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
184
184
 
185
185
  assert_equal('bar', parser.options[:aaa])
186
186
  assert(parser.options[:bbb])
187
187
  assert(parser.options[:ccc])
188
- assert_equal(%w[foo qux], parser.arguments)
188
+ assert_equal(%w[foo qux], parser.arguments.to_a)
189
189
  end
190
190
 
191
191
  def test_parse_with_two_short_valueful_options
192
- input = %w[foo -a -p 2]
193
- definitions = [
192
+ input = %w[foo -a -p 2]
193
+ opt_defns = [
194
194
  { long: 'all', short: 'a', argument: :required },
195
195
  { long: 'port', short: 'p', argument: :required },
196
- ]
196
+ ].map { |hash| make_opt_defn(hash) }
197
197
 
198
198
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
199
- Cri::OptionParser.parse(input, definitions)
199
+ Cri::OptionParser.new(input, opt_defns, []).run
200
200
  end
201
201
  end
202
202
 
203
203
  def test_parse_with_short_valueless_option_with_optional_value
204
- input = %w[foo -a]
205
- definitions = [
204
+ input = %w[foo -a]
205
+ opt_defns = [
206
206
  { long: 'aaa', short: 'a', argument: :optional },
207
- ]
207
+ ].map { |hash| make_opt_defn(hash) }
208
208
 
209
- parser = Cri::OptionParser.parse(input, definitions)
209
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
210
210
 
211
211
  assert(parser.options[:aaa])
212
- assert_equal(['foo'], parser.arguments)
212
+ assert_equal(['foo'], parser.arguments.to_a)
213
213
  end
214
214
 
215
215
  def test_parse_with_short_valueful_option_with_optional_value
216
- input = %w[foo -a xxx]
217
- definitions = [
216
+ input = %w[foo -a xxx]
217
+ opt_defns = [
218
218
  { long: 'aaa', short: 'a', argument: :optional },
219
- ]
219
+ ].map { |hash| make_opt_defn(hash) }
220
220
 
221
- parser = Cri::OptionParser.parse(input, definitions)
221
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
222
222
 
223
223
  assert_equal({ aaa: 'xxx' }, parser.options)
224
- assert_equal(['foo'], parser.arguments)
224
+ assert_equal(['foo'], parser.arguments.to_a)
225
225
  end
226
226
 
227
227
  def test_parse_with_short_valueless_option_with_optional_value_and_more_options
228
- input = %w[foo -a -b -c]
229
- definitions = [
228
+ input = %w[foo -a -b -c]
229
+ opt_defns = [
230
230
  { long: 'aaa', short: 'a', argument: :optional },
231
231
  { long: 'bbb', short: 'b', argument: :forbidden },
232
232
  { long: 'ccc', short: 'c', argument: :forbidden },
233
- ]
233
+ ].map { |hash| make_opt_defn(hash) }
234
234
 
235
- parser = Cri::OptionParser.parse(input, definitions)
235
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
236
236
 
237
237
  assert(parser.options[:aaa])
238
238
  assert(parser.options[:bbb])
239
239
  assert(parser.options[:ccc])
240
- assert_equal(['foo'], parser.arguments)
240
+ assert_equal(['foo'], parser.arguments.to_a)
241
241
  end
242
242
 
243
243
  def test_parse_with_single_hyphen
244
- input = %w[foo - bar]
245
- definitions = []
244
+ input = %w[foo - bar]
245
+ opt_defns = []
246
246
 
247
- parser = Cri::OptionParser.parse(input, definitions)
247
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
248
248
 
249
249
  assert_equal({}, parser.options)
250
- assert_equal(['foo', '-', 'bar'], parser.arguments)
250
+ assert_equal(['foo', '-', 'bar'], parser.arguments.to_a)
251
251
  end
252
252
 
253
253
  def test_parse_with_end_marker
254
- input = %w[foo bar -- -x --yyy -abc]
255
- definitions = []
254
+ input = %w[foo bar -- -x --yyy -abc]
255
+ opt_defns = []
256
256
 
257
- parser = Cri::OptionParser.parse(input, definitions)
257
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
258
258
 
259
259
  assert_equal({}, parser.options)
260
- assert_equal(['foo', 'bar', '-x', '--yyy', '-abc'], parser.arguments)
260
+ assert_equal(['foo', 'bar', '-x', '--yyy', '-abc'], parser.arguments.to_a)
261
261
  end
262
262
 
263
263
  def test_parse_with_end_marker_between_option_key_and_value
264
- input = %w[foo --aaa -- zzz]
265
- definitions = [
264
+ input = %w[foo --aaa -- zzz]
265
+ opt_defns = [
266
266
  { long: 'aaa', short: 'a', argument: :required },
267
- ]
267
+ ].map { |hash| make_opt_defn(hash) }
268
268
 
269
269
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
270
- Cri::OptionParser.parse(input, definitions)
270
+ Cri::OptionParser.new(input, opt_defns, []).run
271
271
  end
272
272
  end
273
273
 
274
274
  def test_parse_with_multiple_options
275
275
  input = %w[foo -o test -o test2 -v -v -v]
276
- definitions = [
276
+ opt_defns = [
277
277
  { long: 'long', short: 'o', argument: :required, multiple: true },
278
278
  { long: 'verbose', short: 'v', multiple: true },
279
- ]
280
- parser = Cri::OptionParser.parse(input, definitions)
279
+ ].map { |hash| make_opt_defn(hash) }
280
+
281
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
281
282
 
282
283
  assert_equal(%w[test test2], parser.options[:long])
283
284
  assert_equal(3, parser.options[:verbose].size)
284
285
  end
285
286
 
286
287
  def test_parse_with_default_required_unspecified
287
- input = %w[foo]
288
- definitions = [
288
+ input = %w[foo]
289
+ opt_defns = [
289
290
  { long: 'animal', short: 'a', argument: :required, default: 'donkey' },
290
- ]
291
+ ].map { |hash| make_opt_defn(hash) }
291
292
 
292
- parser = Cri::OptionParser.parse(input, definitions)
293
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
293
294
 
294
295
  assert_equal({ animal: 'donkey' }, parser.options)
295
- assert_equal(['foo'], parser.arguments)
296
+ assert_equal(['foo'], parser.arguments.to_a)
296
297
  end
297
298
 
298
299
  def test_parse_with_default_required_no_value
299
- input = %w[foo -a]
300
- definitions = [
300
+ input = %w[foo -a]
301
+ opt_defns = [
301
302
  { long: 'animal', short: 'a', argument: :required, default: 'donkey' },
302
- ]
303
+ ].map { |hash| make_opt_defn(hash) }
303
304
 
304
305
  assert_raises(Cri::OptionParser::OptionRequiresAnArgumentError) do
305
- Cri::OptionParser.parse(input, definitions)
306
+ Cri::OptionParser.new(input, opt_defns, []).run
306
307
  end
307
308
  end
308
309
 
309
310
  def test_parse_with_default_required_value
310
- input = %w[foo -a giraffe]
311
- definitions = [
311
+ input = %w[foo -a giraffe]
312
+ opt_defns = [
312
313
  { long: 'animal', short: 'a', argument: :required, default: 'donkey' },
313
- ]
314
+ ].map { |hash| make_opt_defn(hash) }
314
315
 
315
- parser = Cri::OptionParser.parse(input, definitions)
316
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
316
317
 
317
318
  assert_equal({ animal: 'giraffe' }, parser.options)
318
- assert_equal(['foo'], parser.arguments)
319
+ assert_equal(['foo'], parser.arguments.to_a)
319
320
  end
320
321
 
321
322
  def test_parse_with_default_optional_unspecified
322
- input = %w[foo]
323
- definitions = [
323
+ input = %w[foo]
324
+ opt_defns = [
324
325
  { long: 'animal', short: 'a', argument: :optional, default: 'donkey' },
325
- ]
326
+ ].map { |hash| make_opt_defn(hash) }
326
327
 
327
- parser = Cri::OptionParser.parse(input, definitions)
328
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
328
329
 
329
330
  assert_equal({ animal: 'donkey' }, parser.options)
330
- assert_equal(['foo'], parser.arguments)
331
+ assert_equal(['foo'], parser.arguments.to_a)
331
332
  end
332
333
 
333
334
  def test_parse_with_default_optional_no_value
334
- input = %w[foo -a]
335
- definitions = [
335
+ input = %w[foo -a]
336
+ opt_defns = [
336
337
  { long: 'animal', short: 'a', argument: :optional, default: 'donkey' },
337
- ]
338
+ ].map { |hash| make_opt_defn(hash) }
338
339
 
339
- parser = Cri::OptionParser.parse(input, definitions)
340
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
340
341
 
341
342
  assert_equal({ animal: 'donkey' }, parser.options)
342
- assert_equal(['foo'], parser.arguments)
343
+ assert_equal(['foo'], parser.arguments.to_a)
343
344
  end
344
345
 
345
346
  def test_parse_with_default_optional_value
346
- input = %w[foo -a giraffe]
347
- definitions = [
347
+ input = %w[foo -a giraffe]
348
+ opt_defns = [
348
349
  { long: 'animal', short: 'a', argument: :optional, default: 'donkey' },
349
- ]
350
+ ].map { |hash| make_opt_defn(hash) }
350
351
 
351
- parser = Cri::OptionParser.parse(input, definitions)
352
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
352
353
 
353
354
  assert_equal({ animal: 'giraffe' }, parser.options)
354
- assert_equal(['foo'], parser.arguments)
355
+ assert_equal(['foo'], parser.arguments.to_a)
355
356
  end
356
357
 
357
358
  def test_parse_with_default_optional_value_and_arg
358
- input = %w[foo -a gi raffe]
359
- definitions = [
359
+ input = %w[foo -a gi raffe]
360
+ opt_defns = [
360
361
  { long: 'animal', short: 'a', argument: :optional, default: 'donkey' },
361
- ]
362
+ ].map { |hash| make_opt_defn(hash) }
362
363
 
363
- parser = Cri::OptionParser.parse(input, definitions)
364
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
364
365
 
365
366
  assert_equal({ animal: 'gi' }, parser.options)
366
- assert_equal(%w[foo raffe], parser.arguments)
367
+ assert_equal(%w[foo raffe], parser.arguments.to_a)
367
368
  end
368
369
 
369
370
  def test_parse_with_combined_required_options
370
- input = %w[foo -abc xxx yyy zzz]
371
- definitions = [
371
+ input = %w[foo -abc xxx yyy zzz]
372
+ opt_defns = [
372
373
  { long: 'aaa', short: 'a', argument: :forbidden },
373
374
  { long: 'bbb', short: 'b', argument: :required },
374
375
  { long: 'ccc', short: 'c', argument: :required },
375
- ]
376
+ ].map { |hash| make_opt_defn(hash) }
376
377
 
377
- parser = Cri::OptionParser.parse(input, definitions)
378
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
378
379
 
379
380
  assert_equal({ aaa: true, bbb: 'xxx', ccc: 'yyy' }, parser.options)
380
- assert_equal(%w[foo zzz], parser.arguments)
381
+ assert_equal(%w[foo zzz], parser.arguments.to_a)
381
382
  end
382
383
 
383
384
  def test_parse_with_combined_optional_options
384
- input = %w[foo -abc xxx yyy zzz]
385
- definitions = [
385
+ input = %w[foo -abc xxx yyy zzz]
386
+ opt_defns = [
386
387
  { long: 'aaa', short: 'a', argument: :forbidden },
387
388
  { long: 'bbb', short: 'b', argument: :optional },
388
389
  { long: 'ccc', short: 'c', argument: :required },
389
- ]
390
+ ].map { |hash| make_opt_defn(hash) }
390
391
 
391
- parser = Cri::OptionParser.parse(input, definitions)
392
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
392
393
 
393
394
  assert_equal({ aaa: true, bbb: 'xxx', ccc: 'yyy' }, parser.options)
394
- assert_equal(%w[foo zzz], parser.arguments)
395
+ assert_equal(%w[foo zzz], parser.arguments.to_a)
395
396
  end
396
397
 
397
398
  def test_parse_with_combined_optional_options_with_missing_value
398
- input = %w[foo -abc xxx]
399
- definitions = [
399
+ input = %w[foo -abc xxx]
400
+ opt_defns = [
400
401
  { long: 'aaa', short: 'a', argument: :forbidden },
401
402
  { long: 'bbb', short: 'b', argument: :required },
402
403
  { long: 'ccc', short: 'c', argument: :optional, default: 'c default' },
403
- ]
404
+ ].map { |hash| make_opt_defn(hash) }
404
405
 
405
- parser = Cri::OptionParser.parse(input, definitions)
406
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
406
407
 
407
408
  assert_equal({ aaa: true, bbb: 'xxx', ccc: 'c default' }, parser.options)
408
- assert_equal(%w[foo], parser.arguments)
409
+ assert_equal(%w[foo], parser.arguments.to_a)
409
410
  end
410
411
 
411
412
  def test_parse_with_transform_proc
412
- input = %w[--port 123]
413
- definitions = [
413
+ input = %w[--port 123]
414
+ opt_defns = [
414
415
  { long: 'port', short: 'p', argument: :required, transform: ->(x) { Integer(x) } },
415
- ]
416
+ ].map { |hash| make_opt_defn(hash) }
416
417
 
417
- parser = Cri::OptionParser.parse(input, definitions)
418
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
418
419
 
419
420
  assert_equal({ port: 123 }, parser.options)
420
- assert_equal([], parser.arguments)
421
+ assert_equal([], parser.arguments.to_a)
421
422
  end
422
423
 
423
424
  def test_parse_with_transform_method
424
- input = %w[--port 123]
425
- definitions = [
425
+ input = %w[--port 123]
426
+ opt_defns = [
426
427
  { long: 'port', short: 'p', argument: :required, transform: method(:Integer) },
427
- ]
428
+ ].map { |hash| make_opt_defn(hash) }
428
429
 
429
- parser = Cri::OptionParser.parse(input, definitions)
430
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
430
431
 
431
432
  assert_equal({ port: 123 }, parser.options)
432
- assert_equal([], parser.arguments)
433
+ assert_equal([], parser.arguments.to_a)
433
434
  end
434
435
 
435
436
  def test_parse_with_transform_object
@@ -439,15 +440,15 @@ module Cri
439
440
  end
440
441
  end.new
441
442
 
442
- input = %w[--port 123]
443
- definitions = [
443
+ input = %w[--port 123]
444
+ opt_defns = [
444
445
  { long: 'port', short: 'p', argument: :required, transform: port },
445
- ]
446
+ ].map { |hash| make_opt_defn(hash) }
446
447
 
447
- parser = Cri::OptionParser.parse(input, definitions)
448
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
448
449
 
449
450
  assert_equal({ port: 123 }, parser.options)
450
- assert_equal([], parser.arguments)
451
+ assert_equal([], parser.arguments.to_a)
451
452
  end
452
453
 
453
454
  def test_parse_with_transform_default
@@ -458,27 +459,108 @@ module Cri
458
459
  end
459
460
  end.new
460
461
 
461
- input = %w[]
462
- definitions = [
462
+ input = %w[]
463
+ opt_defns = [
463
464
  { long: 'port', short: 'p', argument: :required, default: 8080, transform: port },
464
- ]
465
+ ].map { |hash| make_opt_defn(hash) }
465
466
 
466
- parser = Cri::OptionParser.parse(input, definitions)
467
+ parser = Cri::OptionParser.new(input, opt_defns, []).run
467
468
 
468
469
  assert_equal({ port: 8080 }, parser.options)
469
- assert_equal([], parser.arguments)
470
+ assert_equal([], parser.arguments.to_a)
470
471
  end
471
472
 
472
473
  def test_parse_with_transform_exception
473
- input = %w[--port one_hundred_and_twenty_three]
474
- definitions = [
474
+ input = %w[--port one_hundred_and_twenty_three]
475
+ opt_defns = [
475
476
  { long: 'port', short: 'p', argument: :required, transform: method(:Integer) },
476
- ]
477
+ ].map { |hash| make_opt_defn(hash) }
477
478
 
478
479
  exception = assert_raises(Cri::OptionParser::IllegalOptionValueError) do
479
- Cri::OptionParser.parse(input, definitions)
480
+ Cri::OptionParser.new(input, opt_defns, []).run
480
481
  end
481
482
  assert_equal('invalid value "one_hundred_and_twenty_three" for --port option', exception.message)
482
483
  end
484
+
485
+ def test_parse_with_param_defns
486
+ input = %w[localhost]
487
+ param_defns = [
488
+ { name: 'host' },
489
+ ].map { |hash| Cri::ParamDefinition.new(hash) }
490
+
491
+ parser = Cri::OptionParser.new(input, [], param_defns).run
492
+ assert_equal({}, parser.options)
493
+ assert_equal('localhost', parser.arguments[0])
494
+ assert_equal('localhost', parser.arguments[:host])
495
+ end
496
+
497
+ def test_parse_with_param_defns_too_few_args
498
+ input = []
499
+ param_defns = [
500
+ { name: 'host' },
501
+ ].map { |hash| Cri::ParamDefinition.new(hash) }
502
+
503
+ parser = Cri::OptionParser.new(input, [], param_defns).run
504
+ exception = assert_raises(Cri::ArgumentList::ArgumentCountMismatchError) do
505
+ parser.arguments
506
+ end
507
+ assert_equal('incorrect number of arguments given: expected 1, but got 0', exception.message)
508
+ end
509
+
510
+ def test_parse_with_param_defns_too_many_args
511
+ input = %w[localhost oink]
512
+ param_defns = [
513
+ { name: 'host' },
514
+ ].map { |hash| Cri::ParamDefinition.new(hash) }
515
+
516
+ parser = Cri::OptionParser.new(input, [], param_defns).run
517
+ exception = assert_raises(Cri::ArgumentList::ArgumentCountMismatchError) do
518
+ parser.arguments
519
+ end
520
+ assert_equal('incorrect number of arguments given: expected 1, but got 2', exception.message)
521
+ end
522
+
523
+ def test_parse_with_param_defns_invalid_key
524
+ input = %w[localhost]
525
+ param_defns = [
526
+ { name: 'host' },
527
+ ].map { |hash| Cri::ParamDefinition.new(hash) }
528
+
529
+ parser = Cri::OptionParser.new(input, [], param_defns).run
530
+
531
+ exception = assert_raises(ArgumentError) do
532
+ parser.arguments['oink']
533
+ end
534
+ assert_equal('argument lists can be indexed using a Symbol or an Integer, but not a String', exception.message)
535
+ end
536
+
537
+ def test_parse_with_param_defns_two_params
538
+ input = %w[localhost example.com]
539
+ param_defns = [
540
+ { name: 'source' },
541
+ { name: 'target' },
542
+ ].map { |hash| Cri::ParamDefinition.new(hash) }
543
+
544
+ parser = Cri::OptionParser.new(input, [], param_defns).run
545
+ assert_equal({}, parser.options)
546
+ assert_equal('localhost', parser.arguments[0])
547
+ assert_equal('localhost', parser.arguments[:source])
548
+ assert_equal('example.com', parser.arguments[1])
549
+ assert_equal('example.com', parser.arguments[:target])
550
+ end
551
+
552
+ def make_opt_defn(hash)
553
+ Cri::OptionDefinition.new(
554
+ short: hash.fetch(:short, nil),
555
+ long: hash.fetch(:long, nil),
556
+ desc: hash.fetch(:desc, nil),
557
+ argument: hash.fetch(:argument, nil),
558
+ multiple: hash.fetch(:multiple, nil),
559
+ block: hash.fetch(:block, nil),
560
+ hidden: hash.fetch(:hidden, nil),
561
+ default: hash.fetch(:default, nil),
562
+ transform: hash.fetch(:transform, nil),
563
+ )
564
+ end
483
565
  end
484
566
  end