hammer_cli 0.19.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/bin/hammer-complete +28 -0
 - data/config/cli_config.template.yml +2 -0
 - data/config/hammer.completion +5 -0
 - data/doc/commands_extension.md +12 -0
 - data/doc/creating_commands.md +100 -0
 - data/doc/installation.md +47 -4
 - data/doc/installation_rpm.md +2 -2
 - data/doc/release_notes.md +31 -6
 - data/lib/hammer_cli.rb +1 -0
 - data/lib/hammer_cli/abstract.rb +61 -4
 - data/lib/hammer_cli/apipie/api_connection.rb +5 -1
 - data/lib/hammer_cli/apipie/command.rb +3 -2
 - data/lib/hammer_cli/apipie/option_builder.rb +15 -13
 - data/lib/hammer_cli/apipie/option_definition.rb +9 -7
 - data/lib/hammer_cli/bash.rb +2 -0
 - data/lib/hammer_cli/bash/completion.rb +159 -0
 - data/lib/hammer_cli/bash/prebuild_command.rb +21 -0
 - data/lib/hammer_cli/command_extensions.rb +21 -1
 - data/lib/hammer_cli/connection.rb +4 -0
 - data/lib/hammer_cli/exception_handler.rb +11 -2
 - data/lib/hammer_cli/full_help.rb +8 -1
 - data/lib/hammer_cli/help/builder.rb +29 -3
 - data/lib/hammer_cli/logger_watch.rb +1 -1
 - data/lib/hammer_cli/main.rb +5 -3
 - data/lib/hammer_cli/options/normalizers.rb +7 -3
 - data/lib/hammer_cli/options/option_definition.rb +26 -6
 - data/lib/hammer_cli/options/option_family.rb +114 -0
 - data/lib/hammer_cli/options/predefined.rb +1 -1
 - data/lib/hammer_cli/output/adapter/abstract.rb +1 -5
 - data/lib/hammer_cli/output/adapter/base.rb +1 -1
 - data/lib/hammer_cli/output/adapter/csv.rb +3 -2
 - data/lib/hammer_cli/output/adapter/json.rb +14 -3
 - data/lib/hammer_cli/output/adapter/silent.rb +1 -1
 - data/lib/hammer_cli/output/adapter/table.rb +27 -8
 - data/lib/hammer_cli/output/adapter/yaml.rb +6 -3
 - data/lib/hammer_cli/output/output.rb +2 -4
 - data/lib/hammer_cli/settings.rb +2 -1
 - data/lib/hammer_cli/subcommand.rb +25 -1
 - data/lib/hammer_cli/testing/command_assertions.rb +2 -2
 - data/lib/hammer_cli/utils.rb +22 -0
 - 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 +23 -2
 - data/test/unit/apipie/api_connection_test.rb +1 -0
 - data/test/unit/apipie/option_builder_test.rb +8 -0
 - data/test/unit/bash_test.rb +138 -0
 - data/test/unit/command_extensions_test.rb +67 -49
 - data/test/unit/exception_handler_test.rb +44 -0
 - data/test/unit/help/builder_test.rb +22 -0
 - data/test/unit/options/option_family_test.rb +48 -0
 - data/test/unit/output/adapter/base_test.rb +58 -0
 - data/test/unit/output/adapter/csv_test.rb +63 -1
 - data/test/unit/output/adapter/json_test.rb +61 -0
 - data/test/unit/output/adapter/table_test.rb +70 -1
 - data/test/unit/output/adapter/yaml_test.rb +59 -0
 - data/test/unit/output/output_test.rb +3 -3
 - metadata +17 -6
 - data/hammer_cli_complete +0 -13
 
| 
         @@ -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 
     | 
    
         
            -
                     
     | 
| 
      
 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
         
     | 
| 
         @@ -41,12 +41,11 @@ module HammerCLI::Apipie 
     | 
|
| 
       41 
41 
     | 
    
         
             
                end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
                def create_option(param, resource_name_map)
         
     | 
