hammer_cli 3.0.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/doc/creating_commands.md +17 -0
  3. data/doc/release_notes.md +16 -1
  4. data/lib/hammer_cli/abstract.rb +58 -53
  5. data/lib/hammer_cli/apipie/command.rb +1 -1
  6. data/lib/hammer_cli/apipie/option_builder.rb +13 -10
  7. data/lib/hammer_cli/apipie/option_definition.rb +1 -8
  8. data/lib/hammer_cli/command_extensions.rb +11 -6
  9. data/lib/hammer_cli/help/builder.rb +10 -6
  10. data/lib/hammer_cli/options/normalizers.rb +129 -23
  11. data/lib/hammer_cli/options/option_definition.rb +17 -22
  12. data/lib/hammer_cli/options/option_family.rb +44 -8
  13. data/lib/hammer_cli/output/adapter/abstract.rb +6 -0
  14. data/lib/hammer_cli/output/adapter/base.rb +1 -1
  15. data/lib/hammer_cli/output/adapter/tree_structure.rb +1 -1
  16. data/lib/hammer_cli/output/field_filter.rb +1 -1
  17. data/lib/hammer_cli/testing/command_assertions.rb +21 -0
  18. data/lib/hammer_cli/utils.rb +6 -0
  19. data/lib/hammer_cli/version.rb +1 -1
  20. data/locale/ca/LC_MESSAGES/hammer-cli.mo +0 -0
  21. data/locale/de/LC_MESSAGES/hammer-cli.mo +0 -0
  22. data/locale/en/LC_MESSAGES/hammer-cli.mo +0 -0
  23. data/locale/en_GB/LC_MESSAGES/hammer-cli.mo +0 -0
  24. data/locale/es/LC_MESSAGES/hammer-cli.mo +0 -0
  25. data/locale/fr/LC_MESSAGES/hammer-cli.mo +0 -0
  26. data/locale/it/LC_MESSAGES/hammer-cli.mo +0 -0
  27. data/locale/ja/LC_MESSAGES/hammer-cli.mo +0 -0
  28. data/locale/ko/LC_MESSAGES/hammer-cli.mo +0 -0
  29. data/locale/pt_BR/LC_MESSAGES/hammer-cli.mo +0 -0
  30. data/locale/ru/LC_MESSAGES/hammer-cli.mo +0 -0
  31. data/locale/zh_CN/LC_MESSAGES/hammer-cli.mo +0 -0
  32. data/locale/zh_TW/LC_MESSAGES/hammer-cli.mo +0 -0
  33. data/test/unit/abstract_test.rb +7 -0
  34. data/test/unit/apipie/option_builder_test.rb +8 -3
  35. data/test/unit/command_extensions_test.rb +10 -2
  36. data/test/unit/help/builder_test.rb +20 -2
  37. data/test/unit/options/normalizers_test.rb +26 -10
  38. data/test/unit/options/option_definition_test.rb +12 -1
  39. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c01e8f5eaffd8ec3cf361847b287268be33771d99a96fe8a6b5370efe91f5a2c
4
- data.tar.gz: cecab97ca08ecafe8706258a05c208983985e7029be6386e11c667ad02161641
3
+ metadata.gz: 17f58fd9560500629473f107167076c735526a1a503fa5c7676d7c0da2dd160e
4
+ data.tar.gz: cf66853b504d0081194fa88192cb6fd7fb007270c26b81276be7ab64ba89aa5f
5
5
  SHA512:
6
- metadata.gz: 2c119a18c68d22130baadf51f0673cbb30ccc391ee9e07a0ba3989a2523f78ad492408ec877a0d9eaa01ce4e8f4820d6c1821507bce8ecdde66081f9fa66c712
7
- data.tar.gz: 52a024407f37cba6e6dfe326497b897adaaa74a226820460a4832fa407be7ec7ebfb9b3b2eedb49d48e3e56f4d21a768b4eac5f427ac834c82a2b41c2402b3ff
6
+ metadata.gz: 7e8838aca5dc25c9374a0747f8528f9594a79f0554e1ddff7728378a688458c835e7cab898abf85c06a37802aa019f8d1eda4883fcef8e1639d23e655eca3217
7
+ data.tar.gz: 3f25216e7fc377aad69858ecfa602206da549ae2d214df29990da055d91459088f2a1c95379560af254c755bc5838e11934e6a28bd1109c503390513b9cf4ebf
@@ -190,6 +190,23 @@ To define an option family, use the following DSL:
190
190
  end
191
191
  ```
192
192
 
193
+ You can also add additional options for automatically built ones:
194
+ ```ruby
195
+ # ...
196
+ build_options
197
+ # If --resource-id option comes from the API params and you want to add options
198
+ # with searchables such as --resource-name, --resource-label
199
+ option_family(associate: 'resource') do
200
+ child '--resource-name', 'RESOURCE', _('Resource desc'), attribute_name: :option_resource_name
201
+ child '--resource-label', 'RESOURCE', _('Resource desc'), attribute_name: :option_resource_label
202
+ end
203
+ # $ hammer command --help:
204
+ # ...
205
+ # Options:
206
+ # --resource[-id|-name|-label] Resource desc
207
+ # ...
208
+ ```
209
+
193
210
  ##### Example
194
211
 
195
212
  ```ruby
data/doc/release_notes.md CHANGED
@@ -1,7 +1,22 @@
1
1
  Release notes
2
2
  =============
