hammer_cli 0.19.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) 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/creating_commands.md +27 -0
  6. data/doc/installation.md +47 -4
  7. data/doc/release_notes.md +17 -9
  8. data/lib/hammer_cli.rb +1 -0
  9. data/lib/hammer_cli/abstract.rb +32 -2
  10. data/lib/hammer_cli/apipie/api_connection.rb +5 -1
  11. data/lib/hammer_cli/apipie/command.rb +3 -2
  12. data/lib/hammer_cli/bash.rb +2 -0
  13. data/lib/hammer_cli/bash/completion.rb +159 -0
  14. data/lib/hammer_cli/bash/prebuild_command.rb +21 -0
  15. data/lib/hammer_cli/connection.rb +4 -0
  16. data/lib/hammer_cli/exception_handler.rb +10 -1
  17. data/lib/hammer_cli/main.rb +5 -3
  18. data/lib/hammer_cli/options/normalizers.rb +7 -3
  19. data/lib/hammer_cli/options/option_definition.rb +22 -0
  20. data/lib/hammer_cli/output/adapter/abstract.rb +1 -5
  21. data/lib/hammer_cli/output/adapter/base.rb +1 -1
  22. data/lib/hammer_cli/output/adapter/csv.rb +3 -2
  23. data/lib/hammer_cli/output/adapter/json.rb +14 -3
  24. data/lib/hammer_cli/output/adapter/silent.rb +1 -1
  25. data/lib/hammer_cli/output/adapter/table.rb +26 -7
  26. data/lib/hammer_cli/output/adapter/yaml.rb +6 -3
  27. data/lib/hammer_cli/output/output.rb +2 -4
  28. data/lib/hammer_cli/settings.rb +2 -1
  29. data/lib/hammer_cli/utils.rb +5 -0
  30. data/lib/hammer_cli/version.rb +1 -1
  31. data/locale/ca/LC_MESSAGES/hammer-cli.mo +0 -0
  32. data/locale/de/LC_MESSAGES/hammer-cli.mo +0 -0
  33. data/locale/en/LC_MESSAGES/hammer-cli.mo +0 -0
  34. data/locale/en_GB/LC_MESSAGES/hammer-cli.mo +0 -0
  35. data/locale/es/LC_MESSAGES/hammer-cli.mo +0 -0
  36. data/locale/fr/LC_MESSAGES/hammer-cli.mo +0 -0
  37. data/locale/it/LC_MESSAGES/hammer-cli.mo +0 -0
  38. data/locale/ja/LC_MESSAGES/hammer-cli.mo +0 -0
  39. data/locale/ko/LC_MESSAGES/hammer-cli.mo +0 -0
  40. data/locale/pt_BR/LC_MESSAGES/hammer-cli.mo +0 -0
  41. data/locale/ru/LC_MESSAGES/hammer-cli.mo +0 -0
  42. data/locale/zh_CN/LC_MESSAGES/hammer-cli.mo +0 -0
  43. data/locale/zh_TW/LC_MESSAGES/hammer-cli.mo +0 -0
  44. data/man/hammer.1.gz +0 -0
  45. data/test/unit/apipie/api_connection_test.rb +1 -0
  46. data/test/unit/bash_test.rb +138 -0
  47. data/test/unit/exception_handler_test.rb +44 -0
  48. data/test/unit/output/adapter/base_test.rb +58 -0
  49. data/test/unit/output/adapter/csv_test.rb +63 -1
  50. data/test/unit/output/adapter/json_test.rb +61 -0
  51. data/test/unit/output/adapter/table_test.rb +70 -1
  52. data/test/unit/output/adapter/yaml_test.rb +59 -0
  53. data/test/unit/output/output_test.rb +3 -3
  54. metadata +75 -68
  55. data/hammer_cli_complete +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da086330c730e02850d33b001aecfc8203c366f535b03c9c48c33f83811fc796