| 
       44 
     | 
    
         
            -
                   
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                  )
         
     | 
| 
      
 44 
     | 
    
         
            +
                  family = HammerCLI::Options::OptionFamily.new
         
     | 
| 
      
 45 
     | 
    
         
            +
                  family.parent(option_switch(param, resource_name_map),
         
     | 
| 
      
 46 
     | 
    
         
            +
                                option_type(param, resource_name_map),
         
     | 
| 
      
 47 
     | 
    
         
            +
                                option_desc(param),
         
     | 
| 
      
 48 
     | 
    
         
            +
                                option_opts(param, resource_name_map))
         
     | 
| 
       50 
49 
     | 
    
         
             
                end
         
     | 
| 
       51 
50 
     | 
    
         | 
| 
       52 
51 
     | 
    
         
             
                def option_switch(param, resource_name_map)
         
     | 
| 
         @@ -61,7 +60,7 @@ module HammerCLI::Apipie 
     | 
|
| 
       61 
60 
     | 
    
         
             
                  param.description || " "
         
     | 
| 
       62 
61 
     | 
    
         
             
                end
         
     | 
| 
       63 
62 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
                def option_opts(param)
         
     | 
| 
      
 63 
     | 
    
         
            +
                def option_opts(param, resource_name_map)
         
     | 
| 
       65 
64 
     | 
    
         
             
                  opts = {}
         
     | 
| 
       66 
65 
     | 
    
         
             
                  opts[:required] = true if (param.required? and require_options?)
         
     | 
| 
       67 
66 
     | 
    
         
             
                  if param.expected_type.to_s == 'array'
         
     | 
| 
         @@ -80,19 +79,22 @@ module HammerCLI::Apipie 
     | 
|
| 
       80 
79 
     | 
    
         
             
                  end
         
     | 
| 
       81 
80 
     | 
    
         
             
                  opts[:attribute_name] = HammerCLI.option_accessor_name(param.name)
         
     | 
| 
       82 
81 
     | 
    
         
             
                  opts[:referenced_resource] = resource_name(param)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  opts[:aliased_resource] = aliased_name(resource_name(param), resource_name_map)
         
     | 
| 
       83 
83 
     | 
    
         | 
| 
       84 
84 
     | 
    
         
             
                  return opts
         
     | 
| 
       85 
85 
     | 
    
         
             
                end
         
     | 
| 
       86 
86 
     | 
    
         | 
| 
      
 87 
     | 
    
         
            +
                def aliased_name(name, resource_name_map)
         
     | 
| 
      
 88 
     | 
    
         
            +
                  return if name.nil?
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  resource_name_map[name.to_s] || resource_name_map[name.to_sym] || name
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
       87 
93 
     | 
    
         
             
                def aliased(param, resource_name_map)
         
     | 
| 
       88 
94 
     | 
    
         
             
                  resource_name = resource_name(param)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  return param.name if resource_name.nil?
         
     | 
| 
       89 
96 
     | 
    
         | 
| 
       90 
     | 
    
         
            -
                   
     | 
| 
       91 
     | 
    
         
            -
                    return param.name
         
     | 
| 
       92 
     | 
    
         
            -
                  else
         
     | 
| 
       93 
     | 
    
         
            -
                    aliased_name = resource_name_map[resource_name.to_s] || resource_name_map[resource_name.to_sym] || resource_name
         
     | 
| 
       94 
     | 
    
         
            -
                    return param.name.gsub(resource_name, aliased_name.to_s)
         
     | 
| 
       95 
     | 
    
         
            -
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                  param.name.gsub(resource_name, aliased_name(resource_name, resource_name_map).to_s)
         
     | 
| 
       96 
98 
     | 
    
         
             
                end
         
     | 
| 
       97 
99 
     | 
    
         | 
| 
       98 
100 
     | 
    
         
             
                def resource_name(param)
         
     | 
