hammer_cli 2.0.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/doc/commands_extension.md +12 -0
- data/doc/creating_commands.md +73 -0
- data/doc/installation_rpm.md +2 -2
- data/doc/release_notes.md +10 -0
- data/lib/hammer_cli/abstract.rb +29 -2
- data/lib/hammer_cli/apipie/option_builder.rb +15 -13
- data/lib/hammer_cli/apipie/option_definition.rb +9 -7
- data/lib/hammer_cli/command_extensions.rb +21 -1
- data/lib/hammer_cli/exception_handler.rb +1 -1
- data/lib/hammer_cli/full_help.rb +8 -1
- data/lib/hammer_cli/help/builder.rb +10 -3
- data/lib/hammer_cli/logger_watch.rb +1 -1
- data/lib/hammer_cli/options/option_definition.rb +4 -6
- data/lib/hammer_cli/options/option_family.rb +114 -0
- data/lib/hammer_cli/options/predefined.rb +1 -1
- data/lib/hammer_cli/subcommand.rb +25 -1
- data/lib/hammer_cli/testing/command_assertions.rb +2 -2
- data/lib/hammer_cli/utils.rb +17 -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/man/hammer.1.gz +0 -0
- data/test/unit/abstract_test.rb +23 -2
- data/test/unit/apipie/option_builder_test.rb +8 -0
- data/test/unit/command_extensions_test.rb +67 -49
- data/test/unit/help/builder_test.rb +22 -0
- data/test/unit/options/option_family_test.rb +48 -0
- metadata +74 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4f350c15926f88971bd4ca5a7c6e2de1a58c4f5500129e61a68b2ab7f3e2643
|
4
|
+
data.tar.gz: 9ff9f540add2c845efb74fdced769846fe9118a1dcafdab99537a3eb1d9263f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1540908ed1806938b8bf53819b0884a17bd2e6d095b5f2ab14a8843bb125e49934732546390475d60349ef9a2cc7bcbf44c19af7c8cbe76e1f61786d1b2b9a0a
|
7
|
+
data.tar.gz: 0c9f1525623225a28472b3221c528b0d03d425507263049b34ff4e0ebf167dc08ea3f9ed6f9014459930639a26935755327cb0a7b339c8f2affaa714179ab09c
|
data/doc/commands_extension.md
CHANGED
@@ -10,6 +10,11 @@ Each command can be easily extended with one ore more `HammerCLI::CommandExtensi
|
|
10
10
|
inheritable true
|
11
11
|
# Simply add a new option to a command is being extended
|
12
12
|
option(option_params)
|
13
|
+
# Add option family to a command
|
14
|
+
option_family(common_options = {}) do
|
15
|
+
parent option_params
|
16
|
+
child option_params
|
17
|
+
end
|
13
18
|
# Extend hash with data returned from server before it is printed
|
14
19
|
before_print do |data|
|
15
20
|
# data modifications
|
@@ -65,6 +70,13 @@ class MyCommandExtensions < HammerCLI::CommandExtensions
|
|
65
70
|
|
66
71
|
option ['--new-option'], 'TYPE', _('Option description')
|
67
72
|
|
73
|
+
option_family(
|
74
|
+
description: _('Common description')
|
75
|
+
) do
|
76
|
+
parent ['--new-option'], 'TYPE', _('Option description')
|
77
|
+
child ['--new-option-ver2'], 'TYPE', _('Option description')
|
78
|
+
end
|
79
|
+
|
68
80
|
before_print do |data|
|
69
81
|
data['results'].each do |result|
|
70
82
|
result['status'] = process_errors(result['errors'])
|
data/doc/creating_commands.md
CHANGED
@@ -173,6 +173,54 @@ Here is the list of predefined options:
|
|
173
173
|
* `:fields` Expects a list with fields to show in output, see [example](creating_commands.md#printing-hash-records).
|
174
174
|
|
175
175
|
|
176
|
+
### Option family
|
177
|
+
Option family is the way to unify options which have the same meaning or purpose,
|
178
|
+
but contain some differences in their definitions (e.g. the name/switch of an option).
|
179
|
+
Mainly serves as a container for options, which purpose is to show less repetitive
|
180
|
+
output in commands' help. Option builders use it by default.
|
181
|
+
|
182
|
+
To define an option family, use the following DSL:
|
183
|
+
```ruby
|
184
|
+
# options is a Hash with options for family/each defined option within it
|
185
|
+
option_family(options = {}) do
|
186
|
+
# parent is the main option. Must be single, option family can have only one parent.
|
187
|
+
parent switches, type, description, options
|
188
|
+
# child is an additional option. Could be none or more than one. Aren't shown in the help output.
|
189
|
+
child switches, type, description, options
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
##### Example
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
option_family(
|
197
|
+
aliased_resource: 'environment',
|
198
|
+
description: _('Puppet environment'),
|
199
|
+
deprecation: _("Use %s instead") % '--puppet-environment[-id]'
|
200
|
+
deprecated: { '--environment' => _("Use %s instead") % '--puppet-environment[-id]',
|
201
|
+
'--environment-id' => _("Use %s instead") % '--puppet-environment[-id]'}
|
202
|
+
) do
|
203
|
+
parent '--environment-id', 'ENVIRONMENT_ID', _(''),
|
204
|
+
format: HammerCLI::Options::Normalizers::Number.new,
|
205
|
+
attribute_name: :option_environment_id
|
206
|
+
child '--environment', 'ENVIRONMENT_NAME', _('Environment name'),
|
207
|
+
attribute_name: :option_environment_name
|
208
|
+
end
|
209
|
+
|
210
|
+
# $ hammer command --help:
|
211
|
+
# ...
|
212
|
+
# Options:
|
213
|
+
# --environment[-id] Puppet environment (Deprecated: Use --puppet-environment[-id] instead)
|
214
|
+
# ...
|
215
|
+
|
216
|
+
# $ hammer full-help:
|
217
|
+
# ...
|
218
|
+
# Options:
|
219
|
+
# --environment ENVIRONMENT_NAME Environment name (--environment is deprecated: Use --puppet-environment[-id] instead)
|
220
|
+
# --environment-id ENVIRONMENT_ID (--environment-id is deprecated: Use --puppet-environment[-id] instead)
|
221
|
+
# ...
|
222
|
+
```
|
223
|
+
|
176
224
|
### Option builders
|
177
225
|
Hammer commands offer option builders that can be used for automatic option generation.
|
178
226
|
See [documentation page](option_builders.md#option-builders) dedicated to this topic for more details.
|
@@ -406,6 +454,31 @@ Options:
|
|
406
454
|
-h, --help print help
|
407
455
|
```
|
408
456
|
|
457
|
+
#### Aliasing subcommands
|
458
|
+
|
459
|
+
Commands can have two or more names, e.g. aliases. To support such functionality
|
460
|
+
simple name addition could be used via `command_name` or `command_names` method:
|
461
|
+
```ruby
|
462
|
+
module HammerCLIHello
|
463
|
+
|
464
|
+
class SayCommand < HammerCLI::AbstractCommand
|
465
|
+
|
466
|
+
class GreetingsCommand < HammerCLI::AbstractCommand
|
467
|
+
command_name 'hello'
|
468
|
+
command_name 'hi'
|
469
|
+
# or use can use other method:
|
470
|
+
command_names 'hello', 'hi'
|
471
|
+
|
472
|
+
desc 'Say Hello World!'
|
473
|
+
# ...
|
474
|
+
end
|
475
|
+
|
476
|
+
autoload_subcommands
|
477
|
+
end
|
478
|
+
|
479
|
+
HammerCLI::MainCommand.subcommand 'say', "Say something", HammerCLIHello::SayCommand
|
480
|
+
end
|
481
|
+
```
|
409
482
|
|
410
483
|
### Conflicting subcommands
|
411
484
|
It can happen that two different plugins define subcommands with the same name by accident.
|
data/doc/installation_rpm.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
### Installation from RPMs
|
2
2
|
|
3
|
-
#### Step 1:
|
3
|
+
#### Step 1: set up yum repositories
|
4
4
|
|
5
|
-
For Foreman 1.3 stable the hammer packages are part of your installation repo and you can skip this step.
|
5
|
+
For Foreman 1.3 stable, the hammer packages are part of your installation repo and you can skip this step.
|
6
6
|
|
7
7
|
You can choose from stable or nightly repo. Nightly has more recent version of hammer packages, but it was subject to less testing so there is a higher risk of issues.
|
8
8
|
Add the Foreman yum repository to your yum repo files. For Fedora installations replace 'el6' with 'f18' or 'f19' as appropriate.
|
data/doc/release_notes.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
Release notes
|
2
2
|
=============
|
3
|
+
### 2.2.1 (2020-09-04)
|
4
|
+
* Make fuzzy search work in hammer shell ([PR #335](https://github.com/theforeman/hammer-cli/pull/335)), [#30747](http://projects.theforeman.org/issues/30747)
|
5
|
+
|
6
|
+
### 2.1.0 (2020-05-14)
|
7
|
+
* Hammer full-help returns correct output, [#29697](http://projects.theforeman.org/issues/29697)
|
8
|
+
* Add fuzzy subcommand matching, [#29413](http://projects.theforeman.org/issues/29413)
|
9
|
+
* Help contains squeezed options, [#28440](http://projects.theforeman.org/issues/28440)
|
10
|
+
* Keep referenced resource in option options, [#29015](http://projects.theforeman.org/issues/29015)
|
11
|
+
* Bump to 2.1.0-develop
|
12
|
+
|
3
13
|
### 2.0.0 (2020-02-12)
|
4
14
|
* Bump version to 2.0.0
|
5
15
|
* Bump version to 2.0 ([PR #324](https://github.com/theforeman/hammer-cli/pull/324))
|
data/lib/hammer_cli/abstract.rb
CHANGED
@@ -13,6 +13,7 @@ require 'hammer_cli/options/predefined'
|
|
13
13
|
require 'hammer_cli/help/builder'
|
14
14
|
require 'hammer_cli/help/text_builder'
|
15
15
|
require 'hammer_cli/command_extensions'
|
16
|
+
require 'hammer_cli/options/option_family'
|
16
17
|
require 'logging'
|
17
18
|
|
18
19
|
module HammerCLI
|
@@ -191,6 +192,16 @@ module HammerCLI
|
|
191
192
|
# skip switches that are already defined
|
192
193
|
next if option.nil? or option.switches.any? {|s| find_option(s) }
|
193
194
|
|
195
|
+
if option.respond_to?(:referenced_resource)
|
196
|
+
# Collect options that don't have family, but related to this parent.
|
197
|
+
children = find_options(
|
198
|
+
referenced_resource: option.referenced_resource.to_s,
|
199
|
+
aliased_resource: option.aliased_resource.to_s
|
200
|
+
).select { |o| o.family.nil? || o.family.head.nil? }
|
201
|
+
children.each do |child|
|
202
|
+
option.family.adopt(child) if option.family
|
203
|
+
end
|
204
|
+
end
|
194
205
|
declared_options << option
|
195
206
|
block ||= option.default_conversion_block
|
196
207
|
define_accessors_for(option, &block)
|
@@ -207,6 +218,7 @@ module HammerCLI
|
|
207
218
|
extension.delegatee(self)
|
208
219
|
extension.extend_predefined_options(self)
|
209
220
|
extension.extend_options(self)
|
221
|
+
extension.extend_option_family(self)
|
210
222
|
extension.extend_output(self)
|
211
223
|
extension.extend_help(self)
|
212
224
|
logger('Extensions').info "Applied #{extension.details} on #{self}."
|
@@ -222,6 +234,12 @@ module HammerCLI
|
|
222
234
|
|
223
235
|
protected
|
224
236
|
|
237
|
+
def self.option_family(options = {}, &block)
|
238
|
+
options[:creator] ||= self
|
239
|
+
family = HammerCLI::Options::OptionFamily.new(options)
|
240
|
+
family.instance_eval(&block)
|
241
|
+
end
|
242
|
+
|
225
243
|
def self.find_options(switch_filter, other_filters={})
|
226
244
|
filters = other_filters
|
227
245
|
if switch_filter.is_a? Hash
|
@@ -285,8 +303,17 @@ module HammerCLI
|
|
285
303
|
end
|
286
304
|
|
287
305
|
def self.command_name(name=nil)
|
288
|
-
@
|
289
|
-
|
306
|
+
if @names && name
|
307
|
+
@names << name if !@names.include?(name)
|
308
|
+
else
|
309
|
+
@names = [name] if name
|
310
|
+
end
|
311
|
+
@names || (superclass.respond_to?(:command_names) ? superclass.command_names : nil)
|
312
|
+
end
|
313
|
+
|
314
|
+
def self.command_names(*names)
|
315
|
+
@names = names unless names.empty?
|
316
|
+
@names || (superclass.respond_to?(:command_names) ? superclass.command_names : nil)
|
290
317
|
end
|
291
318
|
|
292
319
|
def self.warning(message = nil)
|
@@ -41,12 +41,11 @@ module HammerCLI::Apipie
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def create_option(param, resource_name_map)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
)
|
44
|
+
family = HammerCLI::Options::OptionFamily.new
|
45
|
+
family.parent(option_switch(param, resource_name_map),
|
46
|
+
option_type(param, resource_name_map),
|
47
|
+
option_desc(param),
|
48
|
+
option_opts(param, resource_name_map))
|
50
49
|
end
|
51
50
|
|
52
51
|
def option_switch(param, resource_name_map)
|
@@ -61,7 +60,7 @@ module HammerCLI::Apipie
|
|
61
60
|
param.description || " "
|
62
61
|
end
|
63
62
|
|
64
|
-
def option_opts(param)
|
63
|
+
def option_opts(param, resource_name_map)
|
65
64
|
opts = {}
|
66
65
|
opts[:required] = true if (param.required? and require_options?)
|
67
66
|
if param.expected_type.to_s == 'array'
|
@@ -80,19 +79,22 @@ module HammerCLI::Apipie
|
|
80
79
|
end
|
81
80
|
opts[:attribute_name] = HammerCLI.option_accessor_name(param.name)
|
82
81
|
opts[:referenced_resource] = resource_name(param)
|
82
|
+
opts[:aliased_resource] = aliased_name(resource_name(param), resource_name_map)
|
83
83
|
|
84
84
|
return opts
|
85
85
|
end
|
86
86
|
|
87
|
+
def aliased_name(name, resource_name_map)
|
88
|
+
return if name.nil?
|
89
|
+
|
90
|
+
resource_name_map[name.to_s] || resource_name_map[name.to_sym] || name
|
91
|
+
end
|
92
|
+
|
87
93
|
def aliased(param, resource_name_map)
|
88
94
|
resource_name = resource_name(param)
|
95
|
+
return param.name if resource_name.nil?
|
89
96
|
|
90
|
-
|
91
|
-
return param.name
|
92
|
-
else
|
93
|
-
aliased_name = resource_name_map[resource_name.to_s] || resource_name_map[resource_name.to_sym] || resource_name
|
94
|
-
return param.name.gsub(resource_name, aliased_name.to_s)
|
95
|
-
end
|
97
|
+
param.name.gsub(resource_name, aliased_name(resource_name, resource_name_map).to_s)
|
96
98
|
end
|
97
99
|
|
98
100
|
def resource_name(param)
|
@@ -1,21 +1,23 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'options')
|
2
2
|
|
3
3
|
module HammerCLI::Apipie
|
4
|
-
|
5
4
|
class OptionDefinition < HammerCLI::Options::OptionDefinition
|
6
|
-
|
7
|
-
attr_accessor :referenced_resource
|
5
|
+
attr_accessor :referenced_resource, :aliased_resource, :family
|
8
6
|
|
9
7
|
def initialize(switches, type, description, options = {})
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
@referenced_resource = options[:referenced_resource].to_s if options[:referenced_resource]
|
9
|
+
@aliased_resource = options[:aliased_resource].to_s if options[:aliased_resource]
|
10
|
+
@family = options[:family]
|
13
11
|
super
|
14
12
|
# Apipie currently sends descriptions as escaped HTML once this is changed this should be removed.
|
15
13
|
# See #15198 on Redmine.
|
16
14
|
@description = CGI::unescapeHTML(description)
|
17
15
|
end
|
18
16
|
|
19
|
-
|
17
|
+
def child?
|
18
|
+
return unless @family
|
20
19
|
|
20
|
+
@family.children.include?(self)
|
21
|
+
end
|
22
|
+
end
|
21
23
|
end
|
@@ -15,7 +15,7 @@ module HammerCLI
|
|
15
15
|
ALLOWED_EXTENSIONS = %i[
|
16
16
|
option command_options before_print data output help request
|
17
17
|
request_headers headers request_options options request_params params
|
18
|
-
option_sources predefined_options use_option
|
18
|
+
option_sources predefined_options use_option option_family
|
19
19
|
].freeze
|
20
20
|
|
21
21
|
def initialize(options = {})
|
@@ -86,6 +86,11 @@ module HammerCLI
|
|
86
86
|
@option_sources_block = block
|
87
87
|
end
|
88
88
|
|
89
|
+
def self.option_family(options = {}, &block)
|
90
|
+
@option_family_opts = options
|
91
|
+
@option_family_block = block
|
92
|
+
end
|
93
|
+
|
89
94
|
# Object
|
90
95
|
|
91
96
|
def extend_options(command_class)
|
@@ -151,6 +156,13 @@ module HammerCLI
|
|
151
156
|
self.class.extend_option_sources(sources, command)
|
152
157
|
end
|
153
158
|
|
159
|
+
def extend_option_family(command_class)
|
160
|
+
allowed = @only & %i[option_family]
|
161
|
+
return if allowed.empty? || (allowed & @except).any?
|
162
|
+
|
163
|
+
self.class.extend_option_family(command_class)
|
164
|
+
end
|
165
|
+
|
154
166
|
def delegatee(command_class)
|
155
167
|
self.class.delegatee = command_class
|
156
168
|
end
|
@@ -234,5 +246,13 @@ module HammerCLI
|
|
234
246
|
@option_sources_block.call(sources, command)
|
235
247
|
logger.debug("Called block for #{@delegatee} option sources:\n\t#{@option_sources_block}")
|
236
248
|
end
|
249
|
+
|
250
|
+
def self.extend_option_family(command_class)
|
251
|
+
return if @option_family_block.nil?
|
252
|
+
|
253
|
+
@option_family_opts[:creator] = command_class
|
254
|
+
command_class.send(:option_family, @option_family_opts, &@option_family_block)
|
255
|
+
logger.debug("Called option family block for #{command_class}:\n\t#{@option_family_block}")
|
256
|
+
end
|
237
257
|
end
|
238
258
|
end
|
@@ -69,7 +69,7 @@ module HammerCLI
|
|
69
69
|
|
70
70
|
def handle_usage_exception(e)
|
71
71
|
print_error (_("Error: %{message}") + "\n\n" +
|
72
|
-
_("See: '%{path} --help'.")) % {:
|
72
|
+
_("See: '%{path} --help'.")) % { message: e.message, path: HammerCLI.expand_invocation_path(e.command.invocation_path) }
|
73
73
|
log_full_error e
|
74
74
|
HammerCLI::EX_USAGE
|
75
75
|
end
|
data/lib/hammer_cli/full_help.rb
CHANGED
@@ -6,8 +6,11 @@ module HammerCLI
|
|
6
6
|
|
7
7
|
def execute
|
8
8
|
@adapter = option_md? ? MDAdapter.new : TxtAdapter.new
|
9
|
+
HammerCLI.context[:full_help] = true
|
10
|
+
@invocation_paths = {}
|
9
11
|
print_heading
|
10
12
|
print_help
|
13
|
+
HammerCLI.context[:full_help] = false
|
11
14
|
HammerCLI::EX_OK
|
12
15
|
end
|
13
16
|
|
@@ -19,9 +22,13 @@ module HammerCLI
|
|
19
22
|
end
|
20
23
|
|
21
24
|
def print_help(name='hammer', command=HammerCLI::MainCommand, desc='')
|
22
|
-
@
|
25
|
+
@invocation_paths[name] ||= []
|
26
|
+
@adapter.print_command(name, desc, command.new(name, path: @invocation_paths[name]).help)
|
23
27
|
|
24
28
|
command.recognised_subcommands.each do |sub_cmd|
|
29
|
+
path = "#{name} #{sub_cmd.names.first}"
|
30
|
+
@invocation_paths[path] ||= []
|
31
|
+
@invocation_paths[path] += @invocation_paths[name]
|
25
32
|
print_help(@adapter.command_name(name, sub_cmd.names.first), sub_cmd.subcommand_class, sub_cmd.description)
|
26
33
|
end
|
27
34
|
end
|
@@ -14,7 +14,7 @@ module HammerCLI
|
|
14
14
|
def add_usage(invocation_path, usage_descriptions)
|
15
15
|
heading(Clamp.message(:usage_heading))
|
16
16
|
usage_descriptions.each do |usage|
|
17
|
-
puts " #{invocation_path} #{usage}".rstrip
|
17
|
+
puts " #{HammerCLI.expand_invocation_path(invocation_path)} #{usage}".rstrip
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -29,12 +29,19 @@ module HammerCLI
|
|
29
29
|
|
30
30
|
label_width = DEFAULT_LABEL_INDENT
|
31
31
|
items.each do |item|
|
32
|
-
label
|
32
|
+
label = item.help.first
|
33
33
|
label_width = label.size if label.size > label_width
|
34
34
|
end
|
35
35
|
|
36
36
|
items.each do |item|
|
37
|
-
|
37
|
+
if item.respond_to?(:child?) && item.child?
|
38
|
+
next unless HammerCLI.context[:full_help]
|
39
|
+
end
|
40
|
+
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
|
38
45
|
description.gsub(/^(.)/) { Unicode::capitalize($1) }.each_line do |line|
|
39
46
|
puts " %-#{label_width}s %s" % [label, line]
|
40
47
|
label = ''
|
@@ -22,14 +22,12 @@ module HammerCLI
|
|
22
22
|
|
23
23
|
class OptionDefinition < Clamp::Option::Definition
|
24
24
|
|
25
|
-
attr_accessor :value_formatter
|
26
|
-
attr_accessor :context_target
|
27
|
-
attr_accessor :deprecated_switches
|
25
|
+
attr_accessor :value_formatter, :context_target, :deprecated_switches
|
28
26
|
|
29
27
|
def initialize(switches, type, description, options = {})
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
@value_formatter = options[:format] || HammerCLI::Options::Normalizers::Default.new
|
29
|
+
@context_target = options[:context_target]
|
30
|
+
@deprecated_switches = options[:deprecated]
|
33
31
|
super
|
34
32
|
end
|
35
33
|
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HammerCLI
|
4
|
+
module Options
|
5
|
+
class OptionFamily
|
6
|
+
attr_reader :children
|
7
|
+
|
8
|
+
IDS_REGEX = /\s?([Ii][Dd][s]?)\W|([Ii][Dd][s]?\Z)/
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@all = []
|
12
|
+
@children = []
|
13
|
+
@options = options
|
14
|
+
@creator = options[:creator] || Class.new(HammerCLI::Apipie::Command)
|
15
|
+
@prefix = options[:prefix]
|
16
|
+
@description = options[:description]
|
17
|
+
@root = options[:root] || options[:aliased_resource] || options[:referenced_resource]
|
18
|
+
end
|
19
|
+
|
20
|
+
def description
|
21
|
+
types = all.map(&:type).map { |s| s.split('_').last.to_s }
|
22
|
+
.map(&:capitalize).join('/')
|
23
|
+
@description ||= @parent.help[1].gsub(IDS_REGEX) { |w| w.gsub(/\w+/, types) }
|
24
|
+
if @options[:deprecation].class <= String
|
25
|
+
format_deprecation_msg(@description, _('Deprecated: %{deprecated_msg}') % { deprecated_msg: @options[:deprecation] })
|
26
|
+
elsif @options[:deprecation].class <= Hash
|
27
|
+
full_msg = @options[:deprecation].map do |flag, msg|
|
28
|
+
_('%{flag} is deprecated: %{deprecated_msg}') % { flag: flag, deprecated_msg: msg }
|
29
|
+
end.join(', ')
|
30
|
+
format_deprecation_msg(@description, full_msg)
|
31
|
+
else
|
32
|
+
@description
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def switch
|
37
|
+
return if @parent.nil? && @children.empty?
|
38
|
+
return @parent.help_lhs.strip if @children.empty?
|
39
|
+
|
40
|
+
switch_start = main_switch.each_char
|
41
|
+
.zip(*all.map(&:switches).flatten.map(&:each_char))
|
42
|
+
.select { |a, b| a == b }.transpose.first.join
|
43
|
+
suffixes = all.map do |m|
|
44
|
+
m.switches.map { |s| s.gsub(switch_start, '') }
|
45
|
+
end.flatten.reject(&:empty?).sort { |x, y| x.size <=> y.size }
|
46
|
+
"#{switch_start}[#{suffixes.join('|')}]"
|
47
|
+
end
|
48
|
+
|
49
|
+
def head
|
50
|
+
@parent
|
51
|
+
end
|
52
|
+
|
53
|
+
def all
|
54
|
+
@children + [@parent].compact
|
55
|
+
end
|
56
|
+
|
57
|
+
def parent(switches, type, description, opts = {}, &block)
|
58
|
+
raise StandardError, 'Option family can have only one parent' if @parent
|
59
|
+
|
60
|
+
@parent = new_member(switches, type, description, opts, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def child(switches, type, description, opts = {}, &block)
|
64
|
+
child = new_member(switches, type, description, opts, &block)
|
65
|
+
@children << child
|
66
|
+
child
|
67
|
+
end
|
68
|
+
|
69
|
+
def adopt(child)
|
70
|
+
child.family = self
|
71
|
+
@children << child
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def format_deprecation_msg(option_desc, deprecation_msg)
|
77
|
+
"#{option_desc} (#{deprecation_msg})"
|
78
|
+
end
|
79
|
+
|
80
|
+
def new_member(switches, type, description, opts = {}, &block)
|
81
|
+
opts = opts.merge(@options)
|
82
|
+
opts[:family] = self
|
83
|
+
if opts[:deprecated]
|
84
|
+
handles = [switches].flatten
|
85
|
+
opts[:deprecated] = opts[:deprecated].select do |switch, _msg|
|
86
|
+
handles.include?(switch)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
@creator.instance_eval do
|
90
|
+
option(switches, type, description, opts, &block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def main_switch
|
95
|
+
root = @root || @parent.aliased_resource || @parent.referenced_resource || common_root
|
96
|
+
"--#{@prefix}#{root}".tr('_', '-')
|
97
|
+
end
|
98
|
+
|
99
|
+
def common_root
|
100
|
+
switches = all.map(&:switches).flatten
|
101
|
+
shortest = switches.min_by(&:length)
|
102
|
+
max_len = shortest.length
|
103
|
+
max_len.downto(0) do |curr_len|
|
104
|
+
0.upto(max_len - curr_len) do |start|
|
105
|
+
root = shortest[start, curr_len]
|
106
|
+
if switches.all? { |switch| switch.include?(root) }
|
107
|
+
return root[2..-1].chomp('-')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -6,7 +6,7 @@ module HammerCLI
|
|
6
6
|
module Predefined
|
7
7
|
OPTIONS = {
|
8
8
|
fields: [['--fields'], 'FIELDS',
|
9
|
-
_('Show specified
|
9
|
+
_('Show specified fields or predefined field sets only. (See below)'),
|
10
10
|
format: HammerCLI::Options::Normalizers::List.new,
|
11
11
|
context_target: :fields]
|
12
12
|
}.freeze
|
@@ -23,6 +23,11 @@ module HammerCLI
|
|
23
23
|
@subcommand_class
|
24
24
|
end
|
25
25
|
|
26
|
+
def help
|
27
|
+
names = HammerCLI.context[:full_help] ? @names.join(", ") : @names.first
|
28
|
+
[names, description]
|
29
|
+
end
|
30
|
+
|
26
31
|
attr_reader :warning
|
27
32
|
end
|
28
33
|
|
@@ -90,8 +95,27 @@ module HammerCLI
|
|
90
95
|
logger.info "subcommand #{name} (#{subcommand_class_name}) was created."
|
91
96
|
end
|
92
97
|
|
98
|
+
def find_subcommand(name, fuzzy: true)
|
99
|
+
subcommand = super(name)
|
100
|
+
if subcommand.nil? && fuzzy
|
101
|
+
find_subcommand_starting_with(name)
|
102
|
+
else
|
103
|
+
subcommand
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def find_subcommand_starting_with(name)
|
108
|
+
subcommands = recognised_subcommands.select { |sc| sc.names.any? { |n| n.start_with?(name) } }
|
109
|
+
if subcommands.size > 1
|
110
|
+
raise HammerCLI::CommandConflict, _('Found more than one command.') + "\n\n" +
|
111
|
+
_('Did you mean one of these?') + "\n\t" +
|
112
|
+
subcommands.collect(&:names).flatten.select { |n| n.start_with?(name) }.join("\n\t")
|
113
|
+
end
|
114
|
+
subcommands.first
|
115
|
+
end
|
116
|
+
|
93
117
|
def define_subcommand(name, subcommand_class, definition, &block)
|
94
|
-
existing = find_subcommand(name)
|
118
|
+
existing = find_subcommand(name, fuzzy: false)
|
95
119
|
if existing
|
96
120
|
raise HammerCLI::CommandConflict, _("Can't replace subcommand %<name>s (%<existing_class>s) with %<name>s (%<new_class>s).") % {
|
97
121
|
:name => name,
|
@@ -71,13 +71,13 @@ module HammerCLI
|
|
71
71
|
if heading.nil?
|
72
72
|
["Error: #{message}",
|
73
73
|
"",
|
74
|
-
"See: '#{command} --help'.",
|
74
|
+
"See: '#{HammerCLI.expand_invocation_path(command)} --help'.",
|
75
75
|
""].join("\n")
|
76
76
|
else
|
77
77
|
["#{heading}:",
|
78
78
|
" Error: #{message}",
|
79
79
|
" ",
|
80
|
-
" See: '#{command} --help'.",
|
80
|
+
" See: '#{HammerCLI.expand_invocation_path(command)} --help'.",
|
81
81
|
""].join("\n")
|
82
82
|
end
|
83
83
|
end
|
data/lib/hammer_cli/utils.rb
CHANGED
@@ -116,4 +116,21 @@ module HammerCLI
|
|
116
116
|
|
117
117
|
array.insert(idx, *new_items)
|
118
118
|
end
|
119
|
+
|
120
|
+
def self.expand_invocation_path(path)
|
121
|
+
bits = path.split(' ')
|
122
|
+
parent_command = HammerCLI::MainCommand
|
123
|
+
new_path = (bits[1..-1] || []).each_with_object([]) do |bit, names|
|
124
|
+
subcommand = parent_command.find_subcommand(bit, fuzzy: false)
|
125
|
+
next if subcommand.nil?
|
126
|
+
|
127
|
+
names << if subcommand.names.size > 1
|
128
|
+
"<#{subcommand.names.join('|')}>"
|
129
|
+
else
|
130
|
+
subcommand.names.first
|
131
|
+
end
|
132
|
+
parent_command = subcommand.subcommand_class
|
133
|
+
end
|
134
|
+
new_path.unshift(bits.first).join(' ')
|
135
|
+
end
|
119
136
|
end
|
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/man/hammer.1.gz
CHANGED
Binary file
|
data/test/unit/abstract_test.rb
CHANGED
@@ -262,6 +262,27 @@ describe HammerCLI::AbstractCommand do
|
|
262
262
|
end
|
263
263
|
|
264
264
|
end
|
265
|
+
|
266
|
+
describe 'find_subcommand' do
|
267
|
+
it 'should find by full name' do
|
268
|
+
main_cmd.find_subcommand('some_command').wont_be_nil
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'should find by partial name' do
|
272
|
+
main_cmd.find_subcommand('some_').wont_be_nil
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'should not find by wrong name' do
|
276
|
+
main_cmd.find_subcommand('not_existing').must_be_nil
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'should raise if more than one were found' do
|
280
|
+
main_cmd.subcommand('pong', 'description', Subcommand2)
|
281
|
+
proc do
|
282
|
+
main_cmd.find_subcommand('p')
|
283
|
+
end.must_raise HammerCLI::CommandConflict
|
284
|
+
end
|
285
|
+
end
|
265
286
|
end
|
266
287
|
|
267
288
|
describe "options" do
|
@@ -413,7 +434,7 @@ describe HammerCLI::AbstractCommand do
|
|
413
434
|
class CmdName2 < CmdName1
|
414
435
|
end
|
415
436
|
|
416
|
-
CmdName2.command_name.must_equal 'cmd'
|
437
|
+
CmdName2.command_name.must_equal ['cmd']
|
417
438
|
end
|
418
439
|
|
419
440
|
it "should inherit output definition" do
|
@@ -525,7 +546,7 @@ describe HammerCLI::AbstractCommand do
|
|
525
546
|
opt = cmd.find_option('--flag')
|
526
547
|
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal true
|
527
548
|
cmd.output_definition.empty?.must_equal false
|
528
|
-
cmd.new({}).help.must_match(/.*text.*/)
|
549
|
+
cmd.new('', {}).help.must_match(/.*text.*/)
|
529
550
|
end
|
530
551
|
|
531
552
|
it 'should store more than one extension' do
|
@@ -37,6 +37,14 @@ describe HammerCLI::Apipie::OptionBuilder do
|
|
37
37
|
it "should set description with html tags stripped" do
|
38
38
|
options[0].description.must_equal 'filter results'
|
39
39
|
end
|
40
|
+
|
41
|
+
it "should build option with default family" do
|
42
|
+
options[0].family.wont_be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should build parent option within default family" do
|
46
|
+
options[0].child?.must_equal false
|
47
|
+
end
|
40
48
|
end
|
41
49
|
|
42
50
|
|
@@ -23,6 +23,12 @@ describe HammerCLI::CommandExtensions do
|
|
23
23
|
|
24
24
|
class CmdExtensions < HammerCLI::CommandExtensions
|
25
25
|
option '--ext', 'EXT', 'ext'
|
26
|
+
option_family(
|
27
|
+
description: 'Test',
|
28
|
+
) do
|
29
|
+
parent '--test-one', '', ''
|
30
|
+
child '--test-two', '', ''
|
31
|
+
end
|
26
32
|
before_print do |data|
|
27
33
|
data['key'] = 'value'
|
28
34
|
end
|
@@ -64,49 +70,55 @@ describe HammerCLI::CommandExtensions do
|
|
64
70
|
|
65
71
|
it 'should extend help only' do
|
66
72
|
cmd.extend_with(CmdExtensions.new(only: :help))
|
67
|
-
cmd.new({}).help.must_match(/.*Section.*/)
|
68
|
-
cmd.new({}).help.must_match(/.*text.*/)
|
73
|
+
cmd.new('', {}).help.must_match(/.*Section.*/)
|
74
|
+
cmd.new('', {}).help.must_match(/.*text.*/)
|
69
75
|
end
|
70
76
|
|
71
77
|
it 'should extend params only' do
|
72
78
|
cmd.extend_with(CmdExtensions.new(only: :request_params))
|
73
|
-
cmd.new({}).extended_request[0].must_equal(thin: true)
|
74
|
-
cmd.new({}).extended_request[1].must_equal({})
|
75
|
-
cmd.new({}).extended_request[2].must_equal({})
|
79
|
+
cmd.new('', {}).extended_request[0].must_equal(thin: true)
|
80
|
+
cmd.new('', {}).extended_request[1].must_equal({})
|
81
|
+
cmd.new('', {}).extended_request[2].must_equal({})
|
76
82
|
end
|
77
83
|
|
78
84
|
it 'should extend headers only' do
|
79
85
|
cmd.extend_with(CmdExtensions.new(only: :request_headers))
|
80
|
-
cmd.new({}).extended_request[0].must_equal({})
|
81
|
-
cmd.new({}).extended_request[1].must_equal(ssl: true)
|
82
|
-
cmd.new({}).extended_request[2].must_equal({})
|
86
|
+
cmd.new('', {}).extended_request[0].must_equal({})
|
87
|
+
cmd.new('', {}).extended_request[1].must_equal(ssl: true)
|
88
|
+
cmd.new('', {}).extended_request[2].must_equal({})
|
83
89
|
end
|
84
90
|
|
85
91
|
it 'should extend options only' do
|
86
92
|
cmd.extend_with(CmdExtensions.new(only: :request_options))
|
87
|
-
cmd.new({}).extended_request[0].must_equal({})
|
88
|
-
cmd.new({}).extended_request[1].must_equal({})
|
89
|
-
cmd.new({}).extended_request[2].must_equal(with_authentication: true)
|
93
|
+
cmd.new('', {}).extended_request[0].must_equal({})
|
94
|
+
cmd.new('', {}).extended_request[1].must_equal({})
|
95
|
+
cmd.new('', {}).extended_request[2].must_equal(with_authentication: true)
|
90
96
|
end
|
91
97
|
|
92
98
|
it 'should extend params and options and headers' do
|
93
99
|
cmd.extend_with(CmdExtensions.new(only: :request))
|
94
|
-
cmd.new({}).extended_request[0].must_equal(thin: true)
|
95
|
-
cmd.new({}).extended_request[1].must_equal(ssl: true)
|
96
|
-
cmd.new({}).extended_request[2].must_equal(with_authentication: true)
|
100
|
+
cmd.new('', {}).extended_request[0].must_equal(thin: true)
|
101
|
+
cmd.new('', {}).extended_request[1].must_equal(ssl: true)
|
102
|
+
cmd.new('', {}).extended_request[2].must_equal(with_authentication: true)
|
97
103
|
end
|
98
104
|
|
99
105
|
it 'should extend data only' do
|
100
106
|
cmd.extend_with(CmdExtensions.new(only: :data))
|
101
|
-
cmd.new({}).help.wont_match(/.*Section.*/)
|
102
|
-
cmd.new({}).help.wont_match(/.*text.*/)
|
107
|
+
cmd.new('', {}).help.wont_match(/.*Section.*/)
|
108
|
+
cmd.new('', {}).help.wont_match(/.*text.*/)
|
103
109
|
cmd.output_definition.empty?.must_equal true
|
104
110
|
opt = cmd.find_option('--ext')
|
105
111
|
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal false
|
106
|
-
cmd.new({}).extended_request[0].must_equal({})
|
107
|
-
cmd.new({}).extended_request[1].must_equal({})
|
108
|
-
cmd.new({}).extended_request[2].must_equal({})
|
109
|
-
cmd.new({}).extended_data({}).must_equal('key' => 'value')
|
112
|
+
cmd.new('', {}).extended_request[0].must_equal({})
|
113
|
+
cmd.new('', {}).extended_request[1].must_equal({})
|
114
|
+
cmd.new('', {}).extended_request[2].must_equal({})
|
115
|
+
cmd.new('', {}).extended_data({}).must_equal('key' => 'value')
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should extend option family only' do
|
119
|
+
cmd.extend_with(CmdExtensions.new(only: :option_family))
|
120
|
+
cmd.output_definition.empty?.must_equal true
|
121
|
+
cmd.recognised_options.map(&:switches).flatten.must_equal ['--test-one', '--test-two', '-h', '--help']
|
110
122
|
end
|
111
123
|
end
|
112
124
|
|
@@ -116,9 +128,9 @@ describe HammerCLI::CommandExtensions do
|
|
116
128
|
opt = cmd.find_option('--ext')
|
117
129
|
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal false
|
118
130
|
cmd.output_definition.empty?.must_equal false
|
119
|
-
cmd.new({}).extended_request[0].must_equal(thin: true)
|
120
|
-
cmd.new({}).extended_request[1].must_equal(ssl: true)
|
121
|
-
cmd.new({}).extended_request[2].must_equal(with_authentication: true)
|
131
|
+
cmd.new('', {}).extended_request[0].must_equal(thin: true)
|
132
|
+
cmd.new('', {}).extended_request[1].must_equal(ssl: true)
|
133
|
+
cmd.new('', {}).extended_request[2].must_equal(with_authentication: true)
|
122
134
|
end
|
123
135
|
|
124
136
|
it 'should extend all except output' do
|
@@ -126,62 +138,68 @@ describe HammerCLI::CommandExtensions do
|
|
126
138
|
cmd.output_definition.empty?.must_equal true
|
127
139
|
opt = cmd.find_option('--ext')
|
128
140
|
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal true
|
129
|
-
cmd.new({}).extended_request[0].must_equal(thin: true)
|
130
|
-
cmd.new({}).extended_request[1].must_equal(ssl: true)
|
131
|
-
cmd.new({}).extended_request[2].must_equal(with_authentication: true)
|
141
|
+
cmd.new('', {}).extended_request[0].must_equal(thin: true)
|
142
|
+
cmd.new('', {}).extended_request[1].must_equal(ssl: true)
|
143
|
+
cmd.new('', {}).extended_request[2].must_equal(with_authentication: true)
|
132
144
|
end
|
133
145
|
|
134
146
|
it 'should extend all except help' do
|
135
147
|
cmd.extend_with(CmdExtensions.new(except: :help))
|
136
|
-
cmd.new({}).help.wont_match(/.*Section.*/)
|
137
|
-
cmd.new({}).help.wont_match(/.*text.*/)
|
148
|
+
cmd.new('', {}).help.wont_match(/.*Section.*/)
|
149
|
+
cmd.new('', {}).help.wont_match(/.*text.*/)
|
138
150
|
cmd.output_definition.empty?.must_equal false
|
139
151
|
opt = cmd.find_option('--ext')
|
140
152
|
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal true
|
141
|
-
cmd.new({}).extended_request[0].must_equal(thin: true)
|
142
|
-
cmd.new({}).extended_request[1].must_equal(ssl: true)
|
143
|
-
cmd.new({}).extended_request[2].must_equal(with_authentication: true)
|
153
|
+
cmd.new('', {}).extended_request[0].must_equal(thin: true)
|
154
|
+
cmd.new('', {}).extended_request[1].must_equal(ssl: true)
|
155
|
+
cmd.new('', {}).extended_request[2].must_equal(with_authentication: true)
|
144
156
|
end
|
145
157
|
|
146
158
|
it 'should extend all except params' do
|
147
159
|
cmd.extend_with(CmdExtensions.new(except: :request_params))
|
148
|
-
cmd.new({}).extended_request[0].must_equal({})
|
149
|
-
cmd.new({}).extended_request[1].must_equal(ssl: true)
|
150
|
-
cmd.new({}).extended_request[2].must_equal(with_authentication: true)
|
160
|
+
cmd.new('', {}).extended_request[0].must_equal({})
|
161
|
+
cmd.new('', {}).extended_request[1].must_equal(ssl: true)
|
162
|
+
cmd.new('', {}).extended_request[2].must_equal(with_authentication: true)
|
151
163
|
end
|
152
164
|
|
153
165
|
it 'should extend all except headers' do
|
154
166
|
cmd.extend_with(CmdExtensions.new(except: :request_headers))
|
155
|
-
cmd.new({}).extended_request[0].must_equal(thin: true)
|
156
|
-
cmd.new({}).extended_request[1].must_equal({})
|
157
|
-
cmd.new({}).extended_request[2].must_equal(with_authentication: true)
|
167
|
+
cmd.new('', {}).extended_request[0].must_equal(thin: true)
|
168
|
+
cmd.new('', {}).extended_request[1].must_equal({})
|
169
|
+
cmd.new('', {}).extended_request[2].must_equal(with_authentication: true)
|
158
170
|
end
|
159
171
|
|
160
172
|
it 'should extend all except options' do
|
161
173
|
cmd.extend_with(CmdExtensions.new(except: :request_options))
|
162
|
-
cmd.new({}).extended_request[0].must_equal(thin: true)
|
163
|
-
cmd.new({}).extended_request[1].must_equal(ssl: true)
|
164
|
-
cmd.new({}).extended_request[2].must_equal({})
|
174
|
+
cmd.new('', {}).extended_request[0].must_equal(thin: true)
|
175
|
+
cmd.new('', {}).extended_request[1].must_equal(ssl: true)
|
176
|
+
cmd.new('', {}).extended_request[2].must_equal({})
|
165
177
|
end
|
166
178
|
|
167
179
|
it 'should extend all except params and options and headers' do
|
168
180
|
cmd.extend_with(CmdExtensions.new(except: :request))
|
169
|
-
cmd.new({}).extended_request[0].must_equal({})
|
170
|
-
cmd.new({}).extended_request[1].must_equal({})
|
171
|
-
cmd.new({}).extended_request[2].must_equal({})
|
181
|
+
cmd.new('', {}).extended_request[0].must_equal({})
|
182
|
+
cmd.new('', {}).extended_request[1].must_equal({})
|
183
|
+
cmd.new('', {}).extended_request[2].must_equal({})
|
172
184
|
end
|
173
185
|
|
174
186
|
it 'should extend all except data' do
|
175
187
|
cmd.extend_with(CmdExtensions.new(except: :data))
|
176
|
-
cmd.new({}).help.must_match(/.*Section.*/)
|
177
|
-
cmd.new({}).help.must_match(/.*text.*/)
|
188
|
+
cmd.new('', {}).help.must_match(/.*Section.*/)
|
189
|
+
cmd.new('', {}).help.must_match(/.*text.*/)
|
178
190
|
cmd.output_definition.empty?.must_equal false
|
179
191
|
opt = cmd.find_option('--ext')
|
180
192
|
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal true
|
181
|
-
cmd.new({}).extended_request[0].must_equal(thin: true)
|
182
|
-
cmd.new({}).extended_request[1].must_equal(ssl: true)
|
183
|
-
cmd.new({}).extended_request[2].must_equal(with_authentication: true)
|
184
|
-
cmd.new({}).extended_data({}).must_equal({})
|
193
|
+
cmd.new('', {}).extended_request[0].must_equal(thin: true)
|
194
|
+
cmd.new('', {}).extended_request[1].must_equal(ssl: true)
|
195
|
+
cmd.new('', {}).extended_request[2].must_equal(with_authentication: true)
|
196
|
+
cmd.new('', {}).extended_data({}).must_equal({})
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should extend all except option family' do
|
200
|
+
cmd.extend_with(CmdExtensions.new(except: :option_family))
|
201
|
+
cmd.output_definition.empty?.must_equal false
|
202
|
+
cmd.recognised_options.map(&:switches).flatten.must_equal ['--ext', '-h', '--help']
|
185
203
|
end
|
186
204
|
end
|
187
205
|
|
@@ -69,4 +69,26 @@ describe HammerCLI::Help::Builder do
|
|
69
69
|
help.string.strip.must_equal expected_output
|
70
70
|
end
|
71
71
|
end
|
72
|
+
|
73
|
+
describe 'option family' do
|
74
|
+
let(:family) { Class.new(HammerCLI::Options::OptionFamily) }
|
75
|
+
|
76
|
+
it 'prints option families' do
|
77
|
+
fm1 = family.new
|
78
|
+
fm1.parent(['--option-zzz'], 'OPT', 'Some description')
|
79
|
+
fm1.child(['--option-aaa'], 'OPT', 'Some description')
|
80
|
+
fm2 = family.new
|
81
|
+
fm2.parent(['--option-bbb'], 'OPT', 'Some description')
|
82
|
+
fm2.child(['--option-yyy'], 'OPT', 'Some description')
|
83
|
+
|
84
|
+
options = fm1.all + fm2.all
|
85
|
+
help.add_list('Options', options)
|
86
|
+
|
87
|
+
help.string.strip.must_equal [
|
88
|
+
'Options:',
|
89
|
+
' --option[-yyy|-bbb] Some description',
|
90
|
+
' --option[-aaa|-zzz] Some description',
|
91
|
+
].join("\n")
|
92
|
+
end
|
93
|
+
end
|
72
94
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
describe HammerCLI::Options::OptionFamily do
|
4
|
+
let(:family) do
|
5
|
+
HammerCLI::Options::OptionFamily.new(
|
6
|
+
deprecated: { '--test-one' => 'Use --test-two instead' }
|
7
|
+
)
|
8
|
+
end
|
9
|
+
let(:first_option) { HammerCLI::Apipie::OptionDefinition.new("--test-one", '', '') }
|
10
|
+
let(:second_option) { HammerCLI::Apipie::OptionDefinition.new("--test-two", '', '') }
|
11
|
+
let(:third_option) { HammerCLI::Apipie::OptionDefinition.new("--test-three", '', '') }
|
12
|
+
let(:full_family) do
|
13
|
+
family.parent('--test-one', '', 'Test').family.child('--test-two', '', '').family
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'switch' do
|
17
|
+
it 'returns nil if family is empty' do
|
18
|
+
family.switch.must_be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns parent switch if family has no children' do
|
22
|
+
family.parent('--test-one', '', '')
|
23
|
+
family.switch.must_equal '--test-one'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns switch based on members' do
|
27
|
+
full_family.switch.must_equal '--test[-two|-one]'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'description' do
|
32
|
+
it 'returns parent description if nothing passed to initializer' do
|
33
|
+
full_family.description.must_equal full_family.head.help[1]
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns description with deprecation message' do
|
37
|
+
full_family.description.must_equal 'Test (--test-one is deprecated: Use --test-two instead)'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'adopt' do
|
42
|
+
it 'appends an option to children' do
|
43
|
+
full_family.adopt(third_option)
|
44
|
+
full_family.children.size.must_equal 2
|
45
|
+
third_option.family.must_equal full_family
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
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: 2.
|
4
|
+
version: 2.2.1
|
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: 2020-
|
12
|
+
date: 2020-09-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: clamp
|
@@ -74,7 +74,7 @@ dependencies:
|
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
- !ruby/object:Gem::Dependency
|
77
|
-
name:
|
77
|
+
name: amazing_print
|
78
78
|
requirement: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - ">="
|
@@ -153,26 +153,26 @@ executables:
|
|
153
153
|
extensions: []
|
154
154
|
extra_rdoc_files:
|
155
155
|
- doc/commands_extension.md
|
156
|
-
- doc/release_notes.md
|
157
|
-
- doc/design.png
|
158
156
|
- doc/commands_modification.md
|
159
|
-
- doc/
|
160
|
-
- doc/
|
161
|
-
- doc/
|
162
|
-
- doc/
|
157
|
+
- doc/creating_apipie_commands.md
|
158
|
+
- doc/creating_commands.md
|
159
|
+
- doc/design.png
|
160
|
+
- doc/design.uml
|
163
161
|
- doc/developer_docs.md
|
162
|
+
- doc/development_tips.md
|
163
|
+
- doc/help_modification.md
|
164
|
+
- doc/i18n.md
|
165
|
+
- doc/installation.md
|
164
166
|
- doc/installation_deb.md
|
165
|
-
- doc/option_normalizers.md
|
166
|
-
- doc/installation_rpm.md
|
167
167
|
- doc/installation_gem.md
|
168
|
-
- doc/help_modification.md
|
169
|
-
- doc/creating_commands.md
|
170
|
-
- doc/installation_source.md
|
171
168
|
- doc/option_builders.md
|
172
|
-
- doc/
|
169
|
+
- doc/option_normalizers.md
|
170
|
+
- doc/output.md
|
173
171
|
- doc/review_checklist.md
|
174
|
-
- doc/
|
175
|
-
- doc/
|
172
|
+
- doc/writing_a_plugin.md
|
173
|
+
- doc/installation_rpm.md
|
174
|
+
- doc/installation_source.md
|
175
|
+
- doc/release_notes.md
|
176
176
|
- config/cli.modules.d/module_config_template.yml
|
177
177
|
- config/cli_config.template.yml
|
178
178
|
- config/hammer.completion
|
@@ -252,6 +252,7 @@ files:
|
|
252
252
|
- lib/hammer_cli/options/normalizers.rb
|
253
253
|
- lib/hammer_cli/options/option_collector.rb
|
254
254
|
- lib/hammer_cli/options/option_definition.rb
|
255
|
+
- lib/hammer_cli/options/option_family.rb
|
255
256
|
- lib/hammer_cli/options/option_processor.rb
|
256
257
|
- lib/hammer_cli/options/predefined.rb
|
257
258
|
- lib/hammer_cli/options/processor_list.rb
|
@@ -353,6 +354,7 @@ files:
|
|
353
354
|
- test/unit/options/normalizers_test.rb
|
354
355
|
- test/unit/options/option_collector_test.rb
|
355
356
|
- test/unit/options/option_definition_test.rb
|
357
|
+
- test/unit/options/option_family_test.rb
|
356
358
|
- test/unit/options/processor_list_test.rb
|
357
359
|
- test/unit/options/sources/command_line_test.rb
|
358
360
|
- test/unit/options/sources/saved_defaults_test.rb
|
@@ -392,74 +394,75 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
392
394
|
- !ruby/object:Gem::Version
|
393
395
|
version: '0'
|
394
396
|
requirements: []
|
395
|
-
rubygems_version: 3.
|
397
|
+
rubygems_version: 3.1.2
|
396
398
|
signing_key:
|
397
399
|
specification_version: 4
|
398
400
|
summary: Universal command-line interface
|
399
401
|
test_files:
|
400
|
-
- test/
|
401
|
-
- test/
|
402
|
-
- test/
|
403
|
-
- test/
|
404
|
-
- test/
|
405
|
-
- test/unit/
|
406
|
-
- test/unit/
|
407
|
-
- test/unit/test_helper.rb
|
408
|
-
- test/unit/utils_test.rb
|
402
|
+
- test/functional/defaults_test.rb
|
403
|
+
- test/functional/help_test.rb
|
404
|
+
- test/functional/nil_values_test.rb
|
405
|
+
- test/functional/test_helper.rb
|
406
|
+
- test/test_helper.rb
|
407
|
+
- test/unit/abstract_test.rb
|
408
|
+
- test/unit/apipie/api_connection_test.rb
|
409
409
|
- test/unit/apipie/command_test.rb
|
410
410
|
- test/unit/apipie/option_builder_test.rb
|
411
|
-
- test/unit/apipie/test_helper.rb
|
412
|
-
- test/unit/apipie/api_connection_test.rb
|
413
411
|
- test/unit/apipie/option_definition_test.rb
|
412
|
+
- test/unit/apipie/test_helper.rb
|
413
|
+
- test/unit/bash_test.rb
|
414
|
+
- test/unit/ca_cert_manager_test.rb
|
414
415
|
- test/unit/command_extensions_test.rb
|
415
|
-
- test/unit/
|
416
|
-
- test/unit/
|
417
|
-
- test/unit/
|
418
|
-
- test/unit/options/sources/command_line_test.rb
|
419
|
-
- test/unit/options/sources/saved_defaults_test.rb
|
420
|
-
- test/unit/options/matcher_test.rb
|
421
|
-
- test/unit/options/option_collector_test.rb
|
422
|
-
- test/unit/options/normalizers_test.rb
|
423
|
-
- test/unit/options/option_definition_test.rb
|
424
|
-
- test/unit/settings_test.rb
|
425
|
-
- test/unit/history_test.rb
|
416
|
+
- test/unit/completer_test.rb
|
417
|
+
- test/unit/connection_test.rb
|
418
|
+
- test/unit/csv_parser_test.rb
|
426
419
|
- test/unit/defaults_test.rb
|
427
|
-
- test/unit/
|
428
|
-
- test/unit/output/formatters_test.rb
|
429
|
-
- test/unit/output/record_collection_test.rb
|
430
|
-
- test/unit/output/dsl_test.rb
|
431
|
-
- test/unit/output/definition_test.rb
|
432
|
-
- test/unit/output/output_test.rb
|
433
|
-
- test/unit/output/field_filter_test.rb
|
434
|
-
- test/unit/output/fields_test.rb
|
435
|
-
- test/unit/output/adapter/table_test.rb
|
436
|
-
- test/unit/output/adapter/json_test.rb
|
437
|
-
- test/unit/output/adapter/abstract_test.rb
|
438
|
-
- test/unit/output/adapter/yaml_test.rb
|
439
|
-
- test/unit/output/adapter/csv_test.rb
|
440
|
-
- test/unit/output/adapter/base_test.rb
|
441
|
-
- test/unit/abstract_test.rb
|
442
|
-
- test/unit/i18n_test.rb
|
420
|
+
- test/unit/exception_handler_test.rb
|
443
421
|
- test/unit/fixtures/apipie/architectures.json
|
444
422
|
- test/unit/fixtures/apipie/documented.json
|
445
|
-
- test/unit/fixtures/defaults/defaults_dashed.yml
|
446
|
-
- test/unit/fixtures/defaults/defaults.yml
|
447
|
-
- test/unit/fixtures/json_input/valid.json
|
448
|
-
- test/unit/fixtures/json_input/invalid.json
|
449
423
|
- test/unit/fixtures/certs/ca_cert.pem
|
450
424
|
- test/unit/fixtures/certs/non_ca_cert.pem
|
451
|
-
- test/unit/
|
452
|
-
- test/unit/
|
453
|
-
- test/unit/
|
454
|
-
- test/unit/
|
425
|
+
- test/unit/fixtures/defaults/defaults.yml
|
426
|
+
- test/unit/fixtures/defaults/defaults_dashed.yml
|
427
|
+
- test/unit/fixtures/json_input/invalid.json
|
428
|
+
- test/unit/fixtures/json_input/valid.json
|
429
|
+
- test/unit/help/builder_test.rb
|
455
430
|
- test/unit/help/definition/abstract_item_test.rb
|
431
|
+
- test/unit/help/definition/list_test.rb
|
456
432
|
- test/unit/help/definition/note_test.rb
|
433
|
+
- test/unit/help/definition/section_test.rb
|
434
|
+
- test/unit/help/definition/text_test.rb
|
457
435
|
- test/unit/help/definition_test.rb
|
458
|
-
- test/unit/help/builder_test.rb
|
459
436
|
- test/unit/help/text_builder_test.rb
|
460
|
-
- test/unit/
|
461
|
-
- test/
|
462
|
-
- test/
|
463
|
-
- test/
|
464
|
-
- test/
|
465
|
-
- test/
|
437
|
+
- test/unit/history_test.rb
|
438
|
+
- test/unit/i18n_test.rb
|
439
|
+
- test/unit/logger_test.rb
|
440
|
+
- test/unit/main_test.rb
|
441
|
+
- test/unit/messages_test.rb
|
442
|
+
- test/unit/modules_test.rb
|
443
|
+
- test/unit/option_builder_test.rb
|
444
|
+
- test/unit/options/matcher_test.rb
|
445
|
+
- test/unit/options/normalizers_test.rb
|
446
|
+
- test/unit/options/option_collector_test.rb
|
447
|
+
- test/unit/options/option_definition_test.rb
|
448
|
+
- test/unit/options/option_family_test.rb
|
449
|
+
- test/unit/options/processor_list_test.rb
|
450
|
+
- test/unit/options/sources/command_line_test.rb
|
451
|
+
- test/unit/options/sources/saved_defaults_test.rb
|
452
|
+
- test/unit/options/validators/dsl_test.rb
|
453
|
+
- test/unit/output/adapter/abstract_test.rb
|
454
|
+
- test/unit/output/adapter/base_test.rb
|
455
|
+
- test/unit/output/adapter/csv_test.rb
|
456
|
+
- test/unit/output/adapter/json_test.rb
|
457
|
+
- test/unit/output/adapter/table_test.rb
|
458
|
+
- test/unit/output/adapter/yaml_test.rb
|
459
|
+
- test/unit/output/definition_test.rb
|
460
|
+
- test/unit/output/dsl_test.rb
|
461
|
+
- test/unit/output/field_filter_test.rb
|
462
|
+
- test/unit/output/fields_test.rb
|
463
|
+
- test/unit/output/formatters_test.rb
|
464
|
+
- test/unit/output/output_test.rb
|
465
|
+
- test/unit/output/record_collection_test.rb
|
466
|
+
- test/unit/settings_test.rb
|
467
|
+
- test/unit/test_helper.rb
|
468
|
+
- test/unit/utils_test.rb
|