hammer_cli 0.19.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/bin/hammer-complete +28 -0
  3. data/config/cli_config.template.yml +2 -0
  4. data/config/hammer.completion +5 -0
  5. data/doc/commands_extension.md +12 -0
  6. data/doc/creating_commands.md +100 -0
  7. data/doc/installation.md +47 -4
  8. data/doc/installation_rpm.md +2 -2
  9. data/doc/release_notes.md +31 -6
  10. data/lib/hammer_cli.rb +1 -0
  11. data/lib/hammer_cli/abstract.rb +61 -4
  12. data/lib/hammer_cli/apipie/api_connection.rb +5 -1
  13. data/lib/hammer_cli/apipie/command.rb +3 -2
  14. data/lib/hammer_cli/apipie/option_builder.rb +15 -13
  15. data/lib/hammer_cli/apipie/option_definition.rb +9 -7
  16. data/lib/hammer_cli/bash.rb +2 -0
  17. data/lib/hammer_cli/bash/completion.rb +159 -0
  18. data/lib/hammer_cli/bash/prebuild_command.rb +21 -0
  19. data/lib/hammer_cli/command_extensions.rb +21 -1
  20. data/lib/hammer_cli/connection.rb +4 -0
  21. data/lib/hammer_cli/exception_handler.rb +11 -2
  22. data/lib/hammer_cli/full_help.rb +8 -1
  23. data/lib/hammer_cli/help/builder.rb +29 -3
  24. data/lib/hammer_cli/logger_watch.rb +1 -1
  25. data/lib/hammer_cli/main.rb +5 -3
  26. data/lib/hammer_cli/options/normalizers.rb +7 -3
  27. data/lib/hammer_cli/options/option_definition.rb +26 -6
  28. data/lib/hammer_cli/options/option_family.rb +114 -0
  29. data/lib/hammer_cli/options/predefined.rb +1 -1
  30. data/lib/hammer_cli/output/adapter/abstract.rb +1 -5
  31. data/lib/hammer_cli/output/adapter/base.rb +1 -1
  32. data/lib/hammer_cli/output/adapter/csv.rb +3 -2
  33. data/lib/hammer_cli/output/adapter/json.rb +14 -3
  34. data/lib/hammer_cli/output/adapter/silent.rb +1 -1
  35. data/lib/hammer_cli/output/adapter/table.rb +27 -8
  36. data/lib/hammer_cli/output/adapter/yaml.rb +6 -3
  37. data/lib/hammer_cli/output/output.rb +2 -4
  38. data/lib/hammer_cli/settings.rb +2 -1
  39. data/lib/hammer_cli/subcommand.rb +25 -1
  40. data/lib/hammer_cli/testing/command_assertions.rb +2 -2
  41. data/lib/hammer_cli/utils.rb +22 -0
  42. data/lib/hammer_cli/version.rb +1 -1
  43. data/locale/ca/LC_MESSAGES/hammer-cli.mo +0 -0
  44. data/locale/de/LC_MESSAGES/hammer-cli.mo +0 -0
  45. data/locale/en/LC_MESSAGES/hammer-cli.mo +0 -0
  46. data/locale/en_GB/LC_MESSAGES/hammer-cli.mo +0 -0
  47. data/locale/es/LC_MESSAGES/hammer-cli.mo +0 -0
  48. data/locale/fr/LC_MESSAGES/hammer-cli.mo +0 -0
  49. data/locale/it/LC_MESSAGES/hammer-cli.mo +0 -0
  50. data/locale/ja/LC_MESSAGES/hammer-cli.mo +0 -0
  51. data/locale/ko/LC_MESSAGES/hammer-cli.mo +0 -0
  52. data/locale/pt_BR/LC_MESSAGES/hammer-cli.mo +0 -0
  53. data/locale/ru/LC_MESSAGES/hammer-cli.mo +0 -0
  54. data/locale/zh_CN/LC_MESSAGES/hammer-cli.mo +0 -0
  55. data/locale/zh_TW/LC_MESSAGES/hammer-cli.mo +0 -0
  56. data/test/unit/abstract_test.rb +23 -2
  57. data/test/unit/apipie/api_connection_test.rb +1 -0
  58. data/test/unit/apipie/option_builder_test.rb +8 -0
  59. data/test/unit/bash_test.rb +138 -0
  60. data/test/unit/command_extensions_test.rb +67 -49
  61. data/test/unit/exception_handler_test.rb +44 -0
  62. data/test/unit/help/builder_test.rb +22 -0
  63. data/test/unit/options/option_family_test.rb +48 -0
  64. data/test/unit/output/adapter/base_test.rb +58 -0
  65. data/test/unit/output/adapter/csv_test.rb +63 -1
  66. data/test/unit/output/adapter/json_test.rb +61 -0
  67. data/test/unit/output/adapter/table_test.rb +70 -1
  68. data/test/unit/output/adapter/yaml_test.rb +59 -0
  69. data/test/unit/output/output_test.rb +3 -3
  70. metadata +17 -6
  71. data/hammer_cli_complete +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fba56b0d34d9914c40df8f424e68d9268d7e2a38e5f2dd421e10991f477fbd1d