| 
         @@ -1,21 +1,23 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require File.join(File.dirname(__FILE__), 'options')
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module HammerCLI::Apipie
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
4 
     | 
    
         
             
              class OptionDefinition < HammerCLI::Options::OptionDefinition
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
                attr_accessor :referenced_resource
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :referenced_resource, :aliased_resource, :family
         
     | 
| 
       8 
6 
     | 
    
         | 
| 
       9 
7 
     | 
    
         
             
                def initialize(switches, type, description, options = {})
         
     | 
| 
       10 
     | 
    
         
            -
                   
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
                   
     | 
| 
      
 8 
     | 
    
         
            +
                  @referenced_resource = options[:referenced_resource].to_s if options[:referenced_resource]
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @aliased_resource = options[:aliased_resource].to_s if options[:aliased_resource]
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @family = options[:family]
         
     | 
| 
       13 
11 
     | 
    
         
             
                  super
         
     | 
| 
       14 
12 
     | 
    
         
             
                  # Apipie currently sends descriptions as escaped HTML once this is changed this should be removed.
         
     | 
| 
       15 
13 
     | 
    
         
             
                  # See #15198 on Redmine.
         
     | 
| 
       16 
14 
     | 
    
         
             
                  @description = CGI::unescapeHTML(description)
         
     | 
| 
       17 
15 
     | 
    
         
             
                end
         
     | 
| 
       18 
16 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
                def child?
         
     | 
| 
      
 18 
     | 
    
         
            +
                  return unless @family
         
     | 
| 
       20 
19 
     | 
    
         | 
| 
      
 20 
     | 
    
         
            +
                  @family.children.include?(self)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
       21 
23 
     | 
    
         
             
            end
         
     | 
| 
         @@ -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
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module HammerCLI
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Bash
         
     | 
| 
      
 3 
     | 
    
         
            +
                class PrebuildCompletionCommand < HammerCLI::AbstractCommand
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def execute
         
     | 
| 
      
 5 
     | 
    
         
            +
                    map = HammerCLI::MainCommand.completion_map
         
     | 
| 
      
 6 
     | 
    
         
            +
                    cache_file = HammerCLI::Settings.get(:completion_cache_file)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    cache_dir = File.dirname(cache_file)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    FileUtils.mkdir_p(cache_dir) unless File.directory?(cache_dir)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    File.write(File.expand_path(cache_file), map.to_json)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    HammerCLI::EX_OK
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              HammerCLI::MainCommand.subcommand(
         
     | 
| 
      
 17 
     | 
    
         
            +
                'prebuild-bash-completion',
         
     | 
| 
      
 18 
     | 
    
         
            +
                _('Prepare map of options and subcommands for Bash completion'),
         
     | 
| 
      
 19 
     | 
    
         
            +
                HammerCLI::Bash::PrebuildCompletionCommand
         
     | 
| 
      
 20 
     | 
    
         
            +
              )
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -15,7 +15,7 @@ module HammerCLI 
     | 
|
| 
       15 
15 
     | 
    
         
             
                ALLOWED_EXTENSIONS = %i[
         
     | 
| 
       16 
16 
     | 
    
         
             
                  option command_options before_print data output help request
         
     | 
| 
       17 
17 
     | 
    
         
             
                  request_headers headers request_options options request_params params
         
     | 
| 
       18 
     | 
    
         
            -
                  option_sources predefined_options use_option
         
     | 
| 
      
 18 
     | 
    
         
            +
                  option_sources predefined_options use_option option_family
         
     | 
| 
       19 
19 
     | 
    
         
             
                ].freeze
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                def initialize(options = {})
         
     | 
| 
         @@ -86,6 +86,11 @@ module HammerCLI 
     | 
|
| 
       86 
86 
     | 
    
         
             
                  @option_sources_block = block
         
     | 
| 
       87 
87 
     | 
    
         
             
                end
         
     | 
| 
       88 
88 
     | 
    
         | 
| 
      
 89 
     | 
    
         
            +
                def self.option_family(options = {}, &block)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @option_family_opts = options
         
     | 
