hammer_cli 0.15.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/doc/commands_modification.md +82 -0
  3. data/doc/creating_commands.md +78 -38
  4. data/doc/developer_docs.md +2 -0
  5. data/doc/help_modification.md +119 -0
  6. data/doc/i18n.md +2 -2
  7. data/doc/installation_rpm.md +3 -3
  8. data/doc/release_notes.md +9 -1
  9. data/lib/hammer_cli.rb +0 -1
  10. data/lib/hammer_cli/abstract.rb +44 -13
  11. data/lib/hammer_cli/exception_handler.rb +1 -1
  12. data/lib/hammer_cli/help/definition.rb +55 -0
  13. data/lib/hammer_cli/help/definition/abstract_item.rb +38 -0
  14. data/lib/hammer_cli/help/definition/list.rb +53 -0
  15. data/lib/hammer_cli/help/definition/section.rb +36 -0
  16. data/lib/hammer_cli/help/definition/text.rb +21 -0
  17. data/lib/hammer_cli/help/text_builder.rb +26 -49
  18. data/lib/hammer_cli/i18n.rb +0 -1
  19. data/lib/hammer_cli/options/option_collector.rb +12 -9
  20. data/lib/hammer_cli/options/option_processor.rb +17 -0
  21. data/lib/hammer_cli/options/processor_list.rb +37 -0
  22. data/lib/hammer_cli/options/sources/base.rb +17 -0
  23. data/lib/hammer_cli/options/sources/command_line.rb +3 -1
  24. data/lib/hammer_cli/options/sources/saved_defaults.rb +3 -1
  25. data/lib/hammer_cli/options/validators/base.rb +20 -0
  26. data/lib/hammer_cli/options/validators/dsl.rb +160 -0
  27. data/lib/hammer_cli/options/validators/dsl_block_validator.rb +19 -0
  28. data/lib/hammer_cli/output/adapter/csv.rb +11 -11
  29. data/lib/hammer_cli/output/adapter/tree_structure.rb +5 -3
  30. data/lib/hammer_cli/output/definition.rb +42 -4
  31. data/lib/hammer_cli/output/dsl.rb +4 -2
  32. data/lib/hammer_cli/output/fields.rb +9 -2
  33. data/lib/hammer_cli/testing/messages.rb +6 -6
  34. data/lib/hammer_cli/utils.rb +15 -0
  35. data/lib/hammer_cli/version.rb +1 -1
  36. data/locale/ca/LC_MESSAGES/hammer-cli.mo +0 -0
  37. data/locale/de/LC_MESSAGES/hammer-cli.mo +0 -0
  38. data/locale/en/LC_MESSAGES/hammer-cli.mo +0 -0
  39. data/locale/en_GB/LC_MESSAGES/hammer-cli.mo +0 -0
  40. data/locale/es/LC_MESSAGES/hammer-cli.mo +0 -0
  41. data/locale/fr/LC_MESSAGES/hammer-cli.mo +0 -0
  42. data/locale/it/LC_MESSAGES/hammer-cli.mo +0 -0
  43. data/locale/ja/LC_MESSAGES/hammer-cli.mo +0 -0
  44. data/locale/ko/LC_MESSAGES/hammer-cli.mo +0 -0
  45. data/locale/pt_BR/LC_MESSAGES/hammer-cli.mo +0 -0
  46. data/locale/ru/LC_MESSAGES/hammer-cli.mo +0 -0
  47. data/locale/zh_CN/LC_MESSAGES/hammer-cli.mo +0 -0
  48. data/locale/zh_TW/LC_MESSAGES/hammer-cli.mo +0 -0
  49. data/test/unit/abstract_test.rb +70 -5
  50. data/test/unit/exception_handler_test.rb +1 -1
  51. data/test/unit/help/definition/abstract_item_test.rb +33 -0
  52. data/test/unit/help/definition/list_test.rb +17 -0
  53. data/test/unit/help/definition/section_test.rb +25 -0
  54. data/test/unit/help/definition/text_test.rb +11 -0
  55. data/test/unit/help/definition_test.rb +236 -0
  56. data/test/unit/help/text_builder_test.rb +170 -1
  57. data/test/unit/options/option_collector_test.rb +18 -9
  58. data/test/unit/options/processor_list_test.rb +70 -0
  59. data/test/unit/{validator_test.rb → options/validators/dsl_test.rb} +57 -93
  60. data/test/unit/output/adapter/abstract_test.rb +3 -0
  61. data/test/unit/output/adapter/base_test.rb +5 -0
  62. data/test/unit/output/adapter/csv_test.rb +3 -0
  63. data/test/unit/output/adapter/json_test.rb +12 -0
  64. data/test/unit/output/adapter/table_test.rb +5 -0
  65. data/test/unit/output/adapter/yaml_test.rb +11 -0
  66. data/test/unit/output/definition_test.rb +221 -1
  67. data/test/unit/utils_test.rb +23 -0
  68. metadata +31 -5
  69. data/lib/hammer_cli/validator.rb +0 -172
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3fdeba3063ee3ba211aabb8a6d57d17a70fb7f22
4
- data.tar.gz: b085e64553cde11ebed24c0525e461bdcf9ee3e0
3
+ metadata.gz: 836795c65ef362f852b3eed29126508d0bf23927
4
+ data.tar.gz: 8300578ae0b731724817506ee20bda1ea458b71b
5
5
  SHA512:
6
- metadata.gz: 9a706bd9e66688c5642575c9eea4d3d5963847eb93dcac2293b14a3d109d3d52582effa20dd9f69c51448a5497e1555ed769759c58ce9a84b1d70ea96711a2b1
7
- data.tar.gz: 967e76b9ee2e0ed6f131b5dced17032bfe288f9e74ac7e831dc92865b0478ee2b5056a6edcae41064e97d0c36092932529bfa07d0fe6002514dde1e8a28ca0ee
6
+ metadata.gz: 848a8e9636cb5c8e8dea817de554d943f73db10399d897d770d71a7df5cfc66d5c843b351c64587781dd93ae5be81ecd17afe0e2ffa4e0ce7192d55e4d5fd732
7
+ data.tar.gz: aa6bbeb277ae28f0d1c1626f78773dcfbd64a4b81de89609a5fa64eff4a7a607e582f464fb2b58bc7df30b3770c877b2188b62ed14030992630a21932e0b9cf8
@@ -0,0 +1,82 @@
1
+ Modify an existing command
2
+ -------------------------
3
+
4
+ ### Modification of output fields
5
+
6
+ Each command (as well as each field) might have its own
7
+ output definition. You can modify the output definition of existing
8
+ command or a specific field of that command by following output
9
+ definition interface:
10
+
11
+ ```ruby
12
+ HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
13
+ # Appends fields to the end.
14
+ # Where:
15
+ # fields is one or more existing fields
16
+ # block is block of code with new fields
17
+ definition.append(fields) do
18
+ label
19
+ field
20
+ collection
21
+ end
22
+ # Inserts one or more fields.
23
+ # Where:
24
+ # :mode is one of [:before, :after, :replace]
25
+ # :id is field's id or key. Field's label can be used if field does not
26
+ # have id
27
+ # fields is one or more existing fields
28
+ # block is block of code with new fields
29
+ definition.insert(:mode, :id, fields) do
30
+ label
31
+ field
32
+ collection
33
+ end
34
+ # Returns output definition of the command or field specified with path.
35
+ # Where:
36
+ # path = Array of :key or/and :id or/and 'label']
37
+ definition.at(path)
38
+ # Returns field from current output definition.
39
+ definition.find_field(:id)
40
+ # Deletes all fields from current output definition.
41
+ definition.clear
42
+ end
43
+ ```
44
+ #### Examples
45
+ ```ruby
46
+ # Append some fields to the end
47
+ HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
48
+ definition.at(_('Collection Field Label'))
49
+ .append do
50
+ field :value3, _('Value 3')
51
+ end
52
+ end
53
+ # Change field's label
54
+ HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
55
+ definition.at(:path)
56
+ .find_field(:id).label = _('New label')
57
+ end
58
+ # Expand a field with new definition
59
+ HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
60
+ definition.at([_('some'), :path])
61
+ .insert(:replace, :not_container_field_id) do
62
+ field nil, _('Label with new fields'), Fields::Label, id: :now_container_field_id do
63
+ field :new_field1, _('New field 1')
64
+ field :new_field2, _('New field 2')
65
+ end
66
+ end
67
+ end
68
+ # Insert a new field after specific field
69
+ HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
70
+ definition.at([_('some'), :path])
71
+ .insert(:after, :other_field_id, [Fields::Field.new(label: _('My field'), id: :my_field_id)])
72
+ end
73
+ # Insert a new field before specific field
74
+ HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
75
+ definition.insert(:before, :other_field_id,
76
+ Fields::Field.new(label: _('My field'), id: :my_field_id))
77
+ end
78
+ # Remove field
79
+ HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
80
+ definition.insert(:replace, :field_id) do; end
81
+ end
82
+ ```
@@ -169,33 +169,7 @@ In cases when you want to deprecate just one of more possible switches use the e
169
169
  Hammer commands offer option builders that can be used for automatic option generation.