4
- data.tar.gz: 5b1e6d8ea29af809ab09884cc2579c3f328439a7d102335940820bd32ec3289d
3
+ metadata.gz: d903c3dbbe33a2411dfd2e23b781609a40b39b4ed29066cabe35af22335042d5
4
+ data.tar.gz: 25acdf29e7a071a945a4c7c2c4745083e0e11e7d12ce098c662054a7d87caeb1
5
5
  SHA512:
6
- metadata.gz: 7fdb4ed23030a8d87156504452c1b04959dd466015579ec2dec75b548bd5af2baea8a5538f0d4b866a2038addf2034c9df2bd89dc709378b275868e6c48c9e99
7
- data.tar.gz: 45d29077823ab5997435b51f4cfab81a4c1373cd42793958811cf7ec48ab5837f48779b4ab237af9b980aca43dd43b685b2197e3f485dfc4894421098898c5f3
6
+ metadata.gz: 8a117c635359bc261f70cb67bf33fc2080f15b909981a98f87d7c64235a2ecf686bf1530b5293e60565e5e52cab2ed9bbf17f604816099ada8e11b11a5b7f064
7
+ data.tar.gz: 3e296e36f46d6374fc1878a51447cd2fe820104fa5ba191c6bde91a62aff2fd7b76e12743d152ca0eb9457b541e38dd39351da138020501d286c3af071e33b39
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'English'
4
+ @project_root = File.expand_path('../..', __FILE__)
5
+ $LOAD_PATH.unshift(File.join(@project_root, 'lib'))
6
+ HAMMER = ENV['HAMMER'] || File.join(@project_root, 'bin/hammer')
7
+
8
+ require 'yaml'
9
+
10
+ completion_cache_file = ENV['HAMMER_COMPLETION_CACHE'] || '~/.cache/hammer_completion.yml'
11
+ completion_cache_file = File.expand_path(completion_cache_file)
12
+
13
+ # build the cache if it does not exist
14
+ unless File.exist?(completion_cache_file)
15
+ require 'hammer_cli'
16
+ `#{HAMMER} prebuild-bash-completion`
17
+ end
18
+
19
+ require 'hammer_cli/bash/completion'
20
+
21
+ dict = HammerCLI::Bash::Completion.load_description(completion_cache_file)
22
+
23
+ comp_line = ENV['COMP_LINE'] || ''
24
+ comp_args = comp_line.split(' ', 2).last || ''
25
+
26
+ result = HammerCLI::Bash::Completion.new(dict).complete(comp_args)
27
+
28
+ puts result.join("\n")
@@ -44,6 +44,8 @@
44
44
  # Log record pattern (logging gem syntax)
45
45
  #:log_pattern: '[%5l %d %c] %m'
46
46
 
47
+ # Location of cache for bash completion
48
+ :completion_cache_file: '~/.cache/hammer_completion.yml'
47
49
 
48
50
  # SSL auth options
49
51
  #:ssl:
@@ -0,0 +1,5 @@
1
+ #
2
+ # Hammer CLI bash completion script
3
+ #
4
+
5
+ complete -C hammer-complete -o nospace hammer
@@ -10,6 +10,11 @@ Each command can be easily extended with one ore more `HammerCLI::CommandExtensi
10
10
  inheritable true
11
11
  # Simply add a new option to a command is being extended
12
12
  option(option_params)
13
+ # Add option family to a command
14
+ option_family(common_options = {}) do
15
+ parent option_params
16
+ child option_params
17
+ end
13
18
  # Extend hash with data returned from server before it is printed
14
19
  before_print do |data|
15
20
  # data modifications
@@ -65,6 +70,13 @@ class MyCommandExtensions < HammerCLI::CommandExtensions
65
70
 
66
71
  option ['--new-option'], 'TYPE', _('Option description')
67
72
 
73
+ option_family(
74
+ description: _('Common description')
75
+ ) do
76
+ parent ['--new-option'], 'TYPE', _('Option description')
77
+ child ['--new-option-ver2'], 'TYPE', _('Option description')
78
+ end
79
+
68
80
  before_print do |data|
69
81
  data['results'].each do |result|
70
82
  result['status'] = process_errors(result['errors'])
@@ -173,6 +173,54 @@ Here is the list of predefined options:
173
173
  * `:fields` Expects a list with fields to show in output, see [example](creating_commands.md#printing-hash-records).
174
174
 
175
175
 
176
+ ### Option family
177
+ Option family is the way to unify options which have the same meaning or purpose,
178
+ but contain some differences in their definitions (e.g. the name/switch of an option).
179
+ Mainly serves as a container for options, which purpose is to show less repetitive
180
+ output in commands' help. Option builders use it by default.
181
+
182
+ To define an option family, use the following DSL:
183
+ ```ruby
184
+ # options is a Hash with options for family/each defined option within it
185
+ option_family(options = {}) do
186
+ # parent is the main option. Must be single, option family can have only one parent.
187
+ parent switches, type, description, options
188
+ # child is an additional option. Could be none or more than one. Aren't shown in the help output.
189
+ child switches, type, description, options
190
+ end
191
+ ```
192
+
193
+ ##### Example
194
+
195
+ ```ruby
196
+ option_family(
197
+ aliased_resource: 'environment',
198
+ description: _('Puppet environment'),
199
+ deprecation: _("Use %s instead") % '--puppet-environment[-id]'
200
+ deprecated: { '--environment' => _("Use %s instead") % '--puppet-environment[-id]',
201
+ '--environment-id' => _("Use %s instead") % '--puppet-environment[-id]'}
202
+ ) do
203
+ parent '--environment-id', 'ENVIRONMENT_ID', _(''),
204
+ format: HammerCLI::Options::Normalizers::Number.new,
205
+ attribute_name: :option_environment_id
206
+ child '--environment', 'ENVIRONMENT_NAME', _('Environment name'),
207
+ attribute_name: :option_environment_name
208
+ end
209
+
210
+ # $ hammer command --help:
211
+ # ...
212
+ # Options:
213
+ # --environment[-id] Puppet environment (Deprecated: Use --puppet-environment[-id] instead)
214
+ # ...
215
+
216
+ # $ hammer full-help:
217
+ # ...
218
+ # Options:
219
+ # --environment ENVIRONMENT_NAME Environment name (--environment is deprecated: Use --puppet-environment[-id] instead)
220
+ # --environment-id ENVIRONMENT_ID (--environment-id is deprecated: Use --puppet-environment[-id] instead)
221
+ # ...
222
+ ```
223
+
176
224
  ### Option builders
177
225
  Hammer commands offer option builders that can be used for automatic option generation.
178
226
  See [documentation page](option_builders.md#option-builders) dedicated to this topic for more details.
@@ -406,6 +454,31 @@ Options:
406
454
  -h, --help print help
407
455
  ```
408
456
 