| 
      
 91 
     | 
    
         
            +
                  @option_family_block = block
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
       89 
94 
     | 
    
         
             
                # Object
         
     | 
| 
       90 
95 
     | 
    
         | 
| 
       91 
96 
     | 
    
         
             
                def extend_options(command_class)
         
     | 
| 
         @@ -151,6 +156,13 @@ module HammerCLI 
     | 
|
| 
       151 
156 
     | 
    
         
             
                  self.class.extend_option_sources(sources, command)
         
     | 
| 
       152 
157 
     | 
    
         
             
                end
         
     | 
| 
       153 
158 
     | 
    
         | 
| 
      
 159 
     | 
    
         
            +
                def extend_option_family(command_class)
         
     | 
| 
      
 160 
     | 
    
         
            +
                  allowed = @only & %i[option_family]
         
     | 
| 
      
 161 
     | 
    
         
            +
                  return if allowed.empty? || (allowed & @except).any?
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                  self.class.extend_option_family(command_class)
         
     | 
| 
      
 164 
     | 
    
         
            +
                end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
       154 
166 
     | 
    
         
             
                def delegatee(command_class)
         
     | 
| 
       155 
167 
     | 
    
         
             
                  self.class.delegatee = command_class
         
     | 
| 
       156 
168 
     | 
    
         
             
                end
         
     | 
| 
         @@ -234,5 +246,13 @@ module HammerCLI 
     | 
|
| 
       234 
246 
     | 
    
         
             
                  @option_sources_block.call(sources, command)
         
     | 
| 
       235 
247 
     | 
    
         
             
                  logger.debug("Called block for #{@delegatee} option sources:\n\t#{@option_sources_block}")
         
     | 
| 
       236 
248 
     | 
    
         
             
                end
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
                def self.extend_option_family(command_class)
         
     | 
| 
      
 251 
     | 
    
         
            +
                  return if @option_family_block.nil?
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                  @option_family_opts[:creator] = command_class
         
     | 
| 
      
 254 
     | 
    
         
            +
                  command_class.send(:option_family, @option_family_opts, &@option_family_block)
         
     | 
| 
      
 255 
     | 
    
         
            +
                  logger.debug("Called option family block for #{command_class}:\n\t#{@option_family_block}")
         
     | 
| 
      
 256 
     | 
    
         
            +
                end
         
     | 
| 
       237 
257 
     | 
    
         
             
              end
         
     | 
| 
       238 
258 
     | 
    
         
             
            end
         
     | 
| 
         @@ -69,7 +69,7 @@ module HammerCLI 
     | 
|
| 
       69 
69 
     | 
    
         | 
| 
       70 
70 
     | 
    
         
             
                def handle_usage_exception(e)
         
     | 
| 
       71 
71 
     | 
    
         
             
                  print_error (_("Error: %{message}") + "\n\n" +
         
     | 
| 
       72 
     | 
    
         
            -
                               _("See: '%{path} --help'.")) % {: 
     | 
| 
      
 72 
     | 
    
         
            +
                               _("See: '%{path} --help'.")) % { message: e.message, path: HammerCLI.expand_invocation_path(e.command.invocation_path) }
         
     | 
| 
       73 
73 
     | 
    
         
             
                  log_full_error e
         
     | 
| 
       74 
74 
     | 
    
         
             
                  HammerCLI::EX_USAGE
         
     | 
| 
       75 
75 
     | 
    
         
             
                end
         
     | 
| 
         @@ -138,7 +138,16 @@ module HammerCLI 
     | 
|
| 
       138 
138 
     | 
    
         
             
                end
         
     | 
| 
       139 
139 
     | 
    
         | 
| 
       140 
140 
     | 
    
         
             
                def handle_apipie_missing_arguments_error(e)
         
     | 
| 
       141 
     | 
    
         
            -
                   
     | 
| 
      
 141 
     | 
    
         
            +
                  params = e.params.map do |p|
         
     | 
| 
      
 142 
     | 
    
         
            +
                    param = p[/\[.+\]/]
         
     | 
