hammer_cli 0.16.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|