457
+ #### Aliasing subcommands
458
+
459
+ Commands can have two or more names, e.g. aliases. To support such functionality
460
+ simple name addition could be used via `command_name` or `command_names` method:
461
+ ```ruby
462
+ module HammerCLIHello
463
+
464
+ class SayCommand < HammerCLI::AbstractCommand
465
+
466
+ class GreetingsCommand < HammerCLI::AbstractCommand
467
+ command_name 'hello'
468
+ command_name 'hi'
469
+ # or use can use other method:
470
+ command_names 'hello', 'hi'
471
+
472
+ desc 'Say Hello World!'
473
+ # ...
474
+ end
475
+
476
+ autoload_subcommands
477
+ end
478
+
479
+ HammerCLI::MainCommand.subcommand 'say', "Say something", HammerCLIHello::SayCommand
480
+ end
481
+ ```
409
482
 
410
483
  ### Conflicting subcommands
411
484
  It can happen that two different plugins define subcommands with the same name by accident.
@@ -494,6 +567,33 @@ You first create an _output definition_ that you apply to your data. The result
494
567
  is a collection of fields, each having its type. The collection is then passed to an
495
568
  _output adapter_ which handles the actual formatting and printing.
496
569
 
570
+ Adapters support printing by chunks, e.g. if you want to print a large set of
571
+ data (1000+ records), but you make several calls to the server instead of one,
572
+ you may want to print received data right away instead of waiting for the rest.
573
+ This can be achieved via `:current_chunk` option for
574
+ `print_collection` and `print_data` methods. Allowed values for `:current_chunk`
575
+ are `:first`, `:another`, `:last`. By default adapters use `:single` value that
576
+ means only one record will be printed.
577
+
578
+ ##### Printing by chunks
579
+ ```ruby
580
+ # ...
581
+ def execute
582
+ loop do
583
+ # ...
584
+ data = send_request
585
+ print_data(data, current_chunk: :first)
586
+ # ...
587
+ data = send_request
588
+ print_data(data, current_chunk: :another)
589
+ # ...
590
+ data = send_request
591
+ print_data(data, current_chunk: :last)
592
+ end
593
+ end
594
+ # ...
595
+ ```
596
+
497
597
  Hammer provides a DSL for defining the output. Next rather complex example will
498
598
  explain how to use it in action.
499
599
 
@@ -132,8 +132,51 @@ And you are done. Your hammer client is configured and ready to use.
132
132
  Autocompletion
133
133
  --------------
134
134
 
135
- It is necessary to copy the hammer_cli_complete script to the bash_completion.d directory.
136
-
137
- $ sudo cp hammer-cli/hammer_cli_complete /etc/bash_completion.d/
135
+ The completion offers suggestion of possible command-line subcommands and their
136
+ options as usual. It can also suggest values for options and params where file
137
+ or directory path is expected.
138
+
139
+ Bash completion is automatically installed by RPM. To use it for development
140
+ setup `cp ./config/hammer.completion /etc/bash_completion.d/hammer` and load it
141
+ to the current shell `source /etc/bash_completion.d/hammer`. Make sure
142
+ the `$PWD/bin` is in `PATH` or there is full path to `hammer-complete`
143
+ executable specified in `/etc/bash_completion.d/hammer`.
144
+
145
+ Bash completion for hammer needs pre-built cache that holds description of
146
+ all subcommands and its parameters. The cache is located by default in
147
+ `~/.cache/hammer_completion.yml`. The location can be changed in hammer's
148
+ config file. The cache can be built manually with
149
+ `hammer prebuild-bash-completion` or is built automatically when completion is
150
+ used and the cache is missing (this may cause slight delay). The cache expires
151
+ if your API cache was changed (it indicates that the features on the instance
152
+ may have changed which has impact on hammer CLI options and subcommands).
153
+
154
+ #### Available value types
155
+
156
+ Completion of values is dependent on CLI option and prameter settings, e.g.:
157
+
158
+ ```ruby
159
+ option '--value', 'VALUE', 'One of a, b, c', completion: { type: :enum, values: %w[a b c] }
160
+ ```
138
161
 