| 
      
 143 
     | 
    
         
            +
                    param = if param.nil?
         
     | 
| 
      
 144 
     | 
    
         
            +
                              p.tr('_', '-')
         
     | 
| 
      
 145 
     | 
    
         
            +
                            else
         
     | 
| 
      
 146 
     | 
    
         
            +
                              p.scan(/\[[^\[\]]+\]/).first[1...-1].tr('_', '-')
         
     | 
| 
      
 147 
     | 
    
         
            +
                            end
         
     | 
| 
      
 148 
     | 
    
         
            +
                    "--#{param}"
         
     | 
| 
      
 149 
     | 
    
         
            +
                  end
         
     | 
| 
      
 150 
     | 
    
         
            +
                  message = _("Missing arguments for %s.") % "'#{params.uniq.join("', '")}'"
         
     | 
| 
       142 
151 
     | 
    
         
             
                  print_error message
         
     | 
| 
       143 
152 
     | 
    
         
             
                  log_full_error e, message
         
     | 
| 
       144 
153 
     | 
    
         
             
                  HammerCLI::EX_USAGE
         
     | 
    
        data/lib/hammer_cli/full_help.rb
    CHANGED
    
    | 
         @@ -6,8 +6,11 @@ module HammerCLI 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
                def execute
         
     | 
| 
       8 
8 
     | 
    
         
             
                  @adapter = option_md? ? MDAdapter.new : TxtAdapter.new
         
     | 
| 
      
 9 
     | 
    
         
            +
                  HammerCLI.context[:full_help] = true
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @invocation_paths = {}
         
     | 
| 
       9 
11 
     | 
    
         
             
                  print_heading
         
     | 
| 
       10 
12 
     | 
    
         
             
                  print_help
         
     | 
| 
      
 13 
     | 
    
         
            +
                  HammerCLI.context[:full_help] = false
         
     | 
| 
       11 
14 
     | 
    
         
             
                  HammerCLI::EX_OK
         
     | 
| 
       12 
15 
     | 
    
         
             
                end
         
     | 
| 
       13 
16 
     | 
    
         | 
| 
         @@ -19,9 +22,13 @@ module HammerCLI 
     | 
|
| 
       19 
22 
     | 
    
         
             
                end
         
     | 
| 
       20 
23 
     | 
    
         | 
| 
       21 
24 
     | 
    
         
             
                def print_help(name='hammer', command=HammerCLI::MainCommand, desc='')
         
     | 
| 
       22 
     | 
    
         
            -
                  @ 
     | 
| 
      
 25 
     | 
    
         
            +
                  @invocation_paths[name] ||= []
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @adapter.print_command(name, desc, command.new(name, path: @invocation_paths[name]).help)
         
     | 
| 
       23 
27 
     | 
    
         | 
| 
       24 
28 
     | 
    
         
             
                  command.recognised_subcommands.each do |sub_cmd|
         
     | 
| 
      
 29 
     | 
    
         
            +
                    path = "#{name} #{sub_cmd.names.first}"
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @invocation_paths[path] ||= []
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @invocation_paths[path] += @invocation_paths[name]
         
     | 
| 
       25 
32 
     | 
    
         
             
                    print_help(@adapter.command_name(name, sub_cmd.names.first), sub_cmd.subcommand_class, sub_cmd.description)
         
     | 
| 
       26 
33 
     | 
    
         
             
                  end
         
     | 
| 
       27 
34 
     | 
    
         
             
                end
         
     | 
| 
         @@ -14,7 +14,7 @@ module HammerCLI 
     | 
|
| 
       14 
14 
     | 
    
         
             
                  def add_usage(invocation_path, usage_descriptions)
         
     | 
| 
       15 
15 
     | 
    
         
             
                    heading(Clamp.message(:usage_heading))
         
     | 
| 
       16 
16 
     | 
    
         
             
                    usage_descriptions.each do |usage|
         
     | 
| 
       17 
     | 
    
         
            -
                      puts "    #{invocation_path} #{usage}".rstrip
         
     | 