170
170
  See [documentation page](option_builders.md#option-builders) dedicated to this topic for more details.
171
171
 
172
- ### Option validation
173
- Hammer provides extended functionality for validating options.
174
-
175
- #### DSL
176
- First of all there is a dsl for validating combinations of options:
177
- ```ruby
178
- validate_options do
179
- all(:option_name, :option_surname).required # requires all the options
180
- option(:option_age).required # requires a single option,
181
- # equivalent of :required => true in option declaration
182
- any(:option_email, :option_phone).required # requires at least one of the options
183
-
184
- # It is possible to create more complicated constructs.
185
- # This example requires either the full address or nothing
186
- if any(:option_street, :option_city, :option_zip).exist?
187
- all(:option_street, :option_city, :option_zip).required
188
- end
189
-
190
- # Here you can reject all address related option when --no-address is passed
191
- if option(:option_no_address).exist?
192
- all(:option_street, :option_city, :option_zip).rejected
193
- end
194
- end
195
-
196
- ```
197
-
198
- #### Option normalizers
172
+ ### Option normalizers
199
173
  Another option-related feature is a set of normalizers for specific option types. They validate and preprocess
200
174
  option values. Each normalizer has a description of the format it accepts. This description is printed
201
175
  in commands' help.
@@ -244,24 +218,90 @@ option "--attributes", "ATTRIBUTES", "Values of various attributes",
244
218
 
245
219
  ### Advanced option evaluation
246
220
 
247
- Sometimes it is necessary to tune the option values based on other parameters given on CLI.
221
+ Sometimes it is necessary to tune or validate the option values based on other parameters given on CLI.
248
222
  An example could be setting default values based on other options, values lookup in a DB, etc.
249
- The right place for this are `OptionSources`. Abstract Hammer command uses two default option sources -
250
- `HammerCLI::Options::Sources::CommandLine` responsible for intial population of the options,
223
+ The right place for this are option processors. There are two basic kinds of option processors in hammer:
224
+ - *option sources* - they provide values for the options, descendants of `HammerCLI::Options::Sources::Base`
225
+ - *option validators* - they check if the options values are valid, descendants of `HammerCLI::Options::Validators::Base`
226
+
227
+ Option sources and validators can be mixed together. For example it's possible to collect options from the command line,
228
+ do some validation on them and then continue with collecting options from a different source.
229
+ The whole set of processors is invoked only once per command call. The processing is triggered by a first call
230
+ to the `options` or `all_options` method, but at latest right after the option validation
231
+ (before the command's `execute` method is invoked). The order is as follows:
232
+ 1. option parsing
233
+ 1. option normalization
234
+ 1. option processors execution (sources and validators)
235
+ 1. `execute` invocation
236
+
237
+ #### Default option sources
238
+
239
+ Abstract Hammer command uses two default option sources -
240
+ `HammerCLI::Options::Sources::CommandLine` responsible for intial population of the options and
251
241
  `HammerCLI::Options::Sources::SavedDefaults` adding defaults managed by the `defaults` command.
252
242
 
243
+ The default option sources are wrapped in `DefaultInputs` processor list so that it's possible to easily place
244
+ custom sources before or behind all the default ones.
245
+ The full default hierarchy is:
246
+
247
+ ```
248
+ - DefaultInputs
249
+ - CommandLine
250
+ - SavedDefaults (present only when defaults are enabled)
251
+ ```
252
+
253
253
  By overriding `option_sources` method in a command it is possible to add custom option sources
254
254
  for various tasks to the list. The option sources are evaluated one by one each being given output
255
255
  of the previous one as its input so the order in which the sources are listed matters.
256
256
 
257
- Option sources are collected only once per command call. The collection is triggered by first call
258
- to the `options` or `all_options` method, but at latest right after the option validation
259
- (before the command's `execute` method is invoked). The order is as follows:
260
- 1. parse
261
- 1. option normalization
262
- 1. option validation (run against normalized raw options as given on CLI)
263
- 1. option sources execution
264
- 1. `execute` invocation
257
+ #### Option validation
258
+ Hammer provides extended functionality for validating options.
259
+
260
+ First of all there is a DSL for validating combinations of options:
261
+ ```ruby
262
+ validate_options do
263
+ all(:option_name, :option_surname).required # requires all the options
264
+ option(:option_age).required # requires a single option,
265
+ # equivalent of :required => true in option declaration
266
+ any(:option_email, :option_phone).required # requires at least one of the options
267
+
268
+ # It is possible to create more complicated constructs.
269
+ # This example requires either the full address or nothing
270
+ if any(:option_street, :option_city, :option_zip).exist?
271
+ all(:option_street, :option_city, :option_zip).required
272
+ end
273
+
274
+ # Here you can reject all address related option when --no-address is passed
275
+ if option(:option_no_address).exist?
276
+ all(:option_street, :option_city, :option_zip).rejected
277
+ end
278
+ end
279
+
280
+ ```
281
+
282
+ It's possible to insert a validation block on a certain place in the option processor chain:
283
+ ```ruby
284
+ validate_options(:after, 'DefaultInputs') do
285
+ # ...inserts the validation block after DefaultInputs option source
286
+ end
287
+
288
+ validate_options(:prepend) do
289
+ # ...adds validation to the first place in the queue
290
+ end
291
+
292
+ # Following insert modes can be used:
293
+ # before, after, append, prepend (the default behavior)
294
+ ```
295
+
296
+ Alternatively the functionality can be extracted in a validator object that can be shared by multiple commands:
297
+
298
+ ```ruby
299
+ validate_options(:after, 'DefaultInputs', validator: Custom::Validator.new)
300
+
301
+ ```
302
+
303
+ `validate_options` adds validators into a specific command class and they aren't inherited with subclasses.
304
+ Inheritable validators can be created command's `add_validators` method.
265
305
 
266
306
 
267
307
  ### Adding subcommands
@@ -8,6 +8,8 @@ before creating hammer specific plugins.
8
8
  Contents:
9
9
  - [Writing a plugin](writing_a_plugin.md#writing-your-own-hammer-plugin)
10
10
  - [Creating commands](creating_commands.md#create-your-first-command)
11
+ - [Help modification](help_modification.md#modify-an-existing-help)
12
+ - [Commands modification](commands_modification.md#modify-an-existing-command)
11
13
  - [Option builders](option_builders.md#option-builders)
12
14
  - [Creating ApiPie commands](creating_apipie_commands.md#creating-commands-for-restful-api-with-apipie)
13
15
  - [Development tips](development_tips.md#development-tips)
@@ -0,0 +1,119 @@
1
+ Modify an existing help
2
+ -------------------------
3
+ Each command might have its own help definition. This definition is composed of various _help items_, which might contain their own definitions.
4
+
5
+ Let's say `hammer host create -h` help string has the following structure:
6
+ ```
7
+ hammer host create -h
8
+ +-- Usage
9
+ +-- Options
10
+ +-- Additional info
11
+ | +-- Section(Available keys for --interface:)
12
+ | | +-- List
13
+ | | +-- Section
14
+ | | | +-- List
15
+ | | +-- Section
16
+ | | | +-- List
17
+ | | +-- Section
18
+ | | | +-- List
19
+ | +-- Section(Provider specific options:)
20
+ | | +-- Section
21
+ | | | +-- Section
22
+ | | | | +-- List
23
+ ...
24
+ | | +-- Section(Libvirt:)
25
+ | | | +-- Section(--compute-attributes:)
26
+ | | | | +-- List[
27
+ cpus Number of CPUs
28
+ memory String, amount of memory, value in bytes
29
+ start Boolean (expressed as 0 or 1), whether to start the machine or not
30
+ ]
31
+ ...
32
+ | | | +-- Section
33
+ | | | | +-- List
34
+ ```
35
+ Every `Section`, `List` and simple `Text` is _help item_ defined as `HammerCLI::Help::AbstractItem`, so everything might have its own ID now for easier addressing.
36
+
37
+ To modify the structure above, you might use the following:
38
+ ```ruby
39
+ HammerCLIForeman::Host::CreateCommand.extend_help do |h|
40
+ # Simple addition to the end.
41
+ h.section
42
+ h.list
43
+ h.text
44
+
45
+ # Inserts one or more help items.
46
+ # Where:
47
+ # :mode is one of [:before, :after, :replace]
48
+ # :item_id is item's id or label. Item's label can be used if it does not
49
+ # have an id, e.g. any Section
50
+ # block is block of code with new items
51
+ h.insert(:mode, :item_id) do |h|
52
+ h.section
53
+ h.list
54
+ h.text
55
+ end
56
+ # Addition to custom path.
57
+ # Where:
58
+ # path is Array of :item_id or/and 'item_label'
59
+ # block is the code block with new help items.
60
+ h.at(path) do |h|
61
+ ...
62
+ h.section
63
+ h.list
64
+ h.text
65
+ ...
66
+ end
67
+ # Returns help item from current definition.
68
+ h.find_item(:item_id)
69
+ end
70
+ ```
71
+
72
+ #### Examples
73
+ ```ruby
74
+ # Add a new section with list to the end of the Libvirt section.
75
+ HammerCLIForeman::Host::CreateCommand.extend_help do |h|
76
+ h.at(['Provider specific options', 'Libvirt']) do |h|
77
+ h.section('--new-section-with-list', id: :section_with_list) do |h|
78
+ h.list([
79
+ ['item1', _('Desc1')],
80
+ ['item2'],
81
+ ['item3', _('Desc3')]
82
+ ], id: :list_with_id)
83
+ end
84
+ end
85
+ end
86
+
87
+ # Add important information
88
+ HammerCLIForeman::Host::CreateCommand.extend_help do |h|
89
+ h.at(['Provider specific options']) do |h|
90
+ h.insert(:before, 'EC2') do |h|
91
+ h.text('Something important for all providers')
92
+ end
93
+ h.at(['EC2']) do |h|
94
+ h.insert(:before, '--compute-attributes') do |h|
95
+ h.text('Something important for EC2 only')
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ # Delete the Libvirt section from Provider specific options section.
102
+ HammerCLIForeman::Host::CreateCommand.extend_help do |h|
103
+ h.at('Provider specific options') do |h|
104
+ h.insert(:replace, 'Libvirt') do |h|; end
105
+ end
106
+ end
107
+
108
+ # Add more text
109
+ HammerCLIForeman::Host::CreateCommand.extend_help do |h|
110
+ h.at('Provider specific options') do |h|
111
+ h.insert(:before, 'EC2') do |h|
112
+ h.text('Imagine it already was here', id: :old_text_id)
113
+ end
114
+ end
115
+ h.at(['Provider specific options', :old_text_id]) do |h|
116
+ h.text('Additional information', id: :additional_text)
117
+ end
118
+ end
119
+ ```
@@ -73,8 +73,8 @@ HammerCLI::I18n::FindTask.define(HammerCLIAwesome::I18n::LocaleDomain.new, Hamme
73
73
  and create `locale/Makefile` with following content:
74
74
  ```make
75
75
  DOMAIN = hammer-cli-awesome
76
- VERSION = $(shell bundle exec ruby -e 'require "rubygems"; spec = Gem::Specification::load("../hammer_cli_awesome.gemspec"); puts spec.version')
77
- MAIN_MAKEFILE = $(shell bundle exec ruby -e 'require "hammer_cli"; puts HammerCLI::I18n.main_makefile')
76
+ VERSION = $(shell bundle exec ruby -C ../ -e 'require "rubygems"; spec = Gem::Specification::load("../hammer_cli_awesome.gemspec"); puts spec.version')
77
+ MAIN_MAKEFILE = $(shell bundle exec ruby -C ../ -e 'require "hammer_cli"; puts HammerCLI::I18n.main_makefile')
78
78
 
79
79
  include $(MAIN_MAKEFILE)
80
80
  ```
@@ -32,7 +32,7 @@ On RHEL systems you will also have to add [EPEL repository](https://fedoraprojec
32
32
  #### Step 2: install hammer core
33
33
 
34
34
  ```bash
35
- yum install rubygem-hammer_cli
35
+ yum install tfm-rubygem-hammer_cli
36
36
  ```
37
37
 
38
38
  #### Step 3: install plugins
@@ -41,13 +41,13 @@ Currently, there are two plugins, both available as rpm packages.
41
41
  - commands for managing foreman
42
42
 
43
43
  ```bash
44
- yum install rubygem-hammer_cli_foreman
44
+ yum install tfm-rubygem-hammer_cli_foreman
45
45
  ```
46
46
 
47
47
  - commands for managing [katello](https://github.com/Katello/katello)
48
48
 
49
49
  ```bash
50
- yum install rubygem-hammer_cli_katello
50
+ yum install tfm-rubygem-hammer_cli_katello
51
51
  ```
52
52
 
53
53
  To install any other hammer plugin just make sure the appropriate gem is installed and follow with the [configuration](installation.md#configuration).
@@ -1,8 +1,16 @@
1
1
  Release notes
2
2
  =============
3
- ### 0.15.1 (2018-11-06)
3
+ ### 0.16.0 (2019-01-16)
4
+ * Fixed instructions in i18n docs ([#25724](http://projects.theforeman.org/issues/25724))
5
+ * Option validators can be mixed with sources ([#22253](http://projects.theforeman.org/issues/22253))
6
+ * Validator uses options from option collector ([#19574](http://projects.theforeman.org/issues/19574))
7
+ * Hammer -h does work with --output json, csv, yaml ([#25438](http://projects.theforeman.org/issues/25438))
8
+ * Update rpm names to use tfm ([PR #296](https://github.com/theforeman/hammer-cli/pull/296))
9
+ * Allows modification of output fields ([PR #275](https://github.com/theforeman/hammer-cli/pull/275)) ([#21634](http://projects.theforeman.org/issues/21634))
10
+ * Allow skipping message tests ([PR #292](https://github.com/theforeman/hammer-cli/pull/292)) ([#7451](http://projects.theforeman.org/issues/7451))
4
11
  * Helper for editing text in editor ([PR #294](https://github.com/theforeman/hammer-cli/pull/294)) ([#24489](http://projects.theforeman.org/issues/24489))
5
12
  * Add ability to disable defaults ([PR #293](https://github.com/theforeman/hammer-cli/pull/293)) ([#25307](http://projects.theforeman.org/issues/25307))
13
+ * Allow modification of additional help texts ([PR #277](https://github.com/theforeman/hammer-cli/pull/277)) ([#21633](http://projects.theforeman.org/issues/21633))
6
14
 
7
15
  ### 0.15.0 (2018-10-24)
8
16
  * Correct the gemspec ([PR #286](https://github.com/theforeman/hammer-cli/pull/286))
@@ -9,7 +9,6 @@ require 'hammer_cli/settings'
9
9
  require 'hammer_cli/logger'
10
10
  require 'hammer_cli/ca_cert_manager'
11
11
  require 'hammer_cli/connection'
12
- require 'hammer_cli/validator'
13
12
  require 'hammer_cli/output'
14
13
  require 'hammer_cli/options/normalizers'
15
14
  require 'hammer_cli/completer'
@@ -2,14 +2,17 @@ require 'hammer_cli/exception_handler'
2
2
  require 'hammer_cli/logger_watch'
3
3
  require 'hammer_cli/options/option_definition'
4
4
  require 'hammer_cli/options/option_collector'
5
+ require 'hammer_cli/options/processor_list'
5
6
  require 'hammer_cli/options/sources/command_line'
6
7
  require 'hammer_cli/options/sources/saved_defaults'
8
+ require 'hammer_cli/options/validators/dsl_block_validator'
7
9
  require 'hammer_cli/clamp'
8
10
  require 'hammer_cli/subcommand'
9
11
  require 'hammer_cli/options/matcher'
10
12
  require 'hammer_cli/help/builder'
11
13
  require 'hammer_cli/help/text_builder'
12
14
  require 'logging'
15
+
13
16
  module HammerCLI
14
17
 
15
18
  class AbstractCommand < Clamp::Command
@@ -17,6 +20,10 @@ module HammerCLI
17
20
 
18
21
  class << self
19
22
  attr_accessor :validation_blocks
23
+
24
+ def help_extension_blocks
25
+ @help_extension_blocks ||= []
26
+ end
20
27
  end
21
28
 
22
29
  def adapter
@@ -40,7 +47,7 @@ module HammerCLI
40
47
  super
41
48
  validate_options
42
49
  logger.info "Called with options: %s" % options.inspect
43
- rescue HammerCLI::Validator::ValidationError => e
50
+ rescue HammerCLI::Options::Validators::ValidationError => e
44
51
  signal_usage_error e.message
45
52
  end
46
53
 
@@ -48,15 +55,14 @@ module HammerCLI
48
55
  HammerCLI::EX_OK
49
56
  end
50
57
 
51
- def self.validate_options(&block)
58
+ def self.validate_options(mode=:append, target_name=nil, validator: nil, &block)
59
+ validator ||= HammerCLI::Options::Validators::DSLBlockValidator.new(&block)
52
60
  self.validation_blocks ||= []
53
- self.validation_blocks << block
61
+ self.validation_blocks << [mode, target_name, validator]
54
62
  end
55
63
 
56
64
  def validate_options
57
- if self.class.validation_blocks && self.class.validation_blocks.any?
58
- self.class.validation_blocks.each { |validation_block| validator.run(&validation_block) }
59
- end
65
+ # keep the method for legacy reasons
60
66
  end
61
67
 
62
68
  def exception_handler
@@ -80,9 +86,16 @@ module HammerCLI
80
86
  def self.help(invocation_path, builder = HammerCLI::Help::Builder.new)
81
87
  super(invocation_path, builder)
82
88
 
83
- if @help_extension_block
89
+ unless help_extension_blocks.empty?
84
90
  help_extension = HammerCLI::Help::TextBuilder.new(builder.richtext)
85
- @help_extension_block.call(help_extension)
91
+ help_extension_blocks.each do |extension_block|
92
+ begin
93
+ extension_block.call(help_extension)
94
+ rescue ArgumentError => e
95
+ handler = HammerCLI::ExceptionHandler.new
96
+ handler.handle_exception(e)
97
+ end
98
+ end
86
99
  builder.add_text(help_extension.string)
87
100
  end
88
101
  builder.string
@@ -90,7 +103,14 @@ module HammerCLI
90
103
 
91
104
  def self.extend_help(&block)
92
105
  # We save the block for execution on object level, where we can access command's context and check :is_tty? flag
93
- @help_extension_block = block
106
+ self.help_extension_blocks << block
107
+ end
108
+
109
+ def self.extend_output_definition(&block)
110
+ block.call(output_definition)
111
+ rescue ArgumentError => e
112
+ handler = HammerCLI::ExceptionHandler.new
113
+ handler.handle_exception(e)
94
114
  end
95
115
 
96
116
  def self.output(definition=nil, &block)
@@ -180,8 +200,8 @@ module HammerCLI
180
200
  end
181
201
 
182
202
  def validator
183
- options = self.class.recognised_options.collect{|opt| opt.of(self)}
184
- @validator ||= HammerCLI::Validator.new(options)
203
+ # keep the method for legacy reasons, it's used by validate_options
204
+ @validator ||= HammerCLI::Options::Validators::DSL.new(self.class.recognised_options, all_options)
185
205
  end
186
206
 
187
207
  def handle_exception(e)
@@ -240,13 +260,24 @@ module HammerCLI
240
260
  end
241
261
 
242
262
  def option_collector
243
- @option_collector ||= HammerCLI::Options::OptionCollector.new(self.class.recognised_options, option_sources)
263
+ @option_collector ||= HammerCLI::Options::OptionCollector.new(self.class.recognised_options, add_validators(option_sources))
244
264
  end
245
265
 
246
266
 
247
267
  def option_sources
248
- sources = [HammerCLI::Options::Sources::CommandLine.new(self)]
268
+ sources = HammerCLI::Options::ProcessorList.new(name: 'DefaultInputs')
269
+ sources << HammerCLI::Options::Sources::CommandLine.new(self)
249
270
  sources << HammerCLI::Options::Sources::SavedDefaults.new(context[:defaults], logger) if context[:use_defaults]
271
+
272
+ HammerCLI::Options::ProcessorList.new([sources])
273
+ end
274
+
275
+ def add_validators(sources)
276
+ if self.class.validation_blocks
277
+ self.class.validation_blocks.each do |validation_block|
278
+ sources.insert_relative(*validation_block)
279
+ end
280
+ end
250
281
  sources
251
282
  end
252
283