hammer_cli 3.0.1 → 3.2.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.
- checksums.yaml +4 -4
- data/doc/creating_commands.md +17 -0
- data/doc/release_notes.md +16 -1
- data/lib/hammer_cli/abstract.rb +58 -53
- data/lib/hammer_cli/apipie/command.rb +1 -1
- data/lib/hammer_cli/apipie/option_builder.rb +13 -10
- data/lib/hammer_cli/apipie/option_definition.rb +1 -8
- data/lib/hammer_cli/command_extensions.rb +11 -6
- data/lib/hammer_cli/help/builder.rb +10 -6
- data/lib/hammer_cli/options/normalizers.rb +129 -23
- data/lib/hammer_cli/options/option_definition.rb +17 -22
- data/lib/hammer_cli/options/option_family.rb +44 -8
- data/lib/hammer_cli/output/adapter/abstract.rb +6 -0
- data/lib/hammer_cli/output/adapter/base.rb +1 -1
- data/lib/hammer_cli/output/adapter/tree_structure.rb +1 -1
- data/lib/hammer_cli/output/field_filter.rb +1 -1
- data/lib/hammer_cli/testing/command_assertions.rb +21 -0
- data/lib/hammer_cli/utils.rb +6 -0
- data/lib/hammer_cli/version.rb +1 -1
- data/locale/ca/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/de/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/en/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/en_GB/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/es/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/fr/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/it/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/ja/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/ko/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/pt_BR/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/ru/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/zh_CN/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/zh_TW/LC_MESSAGES/hammer-cli.mo +0 -0
- data/test/unit/abstract_test.rb +7 -0
- data/test/unit/apipie/option_builder_test.rb +8 -3
- data/test/unit/command_extensions_test.rb +10 -2
- data/test/unit/help/builder_test.rb +20 -2
- data/test/unit/options/normalizers_test.rb +26 -10
- data/test/unit/options/option_definition_test.rb +12 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 17f58fd9560500629473f107167076c735526a1a503fa5c7676d7c0da2dd160e
|
|
4
|
+
data.tar.gz: cf66853b504d0081194fa88192cb6fd7fb007270c26b81276be7ab64ba89aa5f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7e8838aca5dc25c9374a0747f8528f9594a79f0554e1ddff7728378a688458c835e7cab898abf85c06a37802aa019f8d1eda4883fcef8e1639d23e655eca3217
|
|
7
|
+
data.tar.gz: 3f25216e7fc377aad69858ecfa602206da549ae2d214df29990da055d91459088f2a1c95379560af254c755bc5838e11934e6a28bd1109c503390513b9cf4ebf
|
data/doc/creating_commands.md
CHANGED
|
@@ -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
|
|
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))
|
data/lib/hammer_cli/abstract.rb
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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; <>
|
|
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
|
-
|
|
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 =
|
|
241
|
-
|
|
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
|
-
|
|
30
|
+
def options_for_params(params, filter, resource_name_map, opts = {})
|
|
31
|
+
options = []
|
|
32
32
|
params.each do |p|
|
|
33
|
-
|
|
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
|
-
|
|
37
|
+
options += options_for_params(p.params, filter, resource_name_map, opts)
|
|
36
38
|
else
|
|
37
|
-
|
|
39
|
+
options << create_option(p, resource_name_map, opts)
|
|
38
40
|
end
|
|
39
41
|
end
|
|
40
|
-
|
|
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
|
|
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
|
-
@
|
|
91
|
-
@
|
|
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 @
|
|
262
|
+
return if @option_family_extensions.nil?
|
|
260
263
|
|
|
261
|
-
@
|
|
262
|
-
|
|
263
|
-
|
|
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.
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
description.gsub(/^(.)/) { Unicode
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
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, '"\''),
|
|
110
|
+
[strip_chars(key.strip, '"\''), val.strip]
|
|
83
111
|
end.to_h
|
|
84
112
|
else
|
|
85
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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
|
|
180
|
-
|
|
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
|
-
|
|
274
|
-
|
|
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
|
-
|
|
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
|
|
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 % { :
|
|
60
|
+
warn(message % { option: switch, message: deprecated_switches })
|
|
57
61
|
elsif deprecated_switches.class <= Hash && deprecated_switches.keys.include?(switch)
|
|
58
|
-
warn(message % { :
|
|
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
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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] ||
|
|
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(/\
|
|
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.
|
|
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 =
|
|
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
|
|
|
@@ -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] =
|
|
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] =
|
|
58
|
+
parameters[:context] = context_for_fields
|
|
59
59
|
if formatter
|
|
60
60
|
data = formatter.format(data, field.parameters)
|
|
61
61
|
end
|
|
@@ -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
|
data/lib/hammer_cli/utils.rb
CHANGED
|
@@ -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
|
data/lib/hammer_cli/version.rb
CHANGED
|
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
|
data/test/unit/abstract_test.rb
CHANGED
|
@@ -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)
|
|
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 ['
|
|
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]
|
|
90
|
-
' --option[-aaa|-zzz]
|
|
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
|
|
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
|
|
185
|
-
formatter.format(
|
|
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
|
|
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
|
|
201
|
-
formatter.format("a=1,b=['1 1', ' 2 ', ' 3 3'],c=3").must_equal(
|
|
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
|
|
205
|
-
formatter.format(
|
|
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
|
|
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:
|
|
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
|