hammer_cli 0.16.0 → 0.17.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 +5 -5
- data/doc/commands_extension.md +115 -0
- data/doc/creating_commands.md +27 -0
- data/doc/developer_docs.md +1 -0
- data/doc/release_notes.md +6 -0
- data/lib/hammer_cli/abstract.rb +59 -2
- data/lib/hammer_cli/apipie/command.rb +23 -4
- data/lib/hammer_cli/apipie/option_builder.rb +5 -1
- data/lib/hammer_cli/command_extensions.rb +222 -0
- data/lib/hammer_cli/options/normalizers.rb +55 -5
- data/lib/hammer_cli/options/option_definition.rb +3 -3
- data/lib/hammer_cli/output/adapter/table.rb +2 -2
- data/lib/hammer_cli/subcommand.rb +4 -2
- 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 +29 -1
- data/test/unit/command_extensions_test.rb +189 -0
- data/test/unit/options/normalizers_test.rb +92 -5
- metadata +8 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 025b01a30fd003ed027068563ad6971163cdfaea6a65749d775837df3552ea5e
|
|
4
|
+
data.tar.gz: 99177e6377a8fecf639cfe80fd6c79868a2ef24b6638c692d54da3ed8297c9d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b9d259322165498e492bed10e91e8a73b200b8ca78a72fd3011af3cb7fec4d73861274f9a6c23d597c3b4294a25facc7cec1bb4e02c719856a6ecc1de4c15387
|
|
7
|
+
data.tar.gz: 8e09c9a846262861b6a86f9ea4dc4dbaeb9bde089f35fe6f8f30b2066ab4e611ed9a8557b25d063bd2158a9811d5f73f4541f65b57e4104072551055015928b4
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Extend an existing command
|
|
2
|
+
-------------------------
|
|
3
|
+
|
|
4
|
+
Each command can be easily extended with one ore more `HammerCLI::CommandExtensions`:
|
|
5
|
+
- Define the extension
|
|
6
|
+
```ruby
|
|
7
|
+
class Extensions < HammerCLI::CommandExtensions
|
|
8
|
+
# Tells if those extensions are inherited by subcommands (false by default)
|
|
9
|
+
# Can be changed for specific object, e.g. MyExtensions.new(inheritable: false)
|
|
10
|
+
inheritable true
|
|
11
|
+
# Simply add a new option to a command is being extended
|
|
12
|
+
option(option_params)
|
|
13
|
+
# Extend hash with data returned from server before it is printed
|
|
14
|
+
before_print do |data|
|
|
15
|
+
# data modifications
|
|
16
|
+
end
|
|
17
|
+
# Extend command's output definition
|
|
18
|
+
output do |definition|
|
|
19
|
+
# output definition modifications
|
|
20
|
+
end
|
|
21
|
+
# Extend command's help definition
|
|
22
|
+
help do |h|
|
|
23
|
+
# help modifications
|
|
24
|
+
end
|
|
25
|
+
# Extend hash with headers before request is sent
|
|
26
|
+
request_headers do |headers|
|
|
27
|
+
# headers modifications
|
|
28
|
+
end
|
|
29
|
+
# Extend hash with options before request is sent
|
|
30
|
+
request_options do |options|
|
|
31
|
+
# options modifications
|
|
32
|
+
end
|
|
33
|
+
# Extend hash with params before request is sent
|
|
34
|
+
request_params do |params|
|
|
35
|
+
# params modifications
|
|
36
|
+
end
|
|
37
|
+
# Extend option sources
|
|
38
|
+
option_sources do |sources, command|
|
|
39
|
+
# no need to call super method
|
|
40
|
+
# simply add your sources to sources variable
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
```
|
|
44
|
+
- Extend the command
|
|
45
|
+
```ruby
|
|
46
|
+
MyCommand.extend_with(Extensions.new)
|
|
47
|
+
# Also it is possible to specify exact extensions you want to apply
|
|
48
|
+
# This can be useful when you want to use several extensions
|
|
49
|
+
MyCommand.extend_with(
|
|
50
|
+
# Apply only the output extensions from Extensions
|
|
51
|
+
Extensions.new(only: :output),
|
|
52
|
+
# Apply all except output and help extensions from OtherExtensions
|
|
53
|
+
OtherExtensions.new(except: [:output, :help])
|
|
54
|
+
)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
__NOTE:__
|
|
58
|
+
- `request_*` extensions are applied before sending a request to the server
|
|
59
|
+
- `option`, `output`, `help` extensions are applied right away after the command is extended with `extend_with`
|
|
60
|
+
- `before_print` extensions are applied right away after the server returns the data
|
|
61
|
+
|
|
62
|
+
#### Example
|
|
63
|
+
```ruby
|
|
64
|
+
class MyCommandExtensions < HammerCLI::CommandExtensions
|
|
65
|
+
|
|
66
|
+
option ['--new-option'], 'TYPE', _('Option description')
|
|
67
|
+
|
|
68
|
+
before_print do |data|
|
|
69
|
+
data['results'].each do |result|
|
|
70
|
+
result['status'] = process_errors(result['errors'])
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
# To use your custom helpers define them as class methods
|
|
74
|
+
def self.process_errors(errors)
|
|
75
|
+
errors.empty? ? 'ok' : 'fail'
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
output do |definition|
|
|
79
|
+
definition.append do
|
|
80
|
+
field nil, 'Statuses', Fields::Label do
|
|
81
|
+
from 'results' do
|
|
82
|
+
field 'status', _('Status')
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
help do |h|
|
|
89
|
+
h.text('Something useful')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
request_headers do |headers|
|
|
93
|
+
headers[:ssl] = true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
request_options do |options|
|
|
97
|
+
options[:with_authentication] = true
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
request_params do |params|
|
|
101
|
+
params[:thin] = false
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
option_sources do |sources, command|
|
|
105
|
+
sources.find_by_name('IdResolution').insert_relative(
|
|
106
|
+
:after,
|
|
107
|
+
'IdParams',
|
|
108
|
+
HammerCLIForeman::OptionSources::PuppetEnvironmentParams.new(command)
|
|
109
|
+
)
|
|
110
|
+
sources
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
MyCommand.extend_with(MyCommandExtensions.new)
|
|
115
|
+
```
|
data/doc/creating_commands.md
CHANGED
|
@@ -435,7 +435,34 @@ HammerCLI::MainCommand.lazy_subcommand(
|
|
|
435
435
|
)
|
|
436
436
|
```
|
|
437
437
|
|
|
438
|
+
### Deprecated commands
|
|
439
|
+
To mark a command as deprecated use the `:warning` option as follows:
|
|
438
440
|
|
|
441
|
+
```ruby
|
|
442
|
+
HammerCLI::MainCommand.lazy_subcommand(
|
|
443
|
+
'say', # command's name
|
|
444
|
+
'Say something', # description
|
|
445
|
+
'HammerCLIHello::SayCommand', # command's class in a string
|
|
446
|
+
'hammer_cli_hello/say', # require path of the file
|
|
447
|
+
warning: _('This command is deprecated and will be removed.')
|
|
448
|
+
)
|
|
449
|
+
```
|
|
450
|
+
Or you can mark a command in its definition:
|
|
451
|
+
|
|
452
|
+
```ruby
|
|
453
|
+
class SayCommand < HammerCLI::AbstractCommand
|
|
454
|
+
|
|
455
|
+
class HelloCommand < HammerCLI::AbstractCommand
|
|
456
|
+
warning 'This command is deprecated and will be removed.'
|
|
457
|
+
command_name 'hello'
|
|
458
|
+
desc 'Say Hello World!'
|
|
459
|
+
# ...
|
|
460
|
+
end
|
|
461
|
+
# ...
|
|
462
|
+
|
|
463
|
+
autoload_subcommands
|
|
464
|
+
end
|
|
465
|
+
```
|
|
439
466
|
### Printing some output
|
|
440
467
|
We've mentioned above that it's not recommended practice to print output
|
|
441
468
|
directly with `puts` in Hammer. The reason is we separate definition
|
data/doc/developer_docs.md
CHANGED
|
@@ -10,6 +10,7 @@ Contents:
|
|
|
10
10
|
- [Creating commands](creating_commands.md#create-your-first-command)
|
|
11
11
|
- [Help modification](help_modification.md#modify-an-existing-help)
|
|
12
12
|
- [Commands modification](commands_modification.md#modify-an-existing-command)
|
|
13
|
+
- [Command extensions](commands_extension.md#extend-an-existing-command)
|
|
13
14
|
- [Option builders](option_builders.md#option-builders)
|
|
14
15
|
- [Creating ApiPie commands](creating_apipie_commands.md#creating-commands-for-restful-api-with-apipie)
|
|
15
16
|
- [Development tips](development_tips.md#development-tips)
|
data/doc/release_notes.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
Release notes
|
|
2
2
|
=============
|
|
3
|
+
### 0.17.0 (2019-04-24)
|
|
4
|
+
* Extended capabilities for list type options ([#26120](http://projects.theforeman.org/issues/26120))
|
|
5
|
+
* Document how to deprecate commands ([PR #301](https://github.com/theforeman/hammer-cli/pull/301)) ([#24964](http://projects.theforeman.org/issues/24964))
|
|
6
|
+
* Extensible commands in hammer ([#21635](http://projects.theforeman.org/issues/21635))
|
|
7
|
+
* Table output allows to hide fields with no error ([#26453](http://projects.theforeman.org/issues/26453))
|
|
8
|
+
|
|
3
9
|
### 0.16.0 (2019-01-16)
|
|
4
10
|
* Fixed instructions in i18n docs ([#25724](http://projects.theforeman.org/issues/25724))
|
|
5
11
|
* Option validators can be mixed with sources ([#22253](http://projects.theforeman.org/issues/22253))
|
data/lib/hammer_cli/abstract.rb
CHANGED
|
@@ -11,6 +11,7 @@ require 'hammer_cli/subcommand'
|
|
|
11
11
|
require 'hammer_cli/options/matcher'
|
|
12
12
|
require 'hammer_cli/help/builder'
|
|
13
13
|
require 'hammer_cli/help/text_builder'
|
|
14
|
+
require 'hammer_cli/command_extensions'
|
|
14
15
|
require 'logging'
|
|
15
16
|
|
|
16
17
|
module HammerCLI
|
|
@@ -24,6 +25,38 @@ module HammerCLI
|
|
|
24
25
|
def help_extension_blocks
|
|
25
26
|
@help_extension_blocks ||= []
|
|
26
27
|
end
|
|
28
|
+
|
|
29
|
+
def command_extensions
|
|
30
|
+
@command_extensions = @command_extensions || inherited_command_extensions || []
|
|
31
|
+
@command_extensions
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def inherited_command_extensions
|
|
35
|
+
extensions = nil
|
|
36
|
+
if superclass.respond_to?(:command_extensions)
|
|
37
|
+
parent_extensions = superclass.command_extensions.select(&:inheritable?)
|
|
38
|
+
extensions = parent_extensions.dup unless parent_extensions.empty?
|
|
39
|
+
end
|
|
40
|
+
extensions
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def extend_options_help(option)
|
|
44
|
+
extend_help do |h|
|
|
45
|
+
begin
|
|
46
|
+
h.find_item(:s_option_details)
|
|
47
|
+
rescue ArgumentError
|
|
48
|
+
option_details = HammerCLI::Help::Section.new(_('Option details'), nil, id: :s_option_details, richtext: true)
|
|
49
|
+
option_details.definition << HammerCLI::Help::Text.new(
|
|
50
|
+
_('Following parameters accept format defined by its schema (bold are required):')
|
|
51
|
+
)
|
|
52
|
+
h.definition.unshift(option_details)
|
|
53
|
+
ensure
|
|
54
|
+
h.find_item(:s_option_details).definition << HammerCLI::Help::List.new([
|
|
55
|
+
[option.switches.last, option.value_formatter.schema.description]
|
|
56
|
+
])
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
27
60
|
end
|
|
28
61
|
|
|
29
62
|
def adapter
|
|
@@ -154,6 +187,21 @@ module HammerCLI
|
|
|
154
187
|
declared_options << option
|
|
155
188
|
block ||= option.default_conversion_block
|
|
156
189
|
define_accessors_for(option, &block)
|
|
190
|
+
extend_options_help(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.extend_with(*extensions)
|
|
195
|
+
extensions.each do |extension|
|
|
196
|
+
unless extension.is_a?(HammerCLI::CommandExtensions)
|
|
197
|
+
raise ArgumentError, _('Command extensions should be inherited from %s.') % HammerCLI::CommandExtensions
|
|
198
|
+
end
|
|
199
|
+
extension.delegatee(self)
|
|
200
|
+
extension.extend_options(self)
|
|
201
|
+
extension.extend_output(self)
|
|
202
|
+
extension.extend_help(self)
|
|
203
|
+
logger('Extensions').info "Applied #{extension.details} on #{self}."
|
|
204
|
+
command_extensions << extension
|
|
157
205
|
end
|
|
158
206
|
end
|
|
159
207
|
|
|
@@ -226,10 +274,15 @@ module HammerCLI
|
|
|
226
274
|
@name || (superclass.respond_to?(:command_name) ? superclass.command_name : nil)
|
|
227
275
|
end
|
|
228
276
|
|
|
277
|
+
def self.warning(message = nil)
|
|
278
|
+
@warning_msg = message if message
|
|
279
|
+
@warning_msg
|
|
280
|
+
end
|
|
281
|
+
|
|
229
282
|
def self.autoload_subcommands
|
|
230
283
|
commands = constants.map { |c| const_get(c) }.select { |c| c <= HammerCLI::AbstractCommand }
|
|
231
284
|
commands.each do |cls|
|
|
232
|
-
subcommand
|
|
285
|
+
subcommand(cls.command_name, cls.desc, cls, warning: cls.warning)
|
|
233
286
|
end
|
|
234
287
|
end
|
|
235
288
|
|
|
@@ -269,7 +322,11 @@ module HammerCLI
|
|
|
269
322
|
sources << HammerCLI::Options::Sources::CommandLine.new(self)
|
|
270
323
|
sources << HammerCLI::Options::Sources::SavedDefaults.new(context[:defaults], logger) if context[:use_defaults]
|
|
271
324
|
|
|
272
|
-
HammerCLI::Options::ProcessorList.new([sources])
|
|
325
|
+
sources = HammerCLI::Options::ProcessorList.new([sources])
|
|
326
|
+
self.class.command_extensions.each do |extension|
|
|
327
|
+
extension.extend_option_sources(sources, self)
|
|
328
|
+
end
|
|
329
|
+
sources
|
|
273
330
|
end
|
|
274
331
|
|
|
275
332
|
def add_validators(sources)
|
|
@@ -47,11 +47,10 @@ module HammerCLI::Apipie
|
|
|
47
47
|
protected
|
|
48
48
|
|
|
49
49
|
def send_request
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
else
|
|
53
|
-
raise HammerCLI::OperationNotSupportedError, "The server does not support such operation."
|
|
50
|
+
unless resource && resource.has_action?(action)
|
|
51
|
+
raise HammerCLI::OperationNotSupportedError, _('The server does not support such operation.')
|
|
54
52
|
end
|
|
53
|
+
extended_data(resource.call(action, *extended_request))
|
|
55
54
|
end
|
|
56
55
|
|
|
57
56
|
def request_headers
|
|
@@ -90,5 +89,25 @@ module HammerCLI::Apipie
|
|
|
90
89
|
end
|
|
91
90
|
end
|
|
92
91
|
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def extended_request
|
|
95
|
+
params = request_params
|
|
96
|
+
headers = request_headers
|
|
97
|
+
options = request_options
|
|
98
|
+
self.class.command_extensions.each do |extension|
|
|
99
|
+
extension.extend_request_headers(headers)
|
|
100
|
+
extension.extend_request_options(options)
|
|
101
|
+
extension.extend_request_params(params)
|
|
102
|
+
end
|
|
103
|
+
[params, headers, options]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def extended_data(data)
|
|
107
|
+
self.class.command_extensions.each do |extension|
|
|
108
|
+
extension.extend_before_print(data)
|
|
109
|
+
end
|
|
110
|
+
data
|
|
111
|
+
end
|
|
93
112
|
end
|
|
94
113
|
end
|
|
@@ -65,7 +65,11 @@ module HammerCLI::Apipie
|
|
|
65
65
|
opts = {}
|
|
66
66
|
opts[:required] = true if (param.required? and require_options?)
|
|
67
67
|
if param.expected_type.to_s == 'array'
|
|
68
|
-
|
|
68
|
+
if param.params.empty?
|
|
69
|
+
opts[:format] = HammerCLI::Options::Normalizers::List.new
|
|
70
|
+
else
|
|
71
|
+
opts[:format] = HammerCLI::Options::Normalizers::ListNested.new(param.params)
|
|
72
|
+
end
|
|
69
73
|
elsif param.expected_type.to_s == 'boolean' || param.validator.to_s == 'boolean'
|
|
70
74
|
opts[:format] = HammerCLI::Options::Normalizers::Bool.new
|
|
71
75
|
elsif param.expected_type.to_s == 'string' && param.validator =~ /Must be one of: (.*)\./
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
module HammerCLI
|
|
2
|
+
class CommandExtensions
|
|
3
|
+
class << self
|
|
4
|
+
attr_accessor :delegatee
|
|
5
|
+
|
|
6
|
+
def logger
|
|
7
|
+
Logging.logger[to_s]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def inheritable?
|
|
11
|
+
@inheritable
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
ALLOWED_EXTENSIONS = %i[
|
|
16
|
+
option command_options before_print data output help request
|
|
17
|
+
request_headers headers request_options options request_params params
|
|
18
|
+
option_sources
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
def initialize(options = {})
|
|
22
|
+
@only = options[:only] || ALLOWED_EXTENSIONS
|
|
23
|
+
@only = [@only] unless @only.is_a?(Array)
|
|
24
|
+
@except = options[:except] || []
|
|
25
|
+
@except = [@except] unless @except.is_a?(Array)
|
|
26
|
+
@inheritable = options[:inheritable]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def inheritable?
|
|
30
|
+
return @inheritable unless @inheritable.nil?
|
|
31
|
+
|
|
32
|
+
self.class.inheritable? || false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.method_missing(message, *args, &block)
|
|
36
|
+
if @delegatee
|
|
37
|
+
@delegatee.send(message, *args, &block)
|
|
38
|
+
else
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# DSL
|
|
44
|
+
|
|
45
|
+
def self.inheritable(boolean)
|
|
46
|
+
@inheritable = boolean
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.option(switches, type, description, opts = {}, &block)
|
|
50
|
+
@options ||= []
|
|
51
|
+
@options << { switches: switches,
|
|
52
|
+
type: type,
|
|
53
|
+
description: description,
|
|
54
|
+
opts: opts, block: block }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.before_print(&block)
|
|
58
|
+
@before_print_block = block
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.output(&block)
|
|
62
|
+
@output_extension_block = block
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.help(&block)
|
|
66
|
+
@help_extension_block = block
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.request_headers(&block)
|
|
70
|
+
@request_headers_block = block
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.request_options(&block)
|
|
74
|
+
@request_options_block = block
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.request_params(&block)
|
|
78
|
+
@request_params_block = block
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def self.option_sources(&block)
|
|
82
|
+
@option_sources_block = block
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Object
|
|
86
|
+
|
|
87
|
+
def extend_options(command_class)
|
|
88
|
+
allowed = @only & %i[command_options option]
|
|
89
|
+
return if allowed.empty? || (allowed & @except).any?
|
|
90
|
+
|
|
91
|
+
self.class.extend_options(command_class)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def extend_before_print(data)
|
|
95
|
+
allowed = @only & %i[before_print data]
|
|
96
|
+
return if allowed.empty? || (allowed & @except).any?
|
|
97
|
+
|
|
98
|
+
self.class.extend_before_print(data)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def extend_output(command_class)
|
|
102
|
+
allowed = @only & %i[output]
|
|
103
|
+
return if allowed.empty? || (allowed & @except).any?
|
|
104
|
+
|
|
105
|
+
self.class.extend_output(command_class)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def extend_help(command_class)
|
|
109
|
+
allowed = @only & %i[help]
|
|
110
|
+
return if allowed.empty? || (allowed & @except).any?
|
|
111
|
+
|
|
112
|
+
self.class.extend_help(command_class)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def extend_request_headers(headers)
|
|
116
|
+
allowed = @only & %i[request_headers headers request]
|
|
117
|
+
return if allowed.empty? || (allowed & @except).any?
|
|
118
|
+
|
|
119
|
+
self.class.extend_request_headers(headers)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def extend_request_options(options)
|
|
123
|
+
allowed = @only & %i[request_options options request]
|
|
124
|
+
return if allowed.empty? || (allowed & @except).any?
|
|
125
|
+
|
|
126
|
+
self.class.extend_request_options(options)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def extend_request_params(params)
|
|
130
|
+
allowed = @only & %i[request_params params request]
|
|
131
|
+
return if allowed.empty? || (allowed & @except).any?
|
|
132
|
+
|
|
133
|
+
self.class.extend_request_params(params)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def extend_option_sources(sources, command = nil)
|
|
137
|
+
allowed = @only & %i[option_sources]
|
|
138
|
+
return if allowed.empty? || (allowed & @except).any?
|
|
139
|
+
|
|
140
|
+
self.class.extend_option_sources(sources, command)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def delegatee(command_class)
|
|
144
|
+
self.class.delegatee = command_class
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def details
|
|
148
|
+
except = @except.empty? ? '*nothing*' : @except
|
|
149
|
+
details = if @only == ALLOWED_EXTENSIONS
|
|
150
|
+
"*all* except #{except}"
|
|
151
|
+
else
|
|
152
|
+
"#{@only} only"
|
|
153
|
+
end
|
|
154
|
+
"#{self.class} for #{details}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Class
|
|
158
|
+
|
|
159
|
+
def self.extend_options(command_class)
|
|
160
|
+
return if @options.nil?
|
|
161
|
+
|
|
162
|
+
@options.each do |option|
|
|
163
|
+
command_class.send(:option,
|
|
164
|
+
option[:switches],
|
|
165
|
+
option[:type],
|
|
166
|
+
option[:description],
|
|
167
|
+
option[:opts],
|
|
168
|
+
&option[:block])
|
|
169
|
+
logger.debug("Added option for #{command_class}: #{option}")
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def self.extend_before_print(data)
|
|
174
|
+
return if @before_print_block.nil?
|
|
175
|
+
|
|
176
|
+
@before_print_block.call(data)
|
|
177
|
+
logger.debug("Called block for #{@delegatee} data:\n\t#{@before_print_block}")
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def self.extend_output(command_class)
|
|
181
|
+
return if @output_extension_block.nil?
|
|
182
|
+
|
|
183
|
+
@output_extension_block.call(command_class.output_definition)
|
|
184
|
+
logger.debug("Called block for #{@delegatee} output definition:\n\t#{@output_extension_block}")
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def self.extend_help(command_class)
|
|
188
|
+
return if @help_extension_block.nil?
|
|
189
|
+
|
|
190
|
+
command_class.help_extension_blocks << @help_extension_block
|
|
191
|
+
logger.debug("Saved block for #{@delegatee} help definition:\n\t#{@help_extension_block}")
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.extend_request_headers(headers)
|
|
195
|
+
return if @request_headers_block.nil?
|
|
196
|
+
|
|
197
|
+
@request_headers_block.call(headers)
|
|
198
|
+
logger.debug("Called block for #{@delegatee} request headers:\n\t#{@request_headers_block}")
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def self.extend_request_options(options)
|
|
202
|
+
return if @request_options_block.nil?
|
|
203
|
+
|
|
204
|
+
@request_options_block.call(options)
|
|
205
|
+
logger.debug("Called block for #{@delegatee} request options:\n\t#{@request_options_block}")
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def self.extend_request_params(params)
|
|
209
|
+
return if @request_params_block.nil?
|
|
210
|
+
|
|
211
|
+
@request_params_block.call(params)
|
|
212
|
+
logger.debug("Called block for #{@delegatee} request params:\n\t#{@request_params_block}")
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def self.extend_option_sources(sources, command = nil)
|
|
216
|
+
return if @option_sources_block.nil?
|
|
217
|
+
|
|
218
|
+
@option_sources_block.call(sources, command)
|
|
219
|
+
logger.debug("Called block for #{@delegatee} option sources:\n\t#{@option_sources_block}")
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -27,11 +27,12 @@ module HammerCLI
|
|
|
27
27
|
|
|
28
28
|
class KeyValueList < AbstractNormalizer
|
|
29
29
|
|
|
30
|
-
PAIR_RE = '([^,=]+)=([^,\[]
|
|
30
|
+
PAIR_RE = '([^,=]+)=([^,\{\[]+|[\{\[][^\{\}\[\]]*[\}\]])'
|
|
31
31
|
FULL_RE = "^((%s)[,]?)+$" % PAIR_RE
|
|
32
32
|
|
|
33
33
|
def description
|
|
34
|
-
_("Comma-separated list of key=value")
|
|
34
|
+
_("Comma-separated list of key=value.") + "\n" +
|
|
35
|
+
_("JSON is acceptable and preferred way for complex parameters")
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def format(val)
|
|
@@ -60,7 +61,11 @@ module HammerCLI
|
|
|
60
61
|
result = {}
|
|
61
62
|
val.scan(Regexp.new(PAIR_RE)) do |key, value|
|
|
62
63
|
value = value.strip
|
|
63
|
-
|
|
64
|
+
if value.start_with?('[')
|
|
65
|
+
value = value.scan(/[^,\[\]]+/)
|
|
66
|
+
elsif value.start_with?('{')
|
|
67
|
+
value = parse_key_value(value[1...-1])
|
|
68
|
+
end
|
|
64
69
|
|
|
65
70
|
result[key.strip] = strip_value(value)
|
|
66
71
|
end
|
|
@@ -72,6 +77,10 @@ module HammerCLI
|
|
|
72
77
|
value.map do |item|
|
|
73
78
|
strip_chars(item.strip, '"\'')
|
|
74
79
|
end
|
|
80
|
+
elsif value.is_a? Hash
|
|
81
|
+
value.map do |key, val|
|
|
82
|
+
[strip_chars(key.strip, '"\''), strip_chars(val.strip, '"\'')]
|
|
83
|
+
end.to_h
|
|
75
84
|
else
|
|
76
85
|
strip_chars(value.strip, '"\'')
|
|
77
86
|
end
|
|
@@ -86,14 +95,55 @@ module HammerCLI
|
|
|
86
95
|
|
|
87
96
|
class List < AbstractNormalizer
|
|
88
97
|
def description
|
|
89
|
-
_("Comma separated list of values. Values containing comma should be quoted or escaped with backslash")
|
|
98
|
+
_("Comma separated list of values. Values containing comma should be quoted or escaped with backslash.") + "\n" +
|
|
99
|
+
_("JSON is acceptable and preferred way for complex parameters")
|
|
90
100
|
end
|
|
91
101
|
|
|
92
102
|
def format(val)
|
|
93
|
-
|
|
103
|
+
return [] unless val.is_a?(String) && !val.empty?
|
|
104
|
+
begin
|
|
105
|
+
JSON.parse(val)
|
|
106
|
+
rescue JSON::ParserError
|
|
107
|
+
HammerCLI::CSVParser.new.parse(val)
|
|
108
|
+
end
|
|
94
109
|
end
|
|
95
110
|
end
|
|
96
111
|
|
|
112
|
+
class ListNested < AbstractNormalizer
|
|
113
|
+
class Schema < Array
|
|
114
|
+
def description
|
|
115
|
+
'"' + reduce([]) do |schema, nested_param|
|
|
116
|
+
name = nested_param.name
|
|
117
|
+
name = HighLine.color(name, :bold) if nested_param.required?
|
|
118
|
+
schema << "#{name}=#{nested_param.expected_type}"
|
|
119
|
+
end.join('\,').concat(', ... "')
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
attr_reader :schema
|
|
124
|
+
|
|
125
|
+
def initialize(schema)
|
|
126
|
+
@schema = Schema.new(schema)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def description
|
|
130
|
+
_("Comma separated list of values defined by a schema. See Option details section below.") + "\n" +
|
|
131
|
+
_("JSON is acceptable and preferred way for complex parameters")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def format(val)
|
|
135
|
+
return [] unless val.is_a?(String) && !val.empty?
|
|
136
|
+
begin
|
|
137
|
+
JSON.parse(val)
|
|
138
|
+
rescue JSON::ParserError
|
|
139
|
+
HammerCLI::CSVParser.new.parse(val).inject([]) do |results, item|
|
|
140
|
+
next if item.empty?
|
|
141
|
+
|
|
142
|
+
results << KeyValueList.new.format(item)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
97
147
|
|
|
98
148
|
class Number < AbstractNormalizer
|
|
99
149
|
|
|
@@ -27,9 +27,9 @@ module HammerCLI
|
|
|
27
27
|
attr_accessor :deprecated_switches
|
|
28
28
|
|
|
29
29
|
def initialize(switches, type, description, options = {})
|
|
30
|
-
self.value_formatter = options
|
|
31
|
-
self.context_target = options
|
|
32
|
-
self.deprecated_switches = options
|
|
30
|
+
self.value_formatter = options[:format] || HammerCLI::Options::Normalizers::Default.new
|
|
31
|
+
self.context_target = options[:context_target]
|
|
32
|
+
self.deprecated_switches = options[:deprecated]
|
|
33
33
|
super
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -53,8 +53,8 @@ module HammerCLI::Output::Adapter
|
|
|
53
53
|
# and there is no --no-headers option
|
|
54
54
|
output_stream.puts line unless formatted_collection.empty? || @context[:no_headers]
|
|
55
55
|
|
|
56
|
-
if
|
|
57
|
-
|
|
56
|
+
if collection.respond_to?(:meta) && collection.meta.pagination_set? &&
|
|
57
|
+
@context[:verbosity] >= collection.meta.pagination_verbosity &&
|
|
58
58
|
collection.count < collection.meta.subtotal
|
|
59
59
|
pages = (collection.meta.subtotal.to_f / collection.meta.per_page).ceil
|
|
60
60
|
puts _("Page %{page} of %{total} (use --page and --per-page for navigation).") % {:page => collection.meta.page, :total => pages}
|
|
@@ -18,6 +18,7 @@ module HammerCLI
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def subcommand_class
|
|
21
|
+
@warning ||= @subcommand_class.warning
|
|
21
22
|
warn(@warning) if @warning
|
|
22
23
|
@subcommand_class
|
|
23
24
|
end
|
|
@@ -38,12 +39,13 @@ module HammerCLI
|
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def subcommand_class
|
|
41
|
-
|
|
42
|
-
if !@loaded
|
|
42
|
+
unless @loaded
|
|
43
43
|
require @path
|
|
44
44
|
@loaded = true
|
|
45
45
|
@constantized_class = @subcommand_class.constantize
|
|
46
46
|
end
|
|
47
|
+
@warning ||= @constantized_class.warning
|
|
48
|
+
warn(@warning) if @warning
|
|
47
49
|
@constantized_class
|
|
48
50
|
end
|
|
49
51
|
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/test/unit/abstract_test.rb
CHANGED
|
@@ -499,5 +499,33 @@ describe HammerCLI::AbstractCommand do
|
|
|
499
499
|
assert_equal ['Validator1'], result.map(&:name)
|
|
500
500
|
end
|
|
501
501
|
end
|
|
502
|
-
end
|
|
503
502
|
|
|
503
|
+
describe '#extend_with' do
|
|
504
|
+
class Extensions < HammerCLI::CommandExtensions
|
|
505
|
+
option '--flag', 'FLAG', 'flag'
|
|
506
|
+
output { |definition| definition.append(Fields::Field.new) }
|
|
507
|
+
help { |h| h.text('text') }
|
|
508
|
+
end
|
|
509
|
+
class Cmd < HammerCLI::AbstractCommand
|
|
510
|
+
def execute
|
|
511
|
+
HammerCLI::EX_OK
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
let(:extension) { Extensions.new }
|
|
515
|
+
let(:output_extension) { Extensions.new(only: :output) }
|
|
516
|
+
let(:cmd) { Class.new(Cmd) }
|
|
517
|
+
|
|
518
|
+
it 'should extend command with option, output, help right away' do
|
|
519
|
+
cmd.extend_with(extension)
|
|
520
|
+
opt = cmd.find_option('--flag')
|
|
521
|
+
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal true
|
|
522
|
+
cmd.output_definition.empty?.must_equal false
|
|
523
|
+
cmd.new({}).help.must_match(/.*text.*/)
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
it 'should store more than one extension' do
|
|
527
|
+
cmd.extend_with(extension, output_extension)
|
|
528
|
+
cmd.command_extensions.size.must_equal 2
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
|
2
|
+
|
|
3
|
+
describe HammerCLI::CommandExtensions do
|
|
4
|
+
class CustomCmd < HammerCLI::Apipie::Command
|
|
5
|
+
def execute
|
|
6
|
+
HammerCLI::EX_OK
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def request_headers
|
|
10
|
+
{}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def request_options
|
|
14
|
+
{}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def request_params
|
|
18
|
+
{}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
public :extended_request, :extended_data
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class CmdExtensions < HammerCLI::CommandExtensions
|
|
25
|
+
option '--ext', 'EXT', 'ext'
|
|
26
|
+
before_print do |data|
|
|
27
|
+
data['key'] = 'value'
|
|
28
|
+
end
|
|
29
|
+
output do |definition|
|
|
30
|
+
definition.append(Fields::Field.new)
|
|
31
|
+
definition.append(Fields::Field.new)
|
|
32
|
+
end
|
|
33
|
+
help do |h|
|
|
34
|
+
h.section('Section')
|
|
35
|
+
h.text('text')
|
|
36
|
+
end
|
|
37
|
+
request_headers do |headers|
|
|
38
|
+
headers[:ssl] = true
|
|
39
|
+
end
|
|
40
|
+
request_options do |options|
|
|
41
|
+
options[:with_authentication] = true
|
|
42
|
+
end
|
|
43
|
+
request_params do |params|
|
|
44
|
+
params[:thin] = true
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
let(:cmd) { Class.new(CustomCmd) }
|
|
49
|
+
|
|
50
|
+
context 'only' do
|
|
51
|
+
it 'should extend options only' do
|
|
52
|
+
cmd.extend_with(CmdExtensions.new(only: :option))
|
|
53
|
+
opt = cmd.find_option('--ext')
|
|
54
|
+
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal true
|
|
55
|
+
cmd.output_definition.empty?.must_equal true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'should extend output only' do
|
|
59
|
+
cmd.extend_with(CmdExtensions.new(only: :output))
|
|
60
|
+
cmd.output_definition.empty?.must_equal false
|
|
61
|
+
opt = cmd.find_option('--ext')
|
|
62
|
+
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'should extend help only' do
|
|
66
|
+
cmd.extend_with(CmdExtensions.new(only: :help))
|
|
67
|
+
cmd.new({}).help.must_match(/.*Section.*/)
|
|
68
|
+
cmd.new({}).help.must_match(/.*text.*/)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'should extend params only' do
|
|
72
|
+
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({})
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'should extend headers only' do
|
|
79
|
+
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({})
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'should extend options only' do
|
|
86
|
+
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)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'should extend params and options and headers' do
|
|
93
|
+
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)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'should extend data only' do
|
|
100
|
+
cmd.extend_with(CmdExtensions.new(only: :data))
|
|
101
|
+
cmd.new({}).help.wont_match(/.*Section.*/)
|
|
102
|
+
cmd.new({}).help.wont_match(/.*text.*/)
|
|
103
|
+
cmd.output_definition.empty?.must_equal true
|
|
104
|
+
opt = cmd.find_option('--ext')
|
|
105
|
+
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')
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
context 'except' do
|
|
114
|
+
it 'should extend all except options' do
|
|
115
|
+
cmd.extend_with(CmdExtensions.new(except: :option))
|
|
116
|
+
opt = cmd.find_option('--ext')
|
|
117
|
+
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal false
|
|
118
|
+
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)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it 'should extend all except output' do
|
|
125
|
+
cmd.extend_with(CmdExtensions.new(except: :output))
|
|
126
|
+
cmd.output_definition.empty?.must_equal true
|
|
127
|
+
opt = cmd.find_option('--ext')
|
|
128
|
+
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)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'should extend all except help' do
|
|
135
|
+
cmd.extend_with(CmdExtensions.new(except: :help))
|
|
136
|
+
cmd.new({}).help.wont_match(/.*Section.*/)
|
|
137
|
+
cmd.new({}).help.wont_match(/.*text.*/)
|
|
138
|
+
cmd.output_definition.empty?.must_equal false
|
|
139
|
+
opt = cmd.find_option('--ext')
|
|
140
|
+
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)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'should extend all except params' do
|
|
147
|
+
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)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'should extend all except headers' do
|
|
154
|
+
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)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'should extend all except options' do
|
|
161
|
+
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({})
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'should extend all except params and options and headers' do
|
|
168
|
+
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({})
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it 'should extend all except data' do
|
|
175
|
+
cmd.extend_with(CmdExtensions.new(except: :data))
|
|
176
|
+
cmd.new({}).help.must_match(/.*Section.*/)
|
|
177
|
+
cmd.new({}).help.must_match(/.*text.*/)
|
|
178
|
+
cmd.output_definition.empty?.must_equal false
|
|
179
|
+
opt = cmd.find_option('--ext')
|
|
180
|
+
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({})
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
end
|
|
@@ -14,21 +14,21 @@ describe HammerCLI::Options::Normalizers do
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
describe 'default' do
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
let(:formatter) { HammerCLI::Options::Normalizers::Default.new }
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
it "should not change any value" do
|
|
21
21
|
formatter.format('value').must_equal 'value'
|
|
22
22
|
end
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
it "should not change nil value" do
|
|
25
25
|
formatter.format(nil).must_be_nil
|
|
26
26
|
end
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
it "has empty description" do
|
|
29
29
|
formatter.description.must_equal ''
|
|
30
30
|
end
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
it "has empty completion" do
|
|
33
33
|
formatter.complete('test').must_equal []
|
|
34
34
|
end
|
|
@@ -69,8 +69,85 @@ describe HammerCLI::Options::Normalizers do
|
|
|
69
69
|
it "should catch quoting errors" do
|
|
70
70
|
proc { formatter.format('1,"3,4""s') }.must_raise ArgumentError
|
|
71
71
|
end
|
|
72
|
+
|
|
73
|
+
it "should accept and parse JSON" do
|
|
74
|
+
formatter.format("{\"name\":\"bla\", \"value\":1}").must_equal(
|
|
75
|
+
JSON.parse("{\"name\":\"bla\", \"value\":1}")
|
|
76
|
+
)
|
|
77
|
+
end
|
|
72
78
|
end
|
|
73
79
|
|
|
80
|
+
describe 'list_nested' do
|
|
81
|
+
let(:params_raw) do
|
|
82
|
+
[
|
|
83
|
+
{name: 'name', expected_type: :string, validator: 'string', description: ''},
|
|
84
|
+
{name: 'value', expected_type: :string, validator: 'string', description: ''}
|
|
85
|
+
]
|
|
86
|
+
end
|
|
87
|
+
let(:params) do
|
|
88
|
+
[
|
|
89
|
+
ApipieBindings::Param.new(params_raw.first),
|
|
90
|
+
ApipieBindings::Param.new(params_raw.last)
|
|
91
|
+
]
|
|
92
|
+
end
|
|
93
|
+
let(:param) do
|
|
94
|
+
ApipieBindings::Param.new({
|
|
95
|
+
name: 'array', expected_type: :array, validator: 'nested', description: '',
|
|
96
|
+
params: params_raw
|
|
97
|
+
})
|
|
98
|
+
end
|
|
99
|
+
let(:formatter) { HammerCLI::Options::Normalizers::ListNested.new(param.params) }
|
|
100
|
+
|
|
101
|
+
it "should accept and parse JSON" do
|
|
102
|
+
formatter.format("{\"name\":\"bla\", \"value\":1}").must_equal(
|
|
103
|
+
JSON.parse("{\"name\":\"bla\", \"value\":1}")
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should parse simple input" do
|
|
108
|
+
formatter.format("name=test\\,value=1,name=other\\,value=2").must_equal(
|
|
109
|
+
[{'name' => 'test', 'value' => '1'}, {'name' => 'other', 'value' => '2'}]
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "should parse unexpected input" do
|
|
114
|
+
formatter.format("name=test\\,value=1,name=other\\,value=2,unexp=doe").must_equal(
|
|
115
|
+
[
|
|
116
|
+
{'name' => 'test', 'value' => '1'}, {'name' => 'other', 'value' => '2'},
|
|
117
|
+
{'unexp' => 'doe'}
|
|
118
|
+
]
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "should accept arrays" do
|
|
123
|
+
formatter.format("name=test\\,value=1,name=other\\,value=[1\\,2\\,3]").must_equal(
|
|
124
|
+
[{'name' => 'test', 'value' => '1'}, {'name' => 'other', 'value' => ['1', '2', '3']}]
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "should accept hashes" do
|
|
129
|
+
formatter.format(
|
|
130
|
+
"name=test\\,value={key=key1\\,value=1},name=other\\,value={key=key2\\,value=2}"
|
|
131
|
+
).must_equal(
|
|
132
|
+
[
|
|
133
|
+
{'name' => 'test', 'value' => {'key' => 'key1', 'value' => '1'}},
|
|
134
|
+
{'name' => 'other', 'value' => {'key' => 'key2', 'value' => '2'}},
|
|
135
|
+
]
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "should accept combined input" do
|
|
140
|
+
formatter.format(
|
|
141
|
+
"name=foo\\,value=1\\,adds=[1\\,2\\,3]\\,cpu={name=ddd\\,type=abc}," \
|
|
142
|
+
"name=bar\\,value=2\\,adds=[2\\,2\\,2]\\,cpu={name=ccc\\,type=cba}"
|
|
143
|
+
).must_equal(
|
|
144
|
+
[
|
|
145
|
+
{'name' => 'foo', 'value' => '1', 'adds' => ['1','2','3'], 'cpu' => {'name' => 'ddd', 'type' => 'abc'}},
|
|
146
|
+
{'name' => 'bar', 'value' => '2', 'adds' => ['2','2','2'], 'cpu' => {'name' => 'ccc', 'type' => 'cba'}}
|
|
147
|
+
]
|
|
148
|
+
)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
74
151
|
|
|
75
152
|
describe 'key_value_list' do
|
|
76
153
|
|
|
@@ -133,6 +210,16 @@ describe HammerCLI::Options::Normalizers do
|
|
|
133
210
|
formatter.format("a=1,b=[],c=3").must_equal({'a' => '1', 'b' => [], 'c' => '3'})
|
|
134
211
|
end
|
|
135
212
|
|
|
213
|
+
it "should parse hash with one item" do
|
|
214
|
+
formatter.format("a=1,b={key=abc,value=abc},c=3").must_equal(
|
|
215
|
+
{'a' => '1', 'b' => {'key' => 'abc', 'value' => 'abc'}, 'c' => '3'}
|
|
216
|
+
)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it "should parse empty hash" do
|
|
220
|
+
formatter.format("a=1,b={},c=3").must_equal({'a' => '1', 'b' => {}, 'c' => '3'})
|
|
221
|
+
end
|
|
222
|
+
|
|
136
223
|
it "should parse a comma separated string 2" do
|
|
137
224
|
proc { formatter.format("a=1,b,c=3") }.must_raise ArgumentError
|
|
138
225
|
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: 0.
|
|
4
|
+
version: 0.17.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: 2019-
|
|
12
|
+
date: 2019-04-24 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: clamp
|
|
@@ -156,6 +156,7 @@ extra_rdoc_files:
|
|
|
156
156
|
- doc/release_notes.md
|
|
157
157
|
- doc/commands_modification.md
|
|
158
158
|
- doc/creating_commands.md
|
|
159
|
+
- doc/commands_extension.md
|
|
159
160
|
- doc/development_tips.md
|
|
160
161
|
- doc/developer_docs.md
|
|
161
162
|
- doc/writing_a_plugin.md
|
|
@@ -178,6 +179,7 @@ files:
|
|
|
178
179
|
- bin/hammer
|
|
179
180
|
- config/cli.modules.d/module_config_template.yml
|
|
180
181
|
- config/cli_config.template.yml
|
|
182
|
+
- doc/commands_extension.md
|
|
181
183
|
- doc/commands_modification.md
|
|
182
184
|
- doc/creating_apipie_commands.md
|
|
183
185
|
- doc/creating_commands.md
|
|
@@ -209,6 +211,7 @@ files:
|
|
|
209
211
|
- lib/hammer_cli/ca_cert_fetcher.rb
|
|
210
212
|
- lib/hammer_cli/ca_cert_manager.rb
|
|
211
213
|
- lib/hammer_cli/clamp.rb
|
|
214
|
+
- lib/hammer_cli/command_extensions.rb
|
|
212
215
|
- lib/hammer_cli/completer.rb
|
|
213
216
|
- lib/hammer_cli/connection.rb
|
|
214
217
|
- lib/hammer_cli/context.rb
|
|
@@ -439,6 +442,7 @@ files:
|
|
|
439
442
|
- test/unit/apipie/option_definition_test.rb
|
|
440
443
|
- test/unit/apipie/test_helper.rb
|
|
441
444
|
- test/unit/ca_cert_manager_test.rb
|
|
445
|
+
- test/unit/command_extensions_test.rb
|
|
442
446
|
- test/unit/completer_test.rb
|
|
443
447
|
- test/unit/connection_test.rb
|
|
444
448
|
- test/unit/csv_parser_test.rb
|
|
@@ -510,7 +514,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
510
514
|
version: '0'
|
|
511
515
|
requirements: []
|
|
512
516
|
rubyforge_project:
|
|
513
|
-
rubygems_version: 2.
|
|
517
|
+
rubygems_version: 2.7.9
|
|
514
518
|
signing_key:
|
|
515
519
|
specification_version: 4
|
|
516
520
|
summary: Universal command-line interface
|
|
@@ -708,6 +712,7 @@ test_files:
|
|
|
708
712
|
- test/unit/fixtures/defaults/defaults_dashed.yml
|
|
709
713
|
- test/unit/defaults_test.rb
|
|
710
714
|
- test/unit/messages_test.rb
|
|
715
|
+
- test/unit/command_extensions_test.rb
|
|
711
716
|
- test/unit/ca_cert_manager_test.rb
|
|
712
717
|
- test/unit/test_helper.rb
|
|
713
718
|
- test/functional/help_test.rb
|