139
- Then after starting a new shell the completion should work.
162
+ Possible options for the `:completion` attribute are:
163
+ - `{ type: :flag }` option has no value, default for flags.
164
+ - `{ type: :value }` option has value of unknown type, no suggestions for the
165
+ value, default.
166
+ - `{ type: :list }` option has value of list type, no suggestions for the
167
+ value.
168
+ - `{ type: :key_value_list }` option has value of key=value list type, no
169
+ suggestions for the value.
170
+ - `{ type: :directory }` value is directory, suggestions follow directory
171
+ structure.
172
+ - `{ type: :file, filter: '\.txt$' }` value is file, suggestions follow
173
+ directory structure, optional `:filter` is regexp to filter the results.
174
+ - `{ type: :enum, values: ['first', 'second']}` option can have one of the
175
+ listed values, suggestions follow specified `values`.
176
+ - `{ type: :multienum, values: ['first', 'second']}` option can have one or
177
+ more of the listed values, suggestions follow specified `values`.
178
+ - `{ type: :schema, schema: 'a=int\,b=string' }` option should have value
179
+ according to specified schema, suggestion is the specified schema.
180
+
181
+ All those completion attributes are generated automatically, specify you own to
182
+ override.
@@ -1,8 +1,8 @@
1
1
  ### Installation from RPMs
2
2
 
3
- #### Step 1: setup yum repositories
3
+ #### Step 1: set up yum repositories
4
4
 
5
- For Foreman 1.3 stable the hammer packages are part of your installation repo and you can skip this step.
5
+ For Foreman 1.3 stable, the hammer packages are part of your installation repo and you can skip this step.
6
6
 
7
7
  You can choose from stable or nightly repo. Nightly has more recent version of hammer packages, but it was subject to less testing so there is a higher risk of issues.
8
8
  Add the Foreman yum repository to your yum repo files. For Fedora installations replace 'el6' with 'f18' or 'f19' as appropriate.
@@ -1,15 +1,40 @@
1
1
  Release notes
2
2
  =============