4
- data.tar.gz: 912ec4c67dbefe2cb3d6595cc6a8f8b484f12d4c004dd901442135fb4cce9103
3
+ metadata.gz: 1beb690641cf038528a35bee7964c73e60acbe8a70e94bb9d1a72af372218ea1
4
+ data.tar.gz: 516fd63e88f8df2996aefd0d3276a9ff56e1bf5f17c1a7a8b656210512f2896c
5
5
  SHA512:
6
- metadata.gz: 655cd84a3f0114b2c216157941c86f7b76dcb7c79a54efcf8c7fe66ed72cc9bc83671eaef98223894e396c29d433df76f17ad86f980a92afdd6ec70591caffc8
7
- data.tar.gz: 3a1a1826637889a8bf6ee7d8db334944daa0c30b343282b7e29edf7463d84cdc90084a696aacec19f3ca4a0dde9083fe41d4801e88449bd3493d5af6d7ec6337
6
+ metadata.gz: 8b35646b4dc9c494687b7bbd38c578ce5cf70e90080025f622b89a28f1c1d29abeafc11dbe1e16cb0786fe8ae3ac4c8673b905e8bf03cf87de86383817c653a6
7
+ data.tar.gz: 3a01e546cf71e7f015ec27d8c127328e380c3736106bc01b95d1b81593f2c7fa0a027e90a95bbf179407a953da871911e8dd0349c0ed980ed5cad613bd98363b
@@ -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
@@ -494,6 +494,33 @@ You first create an _output definition_ that you apply to your data. The result
494
494
  is a collection of fields, each having its type. The collection is then passed to an
495
495
  _output adapter_ which handles the actual formatting and printing.
496
496
 
497
+ Adapters support printing by chunks, e.g. if you want to print a large set of
498
+ data (1000+ records), but you make several calls to the server instead of one,
499
+ you may want to print received data right away instead of waiting for the rest.
500
+ This can be achieved via `:current_chunk` option for
501
+ `print_collection` and `print_data` methods. Allowed values for `:current_chunk`
502
+ are `:first`, `:another`, `:last`. By default adapters use `:single` value that
503
+ means only one record will be printed.
504
+
505
+ ##### Printing by chunks
506
+ ```ruby
507
+ # ...
508
+ def execute
509
+ loop do
510
+ # ...
511
+ data = send_request
512
+ print_data(data, current_chunk: :first)
513
+ # ...
514
+ data = send_request
515
+ print_data(data, current_chunk: :another)
516
+ # ...
517
+ data = send_request
518
+ print_data(data, current_chunk: :last)
519
+ end
520
+ end
521
+ # ...
522
+ ```
523
+
497
524
  Hammer provides a DSL for defining the output. Next rather complex example will
498
525
  explain how to use it in action.
499
526
 
data/doc/installation.md CHANGED
@@ -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.
data/doc/release_notes.md CHANGED
@@ -1,18 +1,26 @@
1
1
  Release notes
2
2
  =============