3
- ### 3.0.1 (2021-11-02)
3
+ ### 3.2.0 (2022-02-10)
4
+ * Fix fr translation ([PR #358](https://github.com/theforeman/hammer-cli/pull/358)), [#34204](http://projects.theforeman.org/issues/34204)
5
+ * Add missing_args_error_result test helper ([PR #357](https://github.com/theforeman/hammer-cli/pull/357))
6
+ * Allow explicit strings in key=value options ([PR #356](https://github.com/theforeman/hammer-cli/pull/356)), [#34079](http://projects.theforeman.org/issues/34079)
7
+ * Bump to 3.2.0-develop
8
+
9
+ ### 3.1.0 (2021-11-10)
4
10
  * Remove a space in hammer's shebang, [#33810](http://projects.theforeman.org/issues/33810)
11
+ * Revert fix rake version
12
+ * Fix rake version
13
+ * Wrap option descriptions to 80 chars, [#33129](http://projects.theforeman.org/issues/33129)
14
+ * Don't store @context in field params, [#33259](http://projects.theforeman.org/issues/33259)
15
+ * Change from superficial copy to deep copy of fields ([PR #348](https://github.com/theforeman/hammer-cli/pull/348)), [#29093](http://projects.theforeman.org/issues/29093)
16
+ * Make api docs params to be the main options, [#33226](http://projects.theforeman.org/issues/33226)
17
+ * Show depr warning only on option usage, [#33225](http://projects.theforeman.org/issues/33225)
18
+ * Extract descs to option details section, [#32783](http://projects.theforeman.org/issues/32783)
19
+ * Bump to 3.1.0-develop
5
20
 
6
21
  ### 3.0.0 (2021-08-04)
7
22
  * Update rel-eng notebook ([PR #347](https://github.com/theforeman/hammer-cli/pull/347))
@@ -25,6 +25,18 @@ module HammerCLI
25
25
  class << self
26
26
  attr_accessor :validation_blocks
27
27
 
28
+ def family_registry
29
+ @family_registry ||= HammerCLI::Options::OptionFamilyRegistry.new
30
+ end
31
+
32
+ def option_families
33
+ ancestors.inject([]) do |registry, ancestor|
34
+ next registry unless ancestor <= HammerCLI::AbstractCommand
35
+
36
+ registry + ancestor.family_registry
37
+ end
38
+ end
39
+
28
40
  def help_extension_blocks
29
41
  @help_extension_blocks ||= []
30
42
  end
@@ -43,25 +55,37 @@ module HammerCLI
43
55
  extensions
44
56
  end
45
57
 
46
- def extend_options_help(option)
58
+ def add_option_schema(option)
47
59
  extend_help do |h|
60
+ option_details = h.find_item(:s_option_details)
48
61
  begin
49
- h.find_item(:s_option_details)
62
+ option_details.definition.find_item(:t_schema_help)
50
63
  rescue ArgumentError
51
- option_details = HammerCLI::Help::Section.new(_('Option details'), nil, id: :s_option_details, richtext: true)
52
64
  option_details.definition << HammerCLI::Help::Text.new(
53
65
  _('Following parameters accept format defined by its schema ' \
54
- '(bold are required; <> contain acceptable type; [] contain acceptable value):')
66
+ '(bold are required; <> contains acceptable type; [] contains acceptable value):'),
67
+ id: :t_schema_help
55
68
  )
56
- h.definition.unshift(option_details)
57
- ensure
58
- h.find_item(:s_option_details).definition << HammerCLI::Help::List.new([
59
- [option.switches.last, option.value_formatter.schema.description]
60
- ])
61
69
  end
70
+ option_details.definition << HammerCLI::Help::List.new([
71
+ [option.switches.last, option.value_formatter.schema.description]
72
+ ])
62
73
  end
63
74
  end
64
75
 
76
+ def add_option_details_section(help)
77
+ option_details = HammerCLI::Help::Section.new(_('Option details'), nil, id: :s_option_details, richtext: true)
78
+ option_details.definition << HammerCLI::Help::Text.new(
79
+ _('Here you can find option types and the value an option can accept:')
80
+ )
81
+ type_list = HammerCLI::Options::Normalizers.available.each_with_object([]) do |n, l|
82
+ l << [n.completion_type.to_s.upcase, n.common_description]
83
+ end.uniq(&:first).sort
84
+
85
+ option_details.definition << HammerCLI::Help::List.new(type_list)
86
+ help.definition.unshift(option_details)
87
+ end
88
+
65
89
  def add_sets_help(help)
66
90
  sets_details = HammerCLI::Help::Section.new(_('Predefined field sets'), nil, id: :s_sets_details, richtext: true)
67
91
  sets_details.definition << HammerCLI::Help::Text.new(output_definition.sets_table)
@@ -138,6 +162,7 @@ module HammerCLI
138
162
  super(invocation_path, builder)
139
163
  help_extension = HammerCLI::Help::TextBuilder.new(builder.richtext)
140
164
  fields_switch = HammerCLI::Options::Predefined::OPTIONS[:fields].first[0]
165
+ add_option_details_section(help_extension) if recognised_options.size > 1
141
166
  add_sets_help(help_extension) if find_option(fields_switch)
142
167
  unless help_extension_blocks.empty?
143
168
  help_extension_blocks.each do |extension_block|
@@ -194,18 +219,29 @@ module HammerCLI
194
219
  @option_builder
195
220
  end
196
221
 
222
+ def self.option(switches, type, description, opts = {}, &block)
223
+ option = HammerCLI::Options::OptionDefinition.new(switches, type, description, opts).tap do |option|
224
+ declared_options << option
225
+ block ||= option.default_conversion_block
226
+ define_accessors_for(option, &block)
227
+ add_option_schema(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
228
+ completion_type_for(option, opts)
229
+ end
230
+ option
231
+ end
232
+
197
233
  def self.build_options(builder_params={})
198
234
  builder_params = yield(builder_params) if block_given?
235
+ builder_params[:command] = self
199
236
 
200
237
  option_builder.build(builder_params).each do |option|
201
238
  # skip switches that are already defined
202
- next if option.nil? || option.switches.any? { |s| find_option(s) }
239
+ next if option.nil? || option.family || option.switches.any? { |s| find_option(s) }
203
240
 
204
- adjust_family(option) if option.respond_to?(:family)
205
241
  declared_options << option
206
242
  block ||= option.default_conversion_block
207
243
  define_accessors_for(option, &block)
208
- extend_options_help(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
244
+ add_option_schema(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
209
245
  completion_type_for(option)
210
246
  end
211
247
  end
@@ -233,14 +269,20 @@ module HammerCLI
233
269
  end
234
270
  end
235
271
 
236
- protected
237
-
238
272
  def self.option_family(options = {}, &block)
239
273
  options[:creator] ||= self
240
- family = HammerCLI::Options::OptionFamily.new(options)
241
- family.instance_eval(&block)
274
+ family = if options[:associate]
275
+ option_families.find { |f| f.root.to_s == options[:associate].to_s }
276
+ else
277
+ HammerCLI::Options::OptionFamily.new(options)
278
+ end
279
+ return family.instance_eval(&block) if family
280
+
281
+ logger('Option Family').debug "No family found for #{options[:associate]}, skipping"
242
282
  end
243
283
 
284
+ protected
285
+
244
286
  def self.find_options(switch_filter, other_filters={})
245
287
  filters = other_filters
246
288
  if switch_filter.is_a? Hash
@@ -339,17 +381,6 @@ module HammerCLI
339
381
  end
340
382
  end
341
383
 
342
- def self.option(switches, type, description, opts = {}, &block)
343
- option = HammerCLI::Options::OptionDefinition.new(switches, type, description, opts).tap do |option|
344
- declared_options << option
345
- block ||= option.default_conversion_block
346
- define_accessors_for(option, &block)
347
- completion_type_for(option, opts)
348
- end
349
- extend_options_help(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
350
- option
351
- end
352
-
353
384
  def all_options
354
385
  option_collector.all_options
355
386
  end
@@ -412,32 +443,6 @@ module HammerCLI
412
443
 
413
444
  private
414
445
 
415
- def self.adjust_family(option)
416
- # Collect options that should share the same family
417
- # If those options have family, adopt the current one
418
- # Else adopt those options to the family of the current option
419
- # NOTE: this shouldn't rewrite any options,
420
- # although options from similar family could be adopted (appended)
421
- options = find_options(
422
- aliased_resource: option.aliased_resource.to_s
423
- ).select { |o| o.family.nil? || o.family.formats.include?(option.value_formatter.class) }.group_by do |o|
424
- next :to_skip if option.family.children.include?(o)
425
- next :to_adopt if o.family.nil? || o.family.head.nil?
426
- next :to_skip if o.family.children.include?(option)
427
- # If both family heads handle the same switch
428
- # then `option` is probably from similar family and can be adopted
429
- next :adopt_by if option.family.head.nil? || o.family.head.handles?(option.family.head.long_switch)
430
-
431
- :to_skip
432
- end
433
- options[:to_adopt]&.each do |child|
434
- option.family&.adopt(child)
435
- end
436
- options[:adopt_by]&.map(&:family)&.uniq&.each do |family|
437
- family.adopt(option)
438
- end
439
- end
440
-
441
446
  def self.inherited_output_definition
442
447
  od = nil
443
448
  if superclass.respond_to? :output_definition
@@ -86,9 +86,9 @@ module HammerCLI::Apipie
86
86
  declared_options << option
87
87
  block ||= option.default_conversion_block
88
88
  define_accessors_for(option, &block)
89
+ add_option_schema(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
89
90
  completion_type_for(option, opts)
90
91
  end
91
- extend_options_help(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
92
92
  option
93
93
  end
94
94
 
@@ -9,11 +9,11 @@ module HammerCLI::Apipie
9
9
  @require_options = options[:require_options].nil? ? true : options[:require_options]
10
10
  end
11
11
 
12
- def build(builder_params={})
12
+ def build(builder_params = {})
13
13
  filter = Array(builder_params[:without])
14
14
  resource_name_map = builder_params[:resource_mapping] || {}
15
15
 
16
- options_for_params(@action.params, filter, resource_name_map)
16
+ options_for_params(@action.params, filter, resource_name_map, command: builder_params[:command])
17
17
  end
18
18
 
19
19
  attr_writer :require_options
@@ -27,21 +27,24 @@ module HammerCLI::Apipie
27
27
  HammerCLI::Apipie::OptionDefinition.new(*args)
28
28
  end
29
29
 
30
- def options_for_params(params, filter, resource_name_map)
31
- opts = []
30
+ def options_for_params(params, filter, resource_name_map, opts = {})
31
+ options = []
32
32
  params.each do |p|
33
- next if filter.include?(p.name) || filter.include?(p.name.to_sym)
33
+ exists = opts[:command].find_option(option_switch(p, resource_name_map))
34
+ next if filter.include?(p.name) || filter.include?(p.name.to_sym) || exists
35
+
34
36
  if p.expected_type == :hash
35
- opts += options_for_params(p.params, filter, resource_name_map)
37
+ options += options_for_params(p.params, filter, resource_name_map, opts)
36
38
  else
37
- opts << create_option(p, resource_name_map)
39
+ options << create_option(p, resource_name_map, opts)
38
40
  end
39
41
  end
40
- opts
42
+ options
41
43
  end
42
44
 
43
- def create_option(param, resource_name_map)
44
- family = HammerCLI::Options::OptionFamily.new
45
+ def create_option(param, resource_name_map, opts = {})
46
+ family = HammerCLI::Options::OptionFamily.new(creator: opts[:command])
47
+ # APIdoc params are considered to be the main options (parent) by default
45
48
  family.parent(option_switch(param, resource_name_map),
46
49
  option_type(param, resource_name_map),
47
50
  option_desc(param),
@@ -2,22 +2,15 @@ require File.join(File.dirname(__FILE__), 'options')
2
2
 
3
3
  module HammerCLI::Apipie
4
4
  class OptionDefinition < HammerCLI::Options::OptionDefinition
5
- attr_accessor :referenced_resource, :aliased_resource, :family
5
+ attr_accessor :referenced_resource, :aliased_resource
6
6
 
7
7
  def initialize(switches, type, description, options = {})
8
8
  @referenced_resource = options[:referenced_resource].to_s if options[:referenced_resource]
9
9
  @aliased_resource = options[:aliased_resource].to_s if options[:aliased_resource]
10
- @family = options[:family]
11
10
  super
12
11
  # Apipie currently sends descriptions as escaped HTML once this is changed this should be removed.
13
12
  # See #15198 on Redmine.
14
13
  @description = CGI::unescapeHTML(description)
15
14
  end
16
-
17
- def child?
18
- return unless @family
19
-
20
- @family.children.include?(self)
21
- end
22
15
  end
23
16
  end
@@ -87,8 +87,11 @@ module HammerCLI
87
87
  end
88
88
 
89
89
  def self.option_family(options = {}, &block)
90
- @option_family_opts = options
91
- @option_family_block = block
90
+ @option_family_extensions ||= []
91
+ @option_family_extensions << {
92
+ options: options,
93
+ block: block
94
+ }
92
95
  end
93
96
 
94
97
  # Object
@@ -256,11 +259,13 @@ module HammerCLI
256
259
  end
257
260
 
258
261
  def self.extend_option_family(command_class)
259
- return if @option_family_block.nil?
262
+ return if @option_family_extensions.nil?
260
263
 
261
- @option_family_opts[:creator] = command_class
262
- command_class.send(:option_family, @option_family_opts, &@option_family_block)
263
- logger.debug("Called option family block for #{command_class}:\n\t#{@option_family_block}")
264
+ @option_family_extensions.each do |extension|
265
+ extension[:options][:creator] = command_class
266
+ command_class.send(:option_family, extension[:options], &extension[:block])
267
+ logger.debug("Called option family block for #{command_class}:\n\t#{extension[:block]}")
268
+ end
264
269
  end
265
270
  end
266
271
  end
@@ -29,7 +29,11 @@ module HammerCLI
29
29
 
30
30
  label_width = DEFAULT_LABEL_INDENT
31
31
  items.each do |item|
32
- label = item.help.first
32
+ label = if !HammerCLI.context[:full_help] && item.respond_to?(:family) && item.family && !item.child?
33
+ item.family.help.first
34
+ else
35
+ item.help.first
36
+ end
33
37
  label_width = label.size if label.size > label_width
34
38
  end
35
39
 
@@ -38,11 +42,11 @@ module HammerCLI
38
42
  next unless HammerCLI.context[:full_help]
39
43
  end
40
44
  label, description = if !HammerCLI.context[:full_help] && item.respond_to?(:family) && item.family
41
- [item.family.switch, item.family.description || item.help[1]]
42
- else
43
- item.help
44
- end
45
- description.gsub(/^(.)/) { Unicode::capitalize($1) }.each_line do |line|
45
+ item.family.help
46
+ else
47
+ item.help
48
+ end
49
+ description.gsub(/^(.)/) { Unicode.capitalize(Regexp.last_match(1)) }.wrap.each_line do |line|
46
50
  puts " %-#{label_width}s %s" % [label, line]
47
51
  label = ''
48
52
  end
@@ -4,8 +4,28 @@ require 'hammer_cli/csv_parser'
4
4
  module HammerCLI
5
5
  module Options
6
6
  module Normalizers
7
+ def self.available
8
+ AbstractNormalizer.available
9
+ end
7
10
 
8
11
  class AbstractNormalizer
12
+ class << self
13
+ attr_reader :available
14
+
15
+ def inherited(subclass)
16
+ @available ||= []
17
+ @available << subclass
18
+ end
19
+
20
+ def completion_type
21
+ :value
22
+ end
23
+
24
+ def common_description
25
+ _("Value described in the option's description. Mostly simple string")
26
+ end
27
+ end
28
+
9
29
  def description
10
30
  ""
11
31
  end
@@ -17,6 +37,10 @@ module HammerCLI
17
37
  def complete(val)
18
38
  []
19
39
  end
40
+
41
+ def completion_type
42
+ { type: self.class.completion_type }
43
+ end
20
44
  end
21
45
 
22
46
  class Default < AbstractNormalizer
@@ -30,9 +54,15 @@ module HammerCLI
30
54
  PAIR_RE = '([^,=]+)=([^,\{\[]+|[\{\[][^\{\}\[\]]*[\}\]])'
31
55
  FULL_RE = "^((%s)[,]?)+$" % PAIR_RE
32
56
 
33
- def description
34
- _("Comma-separated list of key=value.") + "\n" +
35
- _("JSON is acceptable and preferred way for complex parameters")
57
+ class << self
58
+ def completion_type
59
+ :key_value_list
60
+ end
61
+
62
+ def common_description
63
+ _('Comma-separated list of key=value.') + "\n" +
64
+ _('JSON is acceptable and preferred way for such parameters')
65
+ end
36
66
  end
37
67
 
38
68
  def format(val)
@@ -74,15 +104,13 @@ module HammerCLI
74
104
 
75
105
  def strip_value(value)
76
106
  if value.is_a? Array
77
- value.map do |item|
78
- strip_chars(item.strip, '"\'')
79
- end
107
+ value.map(&:strip)
80
108
  elsif value.is_a? Hash
81
109
  value.map do |key, val|
82
- [strip_chars(key.strip, '"\''), strip_chars(val.strip, '"\'')]
110
+ [strip_chars(key.strip, '"\''), val.strip]
83
111
  end.to_h
84
112
  else
85
- strip_chars(value.strip, '"\'')
113
+ value.strip
86
114
  end
87
115
  end
88
116
 
@@ -94,9 +122,16 @@ module HammerCLI
94
122
 
95
123
 
96
124
  class List < AbstractNormalizer
97
- def description
98
- _("Comma separated list of values. Values containing comma should be quoted or escaped with backslash.") + "\n" +
99
- _("JSON is acceptable and preferred way for complex parameters")
125
+ class << self
126
+ def completion_type
127
+ :list
128
+ end
129
+
130
+ def common_description
131
+ _('Comma separated list of values. Values containing comma should be quoted or escaped with backslash.') +
132
+ "\n" +
133
+ _('JSON is acceptable and preferred way for such parameters')
134
+ end
100
135
  end
101
136
 
102
137
  def format(val)
@@ -110,6 +145,18 @@ module HammerCLI
110
145
  end
111
146
 
112
147
  class ListNested < AbstractNormalizer
148
+ class << self
149
+ def completion_type
150
+ :schema
151
+ end
152
+
153
+ def common_description
154
+ _('Comma separated list of values defined by a schema.') +
155
+ "\n" +
156
+ _('JSON is acceptable and preferred way for such parameters')
157
+ end
158
+ end
159
+
113
160
  class Schema < Array
114
161
  def description(richtext: true)
115
162
  '"' + reduce([]) do |schema, nested_param|
@@ -135,11 +182,6 @@ module HammerCLI
135
182
  @schema = Schema.new(schema)
136
183
  end
137
184
 
138
- def description
139
- _("Comma separated list of values defined by a schema. See Option details section below.") + "\n" +
140
- _("JSON is acceptable and preferred way for complex parameters")
141
- end
142
-
143
185
  def format(val)
144
186
  return [] unless val.is_a?(String) && !val.empty?
145
187
  begin
@@ -152,9 +194,22 @@ module HammerCLI
152
194
  end
153
195
  end
154
196
  end
197
+
198
+ def completion_type
199
+ super.merge({ schema: schema.description(richtext: false) })
200
+ end
155
201
  end
156
202
 
157
203
  class Number < AbstractNormalizer
204
+ class << self
205
+ def completion_type
206
+ :number
207
+ end
208
+
209
+ def common_description
210
+ _('Numeric value. Integer')
211
+ end
212
+ end
158
213
 
159
214
  def format(val)
160
215
  if numeric?(val)
@@ -167,17 +222,22 @@ module HammerCLI
167
222
  def numeric?(val)
168
223
  Integer(val) != nil rescue false
169
224
  end
170
-
171
225
  end
172
226
 
173
227
 
174
228
  class Bool < AbstractNormalizer
175
- def allowed_values
176
- ['yes', 'no', 'true', 'false', '1', '0']
229
+ class << self
230
+ def completion_type
231
+ :boolean
232
+ end
233
+
234
+ def common_description
235
+ _('One of %s') % ['true/false', 'yes/no', '1/0'].join(', ')
236
+ end
177
237
  end
178
238
 
179
- def description
180
- _('One of %s.') % ['true/false', 'yes/no', '1/0'].join(', ')
239
+ def allowed_values
240
+ ['yes', 'no', 'true', 'false', '1', '0']
181
241
  end
182
242
 
183
243
  def format(bool)
@@ -194,10 +254,23 @@ module HammerCLI
194
254
  def complete(value)
195
255
  allowed_values.map { |v| v + ' ' }
196
256
  end
257
+
258
+ def completion_type
259
+ super.merge({ values: allowed_values })
260
+ end
197
261
  end
198
262
 
199
263
 
200
264
  class File < AbstractNormalizer
265
+ class << self
266
+ def completion_type
267
+ :file
268
+ end
269
+
270
+ def common_description
271
+ _('Path to a file')
272
+ end
273
+ end
201
274
 
202
275
  def format(path)
203
276
  ::File.read(::File.expand_path(path))
@@ -233,6 +306,16 @@ module HammerCLI
233
306
 
234
307
 
235
308
  class Enum < AbstractNormalizer
309
+ class << self
310
+ def completion_type
311
+ :enum
312
+ end
313
+
314
+ def common_description
315
+ _("Possible values are described in the option's description")
316
+ end
317
+ end
318
+
236
319
  attr_reader :allowed_values
237
320
 
238
321
  def initialize(allowed_values)
@@ -260,6 +343,10 @@ module HammerCLI
260
343
  Completer::finalize_completions(@allowed_values)
261
344
  end
262
345
 
346
+ def completion_type
347
+ super.merge({ values: allowed_values })
348
+ end
349
+
263
350
  private
264
351
 
265
352
  def quoted_values
@@ -269,9 +356,14 @@ module HammerCLI
269
356
 
270
357
 
271
358
  class DateTime < AbstractNormalizer
359
+ class << self
360
+ def completion_type
361
+ :datetime
362
+ end
272
363
 
273
- def description
274
- _("Date and time in YYYY-MM-DD HH:MM:SS or ISO 8601 format")
364
+ def common_description
365
+ _('Date and time in YYYY-MM-DD HH:MM:SS or ISO 8601 format')
366
+ end
275
367
  end
276
368
 
277
369
  def format(date)
@@ -283,6 +375,16 @@ module HammerCLI
283
375
  end
284
376
 
285
377
  class EnumList < AbstractNormalizer
378
+ class << self
379
+ def completion_type
380
+ :multienum
381
+ end
382
+
383
+ def common_description
384
+ _("Any combination of possible values described in the option's description")
385
+ end
386
+ end
387
+
286
388
  attr_reader :allowed_values
287
389
 
288
390
  def initialize(allowed_values)
@@ -301,6 +403,10 @@ module HammerCLI
301
403
  Completer::finalize_completions(@allowed_values)
302
404
  end
303
405
 
406
+ def completion_type
407
+ super.merge({ values: allowed_values })
408
+ end
409
+
304
410
  private
305
411
 
306
412
  def quoted_values
@@ -22,12 +22,14 @@ module HammerCLI
22
22
 
23
23
  class OptionDefinition < Clamp::Option::Definition
24
24
 
25
- attr_accessor :value_formatter, :context_target, :deprecated_switches
25
+ attr_accessor :value_formatter, :context_target, :deprecated_switches,
26
+ :family
26
27
 
27
28
  def initialize(switches, type, description, options = {})
28
29
  @value_formatter = options[:format] || HammerCLI::Options::Normalizers::Default.new
29
30
  @context_target = options[:context_target]
30
31
  @deprecated_switches = options[:deprecated]
32
+ @family = options[:family]
31
33
  super
32
34
  end
33
35
 
@@ -36,7 +38,9 @@ module HammerCLI
36
38
  end
37
39
 
38
40
  def help_lhs
39
- super
41
+ lhs = switches.join(', ')
42
+ lhs += " #{completion_type[:type]}".upcase unless flag?
43
+ lhs
40
44
  end
41
45
 
42
46
  def help_rhs
@@ -50,14 +54,14 @@ module HammerCLI
50
54
  rhs.empty? ? " " : rhs
51
55
  end
52
56
 
53
- def handles?(switch)
57
+ def extract_value(switch, arguments)
54
58
  message = _("Warning: Option %{option} is deprecated. %{message}")
55
59
  if deprecated_switches.class <= String && switches.include?(switch)
56
- warn(message % { :option => switch, :message => deprecated_switches })
60
+ warn(message % { option: switch, message: deprecated_switches })
57
61
  elsif deprecated_switches.class <= Hash && deprecated_switches.keys.include?(switch)
58
- warn(message % { :option => switch, :message => deprecated_switches[switch] })
62
+ warn(message % { option: switch, message: deprecated_switches[switch] })
59
63
  end
60
- super(switch)
64
+ super(switch, arguments)
61
65
  end
62
66
 
63
67
  def deprecation_message(switch)
@@ -140,22 +144,13 @@ module HammerCLI
140
144
  return { type: :flag } if @type == :flag
141
145
 
142
146
  formatter ||= value_formatter
143
- completion_type = case formatter
144
- when HammerCLI::Options::Normalizers::Bool,
145
- HammerCLI::Options::Normalizers::Enum
146
- { type: :enum, values: value_formatter.allowed_values }
147
- when HammerCLI::Options::Normalizers::EnumList
148
- { type: :multienum, values: value_formatter.allowed_values }
149
- when HammerCLI::Options::Normalizers::ListNested
150
- { type: :schema, schema: value_formatter.schema.description(richtext: false) }
151
- when HammerCLI::Options::Normalizers::List
152
- { type: :list }
153
- when HammerCLI::Options::Normalizers::KeyValueList
154
- { type: :key_value_list }
155
- when HammerCLI::Options::Normalizers::File
156
- { type: :file }
157
- end
158
- completion_type || { type: :value }
147
+ formatter.completion_type
148
+ end
149
+
150
+ def child?
151
+ return unless @family
152
+
153
+ @family.children.include?(self)
159
154
  end
160
155
 
161
156
  private
@@ -2,25 +2,33 @@
2
2
 
3
3
  module HammerCLI
4
4
  module Options
5
+ class OptionFamilyRegistry < Array
6
+ # rubocop:disable Style/Alias
7
+ alias_method :register, :push
8
+ alias_method :unregister, :delete
9
+ # rubocop:enable Style/Alias
10
+ end
11
+
5
12
  class OptionFamily
6
13
  attr_reader :children
7
14
 
8
- IDS_REGEX = /(\A[Ii][Dd][s]?)|\s([Ii][Dd][s]?)\W|([Ii][Dd][s]?\Z)/
15
+ IDS_REGEX = /(\A[Ii][Dd][s]?)|\s([Ii][Dd][s]?)\W|([Ii][Dd][s]?\Z)|(numeric identifier|identifier)/.freeze
9
16
 
10
17
  def initialize(options = {})
11
18
  @all = []
12
19
  @children = []
13
20
  @options = options
14
- @creator = options[:creator] || Class.new(HammerCLI::Apipie::Command)
21
+ @creator = options[:creator] || self
15
22
  @prefix = options[:prefix]
16
23
  @root = options[:root] || options[:aliased_resource] || options[:referenced_resource]
24
+ @creator.family_registry.register(self) if @creator != self
17
25
  end
18
26
 
19
27
  def description
20
28
  types = all.map(&:type).map { |s| s.split('_').last.to_s }
21
29
  .map(&:downcase).join('/')
22
- parent_desc = @parent.help[1].gsub(IDS_REGEX) { |w| w.gsub(/\w+/, types) }
23
- desc = parent_desc.strip.empty? ? @options[:description] : parent_desc
30
+ parent_desc = @parent.help[1].gsub(IDS_REGEX) { |w| w.gsub(/\b.+\b/, types) }
31
+ desc = @options[:description] || parent_desc.strip.empty? ? @options[:description] : parent_desc
24
32
  if @options[:deprecation].class <= String
25
33
  format_deprecation_msg(desc, _('Deprecated: %{deprecated_msg}') % { deprecated_msg: @options[:deprecation] })
26
34
  elsif @options[:deprecation].class <= Hash
@@ -33,6 +41,21 @@ module HammerCLI
33
41
  end
34
42
  end
35
43
 
44
+ def help
45
+ [help_lhs, help_rhs]
46
+ end
47
+
48
+ def help_lhs
49
+ return @parent&.help_lhs if @children.empty?
50
+
51
+ types = all.map(&:value_formatter).map { |f| f.completion_type[:type].to_s.upcase }
52
+ switch + ' ' + types.uniq.join('/')
53
+ end
54
+
55
+ def help_rhs
56
+ description || @parent.help[1]
57
+ end
58
+
36
59
  def formats
37
60
  return [@options[:format].class] if @options[:format]
38
61
 
@@ -41,7 +64,7 @@ module HammerCLI
41
64
 
42
65
  def switch
43
66
  return if @parent.nil? && @children.empty?
44
- return @parent.help_lhs.strip if @children.empty?
67
+ return @parent.switches.join(', ').strip if @children.empty?
45
68
 
46
69
  switch_start = main_switch.each_char
47
70
  .zip(*all.map(&:switches).flatten.map(&:each_char))
@@ -68,6 +91,8 @@ module HammerCLI
68
91
 
69
92
  def child(switches, type, description, opts = {}, &block)
70
93
  child = new_member(switches, type, description, opts, &block)
94
+ return unless child
95
+
71
96
  @children << child
72
97
  child
73
98
  end
@@ -80,6 +105,18 @@ module HammerCLI
80
105
  @children << child
81
106
  end
82
107
 
108
+ def root
109
+ @root || @parent&.aliased_resource || @parent&.referenced_resource || common_root
110
+ end
111
+
112
+ def option(*args)
113
+ HammerCLI::Apipie::OptionDefinition.new(*args)
114
+ end
115
+
116
+ def find_option(switch)
117
+ all.find { |m| m.handles?(switch) }
118
+ end
119
+
83
120
  private
84
121
 
85
122
  def format_deprecation_msg(option_desc, deprecation_msg)
@@ -90,18 +127,17 @@ module HammerCLI
90
127
  opts = opts.merge(@options)
91
128
  opts[:family] = self
92
129
  if opts[:deprecated]
93
- handles = [switches].flatten
130
+ handles = Array(switches)
94
131
  opts[:deprecated] = opts[:deprecated].select do |switch, _msg|
95
132
  handles.include?(switch)
96
133
  end
97
134
  end
98
135
  @creator.instance_eval do
99
- option(switches, type, description, opts, &block)
136
+ option(switches, type, description, opts, &block) unless Array(switches).any? { |s| find_option(s) }
100
137
  end
101
138
  end
102
139
 
103
140
  def main_switch
104
- root = @root || @parent.aliased_resource || @parent.referenced_resource || common_root
105
141
  "--#{@prefix}#{root}".tr('_', '-')
106
142
  end
107
143
 
@@ -97,6 +97,12 @@ module HammerCLI::Output::Adapter
97
97
  @context[:fields] || ['DEFAULT']
98
98
  end
99
99
 
100
+ def context_for_fields
101
+ {
102
+ show_ids: @context[:show_ids]
103
+ }
104
+ end
105
+
100
106
  private
101
107
 
102
108
  def filter_formatters(formatters_map)
@@ -76,7 +76,7 @@ module HammerCLI::Output::Adapter
76
76
  def render_value(field, data)
77
77
  formatter = @formatters.formatter_for_type(field.class)
78
78
  parameters = field.parameters
79
- parameters[:context] = @context
79
+ parameters[:context] = context_for_fields
80
80
  data = formatter.format(data, field.parameters) if formatter
81
81
  data.to_s
82
82
  end
@@ -55,7 +55,7 @@ module HammerCLI::Output::Adapter
55
55
  else
56
56
  formatter = @formatters.formatter_for_type(field.class)
57
57
  parameters = field.parameters
58
- parameters[:context] = @context
58
+ parameters[:context] = context_for_fields
59
59
  if formatter
60
60
  data = formatter.format(data, field.parameters)
61
61
  end
@@ -11,7 +11,7 @@ module HammerCLI::Output
11
11
 
12
12
  def fields=(fields)
13
13
  @fields = fields || []
14
- @filtered_fields = @fields.dup
14
+ @filtered_fields = Marshal.load(Marshal.dump(@fields))
15
15
  end
16
16
 
17
17
  def filter_by_classes(classes = nil)
@@ -94,6 +94,20 @@ module HammerCLI
94
94
  end
95
95
  end
96
96
 
97
+ def missing_args_error(command, opts, heading = nil)
98
+ opts = Array(opts).map { |o| "'#{o}'" }.join(', ')
99
+ message = " Missing arguments for #{opts}."
100
+ if heading.nil?
101
+ ["Could not #{command[-1]} the #{command[-2]}:",
102
+ message,
103
+ ''].join("\n")
104
+ else
105
+ ["#{heading}:",
106
+ message,
107
+ ''].join("\n")
108
+ end
109
+ end
110
+
97
111
  def usage_error_result(command, message, heading=nil)
98
112
  expected_result = CommandExpectation.new
99
113
  expected_result.expected_err = usage_error(command, message, heading)
@@ -115,6 +129,13 @@ module HammerCLI
115
129
  expected_result
116
130
  end
117
131
 
132
+ def missing_args_error_result(command, opts, heading = nil)
133
+ expected_result = CommandExpectation.new
134
+ expected_result.expected_err = missing_args_error(command, opts, heading)
135
+ expected_result.expected_exit_code = HammerCLI::EX_USAGE
136
+ expected_result
137
+ end
138
+
118
139
  def success_result(message)
119
140
  CommandExpectation.new(message)
120
141
  end
@@ -40,6 +40,12 @@ class String
40
40
  HammerCLI.constant_path(self)[-1]
41
41
  end
42
42
 
43
+ # Rails implementation: https://github.com/rails/rails/blob/main/actionview/lib/action_view/helpers/text_helper.rb#L260
44
+ def wrap(line_width: 80, break_sequence: "\n")
45
+ split("\n").collect! do |line|
46
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").strip : line
47
+ end * break_sequence
48
+ end
43
49
  end
44
50
 
45
51
  class Hash
@@ -1,5 +1,5 @@
1
1
  module HammerCLI
2
2
  def self.version
3
- @version ||= Gem::Version.new "3.0.1"
3
+ @version ||= Gem::Version.new "3.2.0"
4
4
  end
5
5
  end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -308,6 +308,13 @@ describe HammerCLI::AbstractCommand do
308
308
  opt = TestOptionCmd.find_option('--fields')
309
309
  opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal true
310
310
  end
311
+
312
+ it 'should add option type and accepted value' do
313
+ help_str = TestOptionCmd.help('')
314
+ help_str.must_match(
315
+ /LIST Comma separated list of values. Values containing comma should be quoted or escaped with backslash./
316
+ )
317
+ end
311
318
  end
312
319
 
313
320
  describe "#options" do
@@ -17,7 +17,7 @@ describe HammerCLI::Apipie::OptionBuilder do
17
17
  let(:resource) {api.resource(:documented)}
18
18
  let(:action) {resource.action(:index)}
19
19
  let(:builder) { HammerCLI::Apipie::OptionBuilder.new(resource, action) }
20
- let(:builder_options) { {} }
20
+ let(:builder_options) { { command: Class.new(HammerCLI::Apipie::Command) } }
21
21
  let(:options) { builder.build(builder_options) }
22
22
 
23
23
  context "with one simple param" do
@@ -51,7 +51,7 @@ describe HammerCLI::Apipie::OptionBuilder do
51
51
  context "required options" do
52
52
 
53
53
  let(:action) {resource.action(:create)}
54
- let(:required_options) { builder.build.reject{|opt| !opt.required?} }
54
+ let(:required_options) { builder.build(builder_options).reject{|opt| !opt.required?} }
55
55
 
56
56
  it "should set required flag for the required options" do
57
57
  required_options.map(&:attribute_name).sort.must_equal [HammerCLI.option_accessor_name("array_param")]
@@ -146,7 +146,12 @@ describe HammerCLI::Apipie::OptionBuilder do
146
146
 
147
147
  context "aliasing resources" do
148
148
  let(:action) {resource.action(:action_with_ids)}
149
- let(:builder_options) { {:resource_mapping => {:organization => 'company', 'compute_resource' => :compute_provider}} }
149
+ let(:builder_options) do
150
+ {
151
+ resource_mapping: { organization: 'company', 'compute_resource' => :compute_provider },
152
+ command: Class.new(HammerCLI::Apipie::Command)
153
+ }
154
+ end
150
155
 
151
156
  it "renames options" do
152
157
  # builder_options[:resource_mapping] = {:organization => 'company', 'compute_resource' => :compute_provider}
@@ -118,7 +118,7 @@ describe HammerCLI::CommandExtensions do
118
118
  it 'should extend option family only' do
119
119
  cmd.extend_with(CmdExtensions.new(only: :option_family))
120
120
  cmd.output_definition.empty?.must_equal true
121
- cmd.recognised_options.map(&:switches).flatten.must_equal ['--test-one', '--test-two', '-h', '--help']
121
+ cmd.recognised_options.map(&:switches).flatten.must_equal ['-h', '--help', '--test-one', '--test-two']
122
122
  end
123
123
  end
124
124
 
@@ -203,5 +203,13 @@ describe HammerCLI::CommandExtensions do
203
203
  end
204
204
  end
205
205
 
206
-
206
+ context 'associate family' do
207
+ it 'should associate option family' do
208
+ cmd.extend_with(CmdExtensions.new(only: :option_family))
209
+ cmd.option_family associate: 'test' do
210
+ child '--test-three', '', ''
211
+ end
212
+ cmd.recognised_options.map(&:switches).flatten.must_equal ['-h', '--help', '--test-one', '--test-two', '--test-three']
213
+ end
214
+ end
207
215
  end
@@ -21,6 +21,24 @@ describe HammerCLI::Help::Builder do
21
21
  ' --zzz-option OPT_Z Some description'
22
22
  ].join("\n")
23
23
  end
24
+
25
+ it 'prints long option descriptions aligned' do
26
+ opt_a_desc = 'AAAAAAA ' * 20
27
+ opt_b_desc = 'BBBBBBB ' * 20
28
+ options = [
29
+ Clamp::Option::Definition.new(['--aaa-option'], 'OPT_A', opt_a_desc),
30
+ Clamp::Option::Definition.new(['--bbb-option'], 'OPT_B', opt_b_desc)
31
+ ]
32
+ help.add_list('Options', options)
33
+
34
+ help.string.strip.must_equal [
35
+ 'Options:',
36
+ ' --aaa-option OPT_A %s' % ('AAAAAAA ' * 10).strip,
37
+ ' %s' % ('AAAAAAA ' * 10).strip,
38
+ ' --bbb-option OPT_B %s' % ('BBBBBBB ' * 10).strip,
39
+ ' %s' % ('BBBBBBB ' * 10).strip
40
+ ].join("\n")
41
+ end
24
42
  end
25
43
 
26
44
  describe 'adding an option with lower case description' do
@@ -86,8 +104,8 @@ describe HammerCLI::Help::Builder do
86
104
 
87
105
  help.string.strip.must_equal [
88
106
  'Options:',
89
- ' --option[-yyy|-bbb] Some description',
90
- ' --option[-aaa|-zzz] Some description',
107
+ ' --option[-yyy|-bbb] VALUE Some description',
108
+ ' --option[-aaa|-zzz] VALUE Some description'
91
109
  ].join("\n")
92
110
  end
93
111
  end
@@ -177,16 +177,20 @@ describe HammerCLI::Options::Normalizers do
177
177
  formatter.format("a= 1 , b = 2 ,c =3").must_equal({'a' => '1', 'b' => '2', 'c' => '3'})
178
178
  end
179
179
 
180
- it "should parse a comma separated string with spaces using single quotes" do
181
- formatter.format("a= ' 1 ' , b =' 2',c ='3'").must_equal({'a' => ' 1 ', 'b' => ' 2', 'c' => '3'})
180
+ it 'should parse a comma separated string with spaces using single quotes' do
181
+ formatter.format("a= ' 1 ' , b =' 2',c ='3'").must_equal({ 'a' => "' 1 '", 'b' => "' 2'", 'c' => "'3'" })
182
182
  end
183
183
 
184
- it "should parse a comma separated string with spaces using double quotes" do
185
- formatter.format("a= \" 1 \" , b =\" 2\",c =\"3\"").must_equal({'a' => ' 1 ', 'b' => ' 2', 'c' => '3'})
184
+ it 'should parse a comma separated string with spaces using double quotes' do
185
+ formatter.format('a= " 1 " , b =" 2",c ="3"').must_equal({ 'a' => '" 1 "', 'b' => '" 2"', 'c' => '"3"' })
186
186
  end
187
187
 
188
- it "should deal with equal sign in value" do
189
- formatter.format("a=1,b='2=2',c=3").must_equal({'a' => '1', 'b' => '2=2', 'c' => '3'})
188
+ it 'should deal with equal sign in string value' do
189
+ formatter.format("a=1,b='2=2',c=3").must_equal({ 'a' => '1', 'b' => "'2=2'", 'c' => '3' })
190
+ end
191
+
192
+ it 'should deal with equal sign in value' do
193
+ formatter.format('a=1,b=2=2,c=3').must_equal({ 'a' => '1', 'b' => '2=2', 'c' => '3' })
190
194
  end
191
195
 
192
196
  it "should parse arrays" do
@@ -197,12 +201,20 @@ describe HammerCLI::Options::Normalizers do
197
201
  formatter.format("a=1,b=[1, 2, 3],c=3").must_equal({'a' => '1', 'b' => ['1', '2', '3'], 'c' => '3'})
198
202
  end
199
203
 
200
- it "should parse arrays with spaces using by single quotes" do
201
- formatter.format("a=1,b=['1 1', ' 2 ', ' 3 3'],c=3").must_equal({'a' => '1', 'b' => ['1 1', ' 2 ', ' 3 3'], 'c' => '3'})
204
+ it 'should parse arrays with spaces using by single quotes' do
205
+ formatter.format("a=1,b=['1 1', ' 2 ', ' 3 3'],c=3").must_equal(
206
+ { 'a' => '1', 'b' => ["'1 1'", "' 2 '", "' 3 3'"], 'c' => '3' }
207
+ )
208
+ end
209
+
210
+ it 'should parse arrays with spaces using by double quotes' do
211
+ formatter.format('a=1,b=["1 1", " 2 ", " 3 3"],c=3').must_equal(
212
+ { 'a' => '1', 'b' => ['"1 1"', '" 2 "', '" 3 3"'], 'c' => '3' }
213
+ )
202
214
  end
203
215
 
204
- it "should parse arrays with spaces using by double quotes" do
205
- formatter.format("a=1,b=[\"1 1\", \" 2 \", \" 3 3\"],c=3").must_equal({'a' => '1', 'b' => ['1 1', ' 2 ', ' 3 3'], 'c' => '3'})
216
+ it 'should parse arrays with spaces' do
217
+ formatter.format('a=1,b=[1 1, 2 , 3 3],c=3').must_equal({ 'a' => '1', 'b' => ['1 1', '2', '3 3'], 'c' => '3' })
206
218
  end
207
219
 
208
220
  it "should parse array with one item" do
@@ -226,6 +238,10 @@ describe HammerCLI::Options::Normalizers do
226
238
  it "should parse a comma separated string 2" do
227
239
  proc { formatter.format("a=1,b,c=3") }.must_raise ArgumentError
228
240
  end
241
+
242
+ it 'should parse explicit strings' do
243
+ formatter.format('name="*"').must_equal({ 'name' => '"*"' })
244
+ end
229
245
  end
230
246
 
231
247
  describe 'json format' do
@@ -25,6 +25,10 @@ describe HammerCLI::Options::OptionDefinition do
25
25
  option "--another-deprecated", "OLD_OPTION", "Test old option",
26
26
  :context_target => :old_option,
27
27
  :deprecated => "It is going to be removed"
28
+
29
+ def find_option(switch)
30
+ super(switch)
31
+ end
28
32
  end
29
33
 
30
34
  def opt_with_deprecation(deprecation)
@@ -73,6 +77,14 @@ describe HammerCLI::Options::OptionDefinition do
73
77
  context[:test_option].must_equal "VALUE"
74
78
  end
75
79
 
80
+ it "doesn't print deprecation warning if the option is not used" do
81
+ context = {}
82
+ cmd = TestDeprecatedOptionCmd.new('', context)
83
+ cmd.find_option('--deprecated')
84
+ _out, err = capture_io { cmd.run([]) }
85
+ err.must_equal ''
86
+ end
87
+
76
88
  it 'shows depracated message in help' do
77
89
  opt = opt_with_deprecation("Use --better-switch instead")
78
90
  opt.description.must_equal "Test option (Deprecated: Use --better-switch instead)"
@@ -98,4 +110,3 @@ describe HammerCLI::Options::OptionDefinition do
98
110
  end
99
111
  end
100
112
  end
101
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hammer_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Bačovský
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-11-02 00:00:00.000000000 Z
12
+ date: 2022-02-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: clamp
@@ -443,9 +443,9 @@ test_files:
443
443
  - test/unit/options/sources/command_line_test.rb
444
444
  - test/unit/options/sources/saved_defaults_test.rb
445
445
  - test/unit/options/validators/dsl_test.rb
446
- - test/unit/options/normalizers_test.rb
447
446
  - test/unit/options/option_family_test.rb
448
447
  - test/unit/options/option_definition_test.rb
448
+ - test/unit/options/normalizers_test.rb
449
449
  - test/unit/output/adapter/abstract_test.rb
450
450
  - test/unit/output/adapter/base_test.rb
451
451
  - test/unit/output/adapter/csv_test.rb