3
- ### 0.19.1 (2019-12-31)
3
+ ### 2.2.0 (2020-08-11)
4
+ * Update installation_rpm.md ([PR #333](https://github.com/theforeman/hammer-cli/pull/333))
5
+ * Clean gem_release.ipynb up
6
+ * Replace awesome_print with amazing_print ([PR #330](https://github.com/theforeman/hammer-cli/pull/330)), [#29846](http://projects.theforeman.org/issues/29846)
7
+ * Fix typo: s/filed/feild/ ([PR #331](https://github.com/theforeman/hammer-cli/pull/331))
8
+ * Bump to 2.2.0-develop
9
+
10
+ ### 2.1.0 (2020-05-14)
11
+ * Hammer full-help returns correct output, [#29697](http://projects.theforeman.org/issues/29697)
12
+ * Add fuzzy subcommand matching, [#29413](http://projects.theforeman.org/issues/29413)
13
+ * Help contains squeezed options, [#28440](http://projects.theforeman.org/issues/28440)
14
+ * Keep referenced resource in option options, [#29015](http://projects.theforeman.org/issues/29015)
15
+ * Bump to 2.1.0-develop
16
+
17
+ ### 2.0.0 (2020-02-12)
18
+ * Bump version to 2.0.0
19
+ * Bump version to 2.0 ([PR #324](https://github.com/theforeman/hammer-cli/pull/324))
20
+ * Better promts for missing arguments, [#28793](http://projects.theforeman.org/issues/28793)
4
21
  * Allow column max width more than 80, [#28503](http://projects.theforeman.org/issues/28503)
22
+ * Remove computing sha, [#27728](http://projects.theforeman.org/issues/27728)
23
+ * Fixed userdata false display in image list, [#28134](http://projects.theforeman.org/issues/28134)
24
+ * Add new bash completion, [#27728](http://projects.theforeman.org/issues/27728)
25
+ * Allow adapters print page by page, [#17819](http://projects.theforeman.org/issues/17819)
26
+ * Add release documentation ([PR #317](https://github.com/theforeman/hammer-cli/pull/317)), [#28149](http://projects.theforeman.org/issues/28149)
27
+ * Fix pr links in release notes ([PR #318](https://github.com/theforeman/hammer-cli/pull/318)), [#28202](http://projects.theforeman.org/issues/28202)
5
28
  * Extract table generator into reusable component ([PR #314](https://github.com/theforeman/hammer-cli/pull/314)), [#27318](http://projects.theforeman.org/issues/27318)
29
+ * Better prompts for missing arguments ([PR #313](https://github.com/theforeman/hammer-cli/pull/313)), [#27595](http://projects.theforeman.org/issues/27595)
30
+ * Bump to 0.20-develop
6
31
 
7
32
  ### 0.19.0 (2019-10-26)
8
- * Allow schema building for custom options ([PR #316](https://github.com/Apipie/apipie-bindings/pull/316)), [#27899](http://projects.theforeman.org/issues/27899)
9
- * New lines in text attr dont break output ([PR #300](https://github.com/Apipie/apipie-bindings/pull/300)), [#25878](http://projects.theforeman.org/issues/25878)
10
- * Added error to wrong --output ([PR #315](https://github.com/Apipie/apipie-bindings/pull/315)), [#21590](http://projects.theforeman.org/issues/21590)
11
- * Pr review checklist ([PR #305](https://github.com/Apipie/apipie-bindings/pull/305)), [#26950](http://projects.theforeman.org/issues/26950)
12
- * List items in help with customization ([PR #309](https://github.com/Apipie/apipie-bindings/pull/309)), [#27237](http://projects.theforeman.org/issues/27237)
33
+ * Allow schema building for custom options ([PR #316](https://github.com/theforeman/hammer-cli/pull/316)), [#27899](http://projects.theforeman.org/issues/27899)
34
+ * New lines in text attr dont break output ([PR #300](https://github.com/theforeman/hammer-cli/pull/300)), [#25878](http://projects.theforeman.org/issues/25878)
35
+ * Added error to wrong --output ([PR #315](https://github.com/theforeman/hammer-cli/pull/315)), [#21590](http://projects.theforeman.org/issues/21590)
36
+ * Pr review checklist ([PR #305](https://github.com/theforeman/hammer-cli/pull/305)), [#26950](http://projects.theforeman.org/issues/26950)
37
+ * List items in help with customization ([PR #309](https://github.com/theforeman/hammer-cli/pull/309)), [#27237](http://projects.theforeman.org/issues/27237)
13
38
 
14
39
  ### 0.18.0 (2019-08-01)
15
40
  * Unsure minimal label length ([PR #310](https://github.com/theforeman/hammer-cli/pull/310)) ([#26960](http://projects.theforeman.org/issues/26960))
@@ -23,3 +23,4 @@ require 'hammer_cli/apipie'
23
23
  require 'hammer_cli/shell'
24
24
  require 'hammer_cli/defaults'
25
25
  require 'hammer_cli/full_help'
26
+ require 'hammer_cli/bash'
@@ -13,6 +13,7 @@ require 'hammer_cli/options/predefined'
13
13
  require 'hammer_cli/help/builder'
14
14
  require 'hammer_cli/help/text_builder'
15
15
  require 'hammer_cli/command_extensions'
16
+ require 'hammer_cli/options/option_family'
16
17
  require 'logging'
17
18
 
18
19
  module HammerCLI
@@ -74,6 +75,7 @@ module HammerCLI
74
75
  begin
75
76
  begin
76
77
  exit_code = super
78
+ context.delete(:fields)
77
79
  raise "exit code must be integer" unless exit_code.is_a? Integer
78
80
  rescue => e
79
81
  exit_code = handle_exception(e)
@@ -190,10 +192,21 @@ module HammerCLI
190
192
  # skip switches that are already defined
191
193
  next if option.nil? or option.switches.any? {|s| find_option(s) }
192
194
 
195
+ if option.respond_to?(:referenced_resource)
196
+ # Collect options that don't have family, but related to this parent.
197
+ children = find_options(
198
+ referenced_resource: option.referenced_resource.to_s,
199
+ aliased_resource: option.aliased_resource.to_s
200
+ ).select { |o| o.family.nil? || o.family.head.nil? }
201
+ children.each do |child|
202
+ option.family.adopt(child) if option.family
203
+ end
204
+ end
193
205
  declared_options << option
194
206
  block ||= option.default_conversion_block
195
207
  define_accessors_for(option, &block)
196
208
  extend_options_help(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
209
+ completion_type_for(option)
197
210
  end
198
211
  end
199
212
 
@@ -205,6 +218,7 @@ module HammerCLI
205
218
  extension.delegatee(self)
206
219
  extension.extend_predefined_options(self)
207
220
  extension.extend_options(self)
221
+ extension.extend_option_family(self)
208
222
  extension.extend_output(self)
209
223
  extension.extend_help(self)
210
224
  logger('Extensions').info "Applied #{extension.details} on #{self}."
@@ -220,6 +234,12 @@ module HammerCLI
220
234
 
221
235
  protected
222
236
 
237
+ def self.option_family(options = {}, &block)
238
+ options[:creator] ||= self
239
+ family = HammerCLI::Options::OptionFamily.new(options)
240
+ family.instance_eval(&block)
241
+ end
242
+
223
243
  def self.find_options(switch_filter, other_filters={})
224
244
  filters = other_filters
225
245
  if switch_filter.is_a? Hash
@@ -242,8 +262,8 @@ module HammerCLI
242
262
  output.print_record(definition, record)
243
263
  end
244
264
 
245
- def print_collection(definition, collection)
246
- output.print_collection(definition, collection)
265
+ def print_collection(definition, collection, options = {})
266
+ output.print_collection(definition, collection, options)
247
267
  end
248
268
 
249
269
  def print_message(msg, msg_params = {}, options = {})
@@ -283,8 +303,17 @@ module HammerCLI
283
303
  end
284
304
 
285
305
  def self.command_name(name=nil)
286
- @name = name if name
287
- @name || (superclass.respond_to?(:command_name) ? superclass.command_name : nil)
306
+ if @names && name
307
+ @names << name if !@names.include?(name)
308
+ else
309
+ @names = [name] if name
310
+ end
311
+ @names || (superclass.respond_to?(:command_names) ? superclass.command_names : nil)
312
+ end
313
+
314
+ def self.command_names(*names)
315
+ @names = names unless names.empty?
316
+ @names || (superclass.respond_to?(:command_names) ? superclass.command_names : nil)
288
317
  end
289
318
 
290
319
  def self.warning(message = nil)
@@ -314,6 +343,7 @@ module HammerCLI
314
343
  declared_options << option
315
344
  block ||= option.default_conversion_block
316
345
  define_accessors_for(option, &block)
346
+ completion_type_for(option, opts)
317
347
  end
318
348
  extend_options_help(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
319
349
  option
@@ -352,6 +382,33 @@ module HammerCLI
352
382
  sources
353
383
  end
354
384
 
385
+ def self.completion_map
386
+ completion = {}
387
+ # collect options
388
+ recognised_options.each do |opt|
389
+ opt.switches.each do |switch|
390
+ completion[switch] = completion_types.fetch(switch, {})
391
+ end
392
+ end
393
+ # collect subcommands recursively
394
+ recognised_subcommands.each do |cmd|
395
+ completion[cmd.names.first] = cmd.subcommand_class.completion_map
396
+ end
397
+ # collect params
398
+ completion[:params] = completion_types[:params] unless completion_types[:params].empty?
399
+ completion
400
+ end
401
+
402
+ def self.completion_types
403
+ @completion_types ||= { :params => [] }
404
+ end
405
+
406
+ def self.completion_type_for(option, opts = {})
407
+ completion_type = opts.delete(:completion)
408
+ completion_type ||= option.completion_type(opts[:format])
409
+ [option.switches].flatten(1).each { |s| completion_types[s] = completion_type }
410
+ end
411
+
355
412
  private
356
413
 
357
414
  def self.inherited_output_definition