3
- ### 0.19.2 (2020-01-16)
4
- * Fixed userdata false display in image list ([PR #321](https://github.com/theforeman/hammer-cli/pull/321)), [#28134](http://projects.theforeman.org/issues/28134)
5
-
6
- ### 0.19.1 (2019-12-31)
3
+ ### 2.0.0 (2020-02-12)
4
+ * Bump version to 2.0.0
5
+ * Bump version to 2.0 ([PR #324](https://github.com/theforeman/hammer-cli/pull/324))
6
+ * Better promts for missing arguments, [#28793](http://projects.theforeman.org/issues/28793)
7
7
  * Allow column max width more than 80, [#28503](http://projects.theforeman.org/issues/28503)
8
+ * Remove computing sha, [#27728](http://projects.theforeman.org/issues/27728)
9
+ * Fixed userdata false display in image list, [#28134](http://projects.theforeman.org/issues/28134)
10
+ * Add new bash completion, [#27728](http://projects.theforeman.org/issues/27728)
11
+ * Allow adapters print page by page, [#17819](http://projects.theforeman.org/issues/17819)
12
+ * Add release documentation ([PR #317](https://github.com/theforeman/hammer-cli/pull/317)), [#28149](http://projects.theforeman.org/issues/28149)
13
+ * Fix pr links in release notes ([PR #318](https://github.com/theforeman/hammer-cli/pull/318)), [#28202](http://projects.theforeman.org/issues/28202)
8
14
  * Extract table generator into reusable component ([PR #314](https://github.com/theforeman/hammer-cli/pull/314)), [#27318](http://projects.theforeman.org/issues/27318)
15
+ * Better prompts for missing arguments ([PR #313](https://github.com/theforeman/hammer-cli/pull/313)), [#27595](http://projects.theforeman.org/issues/27595)
16
+ * Bump to 0.20-develop
9
17
 
10
18
  ### 0.19.0 (2019-10-26)
11
- * Allow schema building for custom options ([PR #316](https://github.com/Apipie/apipie-bindings/pull/316)), [#27899](http://projects.theforeman.org/issues/27899)
12
- * 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)
13
- * Added error to wrong --output ([PR #315](https://github.com/Apipie/apipie-bindings/pull/315)), [#21590](http://projects.theforeman.org/issues/21590)
14
- * Pr review checklist ([PR #305](https://github.com/Apipie/apipie-bindings/pull/305)), [#26950](http://projects.theforeman.org/issues/26950)
15
- * List items in help with customization ([PR #309](https://github.com/Apipie/apipie-bindings/pull/309)), [#27237](http://projects.theforeman.org/issues/27237)
19
+ * Allow schema building for custom options ([PR #316](https://github.com/theforeman/hammer-cli/pull/316)), [#27899](http://projects.theforeman.org/issues/27899)
20
+ * 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)
21
+ * Added error to wrong --output ([PR #315](https://github.com/theforeman/hammer-cli/pull/315)), [#21590](http://projects.theforeman.org/issues/21590)
22
+ * Pr review checklist ([PR #305](https://github.com/theforeman/hammer-cli/pull/305)), [#26950](http://projects.theforeman.org/issues/26950)
23
+ * List items in help with customization ([PR #309](https://github.com/theforeman/hammer-cli/pull/309)), [#27237](http://projects.theforeman.org/issues/27237)
16
24
 
17
25
  ### 0.18.0 (2019-08-01)
18
26
  * Unsure minimal label length ([PR #310](https://github.com/theforeman/hammer-cli/pull/310)) ([#26960](http://projects.theforeman.org/issues/26960))
data/lib/hammer_cli.rb CHANGED
@@ -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'
@@ -74,6 +74,7 @@ module HammerCLI
74
74
  begin
75
75
  begin
76
76
  exit_code = super
77
+ context.delete(:fields)
77
78
  raise "exit code must be integer" unless exit_code.is_a? Integer
78
79
  rescue => e
79
80
  exit_code = handle_exception(e)
@@ -194,6 +195,7 @@ module HammerCLI
194
195
  block ||= option.default_conversion_block
195
196
  define_accessors_for(option, &block)
196
197
  extend_options_help(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
198
+ completion_type_for(option)
197
199
  end
198
200
  end
199
201
 
@@ -242,8 +244,8 @@ module HammerCLI
242
244
  output.print_record(definition, record)
243
245
  end
244
246
 
245
- def print_collection(definition, collection)
246
- output.print_collection(definition, collection)
247
+ def print_collection(definition, collection, options = {})
248
+ output.print_collection(definition, collection, options)
247
249
  end
248
250
 
249
251
  def print_message(msg, msg_params = {}, options = {})
@@ -314,6 +316,7 @@ module HammerCLI
314
316
  declared_options << option
315
317
  block ||= option.default_conversion_block
316
318
  define_accessors_for(option, &block)
319
+ completion_type_for(option, opts)
317
320
  end
318
321
  extend_options_help(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
319
322
  option
@@ -352,6 +355,33 @@ module HammerCLI
352
355
  sources
353
356
  end
354
357
 
358
+ def self.completion_map
359
+ completion = {}
360
+ # collect options
361
+ recognised_options.each do |opt|
362
+ opt.switches.each do |switch|
363
+ completion[switch] = completion_types.fetch(switch, {})
364
+ end
365
+ end
366
+ # collect subcommands recursively
367
+ recognised_subcommands.each do |cmd|
368
+ completion[cmd.names.first] = cmd.subcommand_class.completion_map
369
+ end
370
+ # collect params
371
+ completion[:params] = completion_types[:params] unless completion_types[:params].empty?
372
+ completion
373
+ end
374
+
375
+ def self.completion_types
376
+ @completion_types ||= { :params => [] }
377
+ end
378
+
379
+ def self.completion_type_for(option, opts = {})
380
+ completion_type = opts.delete(:completion)
381
+ completion_type ||= option.completion_type(opts[:format])
382
+ [option.switches].flatten(1).each { |s| completion_types[s] = completion_type }
383
+ end
384
+
355
385
  private
356
386
 
357
387
  def self.inherited_output_definition
@@ -10,7 +10,11 @@ module HammerCLI::Apipie
10
10
  @api = ApipieBindings::API.new(params, HammerCLI::SSLOptions.new.get_options(params[:uri]))
11
11
  if options[:reload_cache]
12
12
  @api.clean_cache
13
- @logger.debug 'Apipie cache was cleared' unless @logger.nil?
13
+ HammerCLI.clear_cache
14
+ unless @logger.nil?
15
+ @logger.debug 'Apipie cache was cleared'
16
+ @logger.debug 'Completion cache was cleared'
17
+ end
14
18
  end
15
19
  end
16
20
 
@@ -65,8 +65,8 @@ module HammerCLI::Apipie
65
65
  method_options(options)
66
66
  end
67
67
 
68
- def print_data(data)
69
- print_collection(output_definition, data) unless output_definition.empty?
68
+ def print_data(data, options = {})
69
+ print_collection(output_definition, data, options) unless output_definition.empty?
70
70
  print_success_message(data) unless success_message.nil?
71
71
  end
72
72
 
@@ -86,6 +86,7 @@ module HammerCLI::Apipie
86
86
  declared_options << option
87
87
  block ||= option.default_conversion_block
88
88
  define_accessors_for(option, &block)
89
+ completion_type_for(option, opts)
89
90
  end
90
91
  extend_options_help(option) if option.value_formatter.is_a?(HammerCLI::Options::Normalizers::ListNested)
91
92
  option
@@ -0,0 +1,2 @@
1
+ require 'hammer_cli/bash/prebuild_command'
2
+ require 'hammer_cli/bash/completion'
@@ -0,0 +1,159 @@
1
+ require 'json'
2
+
3
+ module HammerCLI
4
+ module Bash
5
+ class Completion
6
+ def initialize(dict)
7
+ @dict = dict
8
+ end
9
+
10
+ def complete(line)
11
+ @complete_line = line.end_with?(' ')
12
+ full_path = line.split(' ')
13
+ complete_path = @complete_line ? full_path : full_path[0..-2]
14
+ dict, path = traverse_tree(@dict, complete_path)
15
+
16
+ return [] unless path.empty? # lost during traversing
17
+
18
+ partial = @complete_line ? '' : full_path.last
19
+ finish_word(dict, partial)
20
+ end
21
+
22
+ def self.load_description(path)
23
+ JSON.load(File.open(path))
24
+ rescue Errno::ENOENT
25
+ {}
26
+ end
27
+
28
+ private
29
+
30
+ def finish_word(dict, incomplete)
31
+ finish_option_value(dict, incomplete) ||
32
+ (finish_option_or_subcommand(dict, incomplete) + finish_param(dict, incomplete))
33
+ end
34
+
35
+ def finish_option_or_subcommand(dict, incomplete)
36
+ dict.keys.select { |k| k.is_a?(String) && k =~ /^#{incomplete}/ }.map { |k| k + ' ' }
37
+ end
38
+
39
+ def complete_value(value_description, partial, is_param)
40
+ case value_description['type']
41
+ when 'value'
42
+ if !partial.empty?
43
+ []
44
+ elsif is_param
45
+ ['--->', 'Add parameter']
46
+ else
47
+ ['--->', 'Add option <value>']
48
+ end
49
+ when 'directory'
50
+ directories(partial)
51
+ when 'file'
52
+ files(partial, value_description)
53
+ when 'enum'
54
+ enum(partial, value_description['values'])
55
+ when 'multienum'
56
+ multienum(partial, value_description['values'])
57
+ when 'schema'
58
+ schema(value_description['schema'])
59
+ when 'list'
60
+ ['--->', 'Add comma-separated list of values']
61
+ when 'key_value_list'
62
+ ['--->', 'Add comma-separated list of key=value']
63
+ end
64
+ end
65
+
66
+ def finish_param(dict, incomplete)
67
+ if dict['params'] && !dict['params'].empty?
68
+ complete_value(dict['params'].first, incomplete, true)
69
+ else
70
+ []
71
+ end
72
+ end
73
+
74
+ def finish_option_value(dict, incomplete)
75
+ complete_value(dict, incomplete, false) if dict.key?('type')
76
+ end
77
+
78
+ def traverse_tree(dict, path)
79
+ return [dict, []] if path.nil? || path.empty?
80
+ result = if dict.key?(path.first)
81
+ if path.first.start_with?('-')
82
+ parse_option(dict, path)
83
+ else
84
+ parse_subcommand(dict, path)
85
+ end
86
+ elsif dict['params']
87
+ # traverse params one by one
88
+ parse_params(dict, path)
89
+ else
90
+ # not found
91
+ [{}, path]
92
+ end
93
+ result
94
+ end
95
+
96
+ def parse_params(dict, path)
97
+ traverse_tree({ 'params' => dict['params'][1..-1] }, path[1..-1])
98
+ end
99
+
100
+ def parse_subcommand(dict, path)
101
+ traverse_tree(dict[path.first], path[1..-1])
102
+ end
103
+
104
+ def parse_option(dict, path)
105
+ if dict[path.first]['type'] == 'flag' # flag
106
+ traverse_tree(dict, path[1..-1])
107
+ elsif path.length >= 2 # option with value
108
+ traverse_tree(dict, path[2..-1])
109
+ else # option with value missing
110
+ [dict[path.first], path[1..-1]]
111
+ end
112
+ end
113
+
114
+ def directories(partial = '')
115
+ dirs = []
116
+ dirs += Dir.glob("#{partial}*").select { |f| File.directory?(f) }
117
+ dirs = dirs.map { |d| d + '/' } if dirs.length == 1
118
+ dirs
119
+ end
120
+
121
+ def files(partial = '', opts = {})
122
+ filter = opts.fetch('filter', '.*')
123
+ file_names = []
124
+ file_names += Dir.glob("#{partial}*").select do |f|
125
+ File.directory?(f) || f =~ /#{filter}/
126
+ end
127
+ file_names.map { |f| File.directory?(f) ? f + '/' : f + ' ' }
128
+ end
129
+
130
+ def enum(partial = '', values = [])
131
+ values.select { |v| v.start_with?(partial) }.map { |v| v + ' ' }
132
+ end
133
+
134
+ def multienum(partial = '', values = [])
135
+ return values if partial.empty?
136
+
137
+ parts = partial.split(',')
138
+ resolved = []
139
+ to_complete = parts.each_with_object([]) do |part, res|
140
+ next resolved << part if values.include?(part)
141
+
142
+ res << part
143
+ end
144
+
145
+ hints = to_complete.map do |p|
146
+ values.select { |v| v.start_with?(p) }
147
+ end.flatten(1).uniq
148
+ return values - parts if hints.empty?
149
+ return [(resolved + hints).join(',')] if hints.size == 1
150
+
151
+ hints
152
+ end
153
+
154
+ def schema(template = '')
155
+ ['--->', "Add value by following schema: #{template}"]
156
+ end
157
+ end
158
+ end
159
+ end