| 
      
 17 
     | 
    
         
            +
                      puts "    #{HammerCLI.expand_invocation_path(invocation_path)} #{usage}".rstrip
         
     | 
| 
       18 
18 
     | 
    
         
             
                    end
         
     | 
| 
       19 
19 
     | 
    
         
             
                  end
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
         @@ -29,12 +29,19 @@ module HammerCLI 
     | 
|
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                    label_width = DEFAULT_LABEL_INDENT
         
     | 
| 
       31 
31 
     | 
    
         
             
                    items.each do |item|
         
     | 
| 
       32 
     | 
    
         
            -
                      label 
     | 
| 
      
 32 
     | 
    
         
            +
                      label = item.help.first
         
     | 
| 
       33 
33 
     | 
    
         
             
                      label_width = label.size if label.size > label_width
         
     | 
| 
       34 
34 
     | 
    
         
             
                    end
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
36 
     | 
    
         
             
                    items.each do |item|
         
     | 
| 
       37 
     | 
    
         
            -
                       
     | 
| 
      
 37 
     | 
    
         
            +
                      if item.respond_to?(:child?) && item.child?
         
     | 
| 
      
 38 
     | 
    
         
            +
                        next unless HammerCLI.context[:full_help]
         
     | 
| 
      
 39 
     | 
    
         
            +
                      end
         
     | 
| 
      
 40 
     | 
    
         
            +
                      label, description = if !HammerCLI.context[:full_help] && item.respond_to?(:family) && item.family
         
     | 
| 
      
 41 
     | 
    
         
            +
                        [item.family.switch, item.family.description || item.help[1]]
         
     | 
| 
      
 42 
     | 
    
         
            +
                      else
         
     | 
| 
      
 43 
     | 
    
         
            +
                        item.help
         
     | 
| 
      
 44 
     | 
    
         
            +
                      end
         
     | 
| 
       38 
45 
     | 
    
         
             
                      description.gsub(/^(.)/) { Unicode::capitalize($1) }.each_line do |line|
         
     | 
| 
       39 
46 
     | 
    
         
             
                        puts " %-#{label_width}s %s" % [label, line]
         
     | 
| 
       40 
47 
     | 
    
         
             
                        label = ''
         
     | 
| 
         @@ -54,6 +61,25 @@ module HammerCLI 
     | 
|
| 
       54 
61 
     | 
    
         
             
                    label = HighLine.color(label, :bold) if @richtext
         
     | 
| 
       55 
62 
     | 
    
         
             
                    puts label
         
     | 
| 
       56 
63 
     | 
    
         
             
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  private
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  def expand_invocation_path(path)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    bits = path.split(' ')
         
     | 
| 
      
 69 
     | 
    
         
            +
                    parent_command = HammerCLI::MainCommand
         
     | 
| 
      
 70 
     | 
    
         
            +
                    new_path = (bits[1..-1] || []).each_with_object([]) do |bit, names|
         
     | 
| 
      
 71 
     | 
    
         
            +
                      subcommand = parent_command.find_subcommand(bit)
         
     | 
| 
      
 72 
     | 
    
         
            +
                      next if subcommand.nil?
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                      names << if subcommand.names.size > 1
         
     | 
| 
      
 75 
     | 
    
         
            +
                                 "<#{subcommand.names.join('|')}>"
         
     | 
| 
      
 76 
     | 
    
         
            +
                               else
         
     | 
| 
      
 77 
     | 
    
         
            +
                                 subcommand.names.first
         
     | 
| 
      
 78 
     | 
    
         
            +
                               end
         
     | 
| 
      
 79 
     | 
    
         
            +
                      parent_command = subcommand.subcommand_class
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
                    new_path.unshift(bits.first).join(' ')
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
       57 
83 
     | 
    
         
             
                end
         
     | 
| 
       58 
84 
     | 
    
         
             
              end
         
     | 
| 
       59 
85 
     | 
    
         
             
            end
         
     |