hammer_cli 0.15.1 → 0.16.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/doc/commands_modification.md +82 -0
  3. data/doc/creating_commands.md +78 -38
  4. data/doc/developer_docs.md +2 -0
  5. data/doc/help_modification.md +119 -0
  6. data/doc/i18n.md +2 -2
  7. data/doc/installation_rpm.md +3 -3
  8. data/doc/release_notes.md +9 -1
  9. data/lib/hammer_cli.rb +0 -1
  10. data/lib/hammer_cli/abstract.rb +44 -13
  11. data/lib/hammer_cli/exception_handler.rb +1 -1
  12. data/lib/hammer_cli/help/definition.rb +55 -0
  13. data/lib/hammer_cli/help/definition/abstract_item.rb +38 -0
  14. data/lib/hammer_cli/help/definition/list.rb +53 -0
  15. data/lib/hammer_cli/help/definition/section.rb +36 -0
  16. data/lib/hammer_cli/help/definition/text.rb +21 -0
  17. data/lib/hammer_cli/help/text_builder.rb +26 -49
  18. data/lib/hammer_cli/i18n.rb +0 -1
  19. data/lib/hammer_cli/options/option_collector.rb +12 -9
  20. data/lib/hammer_cli/options/option_processor.rb +17 -0
  21. data/lib/hammer_cli/options/processor_list.rb +37 -0
  22. data/lib/hammer_cli/options/sources/base.rb +17 -0
  23. data/lib/hammer_cli/options/sources/command_line.rb +3 -1
  24. data/lib/hammer_cli/options/sources/saved_defaults.rb +3 -1
  25. data/lib/hammer_cli/options/validators/base.rb +20 -0
  26. data/lib/hammer_cli/options/validators/dsl.rb +160 -0
  27. data/lib/hammer_cli/options/validators/dsl_block_validator.rb +19 -0
  28. data/lib/hammer_cli/output/adapter/csv.rb +11 -11
  29. data/lib/hammer_cli/output/adapter/tree_structure.rb +5 -3
  30. data/lib/hammer_cli/output/definition.rb +42 -4
  31. data/lib/hammer_cli/output/dsl.rb +4 -2
  32. data/lib/hammer_cli/output/fields.rb +9 -2
  33. data/lib/hammer_cli/testing/messages.rb +6 -6
  34. data/lib/hammer_cli/utils.rb +15 -0
  35. data/lib/hammer_cli/version.rb +1 -1
  36. data/locale/ca/LC_MESSAGES/hammer-cli.mo +0 -0
  37. data/locale/de/LC_MESSAGES/hammer-cli.mo +0 -0
  38. data/locale/en/LC_MESSAGES/hammer-cli.mo +0 -0
  39. data/locale/en_GB/LC_MESSAGES/hammer-cli.mo +0 -0
  40. data/locale/es/LC_MESSAGES/hammer-cli.mo +0 -0
  41. data/locale/fr/LC_MESSAGES/hammer-cli.mo +0 -0
  42. data/locale/it/LC_MESSAGES/hammer-cli.mo +0 -0
  43. data/locale/ja/LC_MESSAGES/hammer-cli.mo +0 -0
  44. data/locale/ko/LC_MESSAGES/hammer-cli.mo +0 -0
  45. data/locale/pt_BR/LC_MESSAGES/hammer-cli.mo +0 -0
  46. data/locale/ru/LC_MESSAGES/hammer-cli.mo +0 -0
  47. data/locale/zh_CN/LC_MESSAGES/hammer-cli.mo +0 -0
  48. data/locale/zh_TW/LC_MESSAGES/hammer-cli.mo +0 -0
  49. data/test/unit/abstract_test.rb +70 -5
  50. data/test/unit/exception_handler_test.rb +1 -1
  51. data/test/unit/help/definition/abstract_item_test.rb +33 -0
  52. data/test/unit/help/definition/list_test.rb +17 -0
  53. data/test/unit/help/definition/section_test.rb +25 -0
  54. data/test/unit/help/definition/text_test.rb +11 -0
  55. data/test/unit/help/definition_test.rb +236 -0
  56. data/test/unit/help/text_builder_test.rb +170 -1
  57. data/test/unit/options/option_collector_test.rb +18 -9
  58. data/test/unit/options/processor_list_test.rb +70 -0
  59. data/test/unit/{validator_test.rb → options/validators/dsl_test.rb} +57 -93
  60. data/test/unit/output/adapter/abstract_test.rb +3 -0
  61. data/test/unit/output/adapter/base_test.rb +5 -0
  62. data/test/unit/output/adapter/csv_test.rb +3 -0
  63. data/test/unit/output/adapter/json_test.rb +12 -0
  64. data/test/unit/output/adapter/table_test.rb +5 -0
  65. data/test/unit/output/adapter/yaml_test.rb +11 -0
  66. data/test/unit/output/definition_test.rb +221 -1
  67. data/test/unit/utils_test.rb +23 -0
  68. metadata +31 -5
  69. data/lib/hammer_cli/validator.rb +0 -172
@@ -50,7 +50,7 @@ module HammerCLI
50
50
  end
51
51
 
52
52
  def print_message(msg, options = {})
53
- output.print_message(msg, nil, options)
53
+ output.print_message(msg, {}, options)
54
54
  end
55
55
 
56
56
  def log_full_error(e, message = e.message)
@@ -0,0 +1,55 @@
1
+ require_relative 'definition/abstract_item'
2
+ require_relative 'definition/text'
3
+ require_relative 'definition/list'
4
+ require_relative 'definition/section'
5
+
6
+ module HammerCLI
7
+ module Help
8
+ class Definition < Array
9
+ def build_string
10
+ @out = StringIO.new
11
+ each do |item|
12
+ next unless item.is_a? AbstractItem
13
+ @out.puts unless first_print?
14
+ @out.puts item.build_string
15
+ end
16
+ @out.string
17
+ end
18
+
19
+ def find_item(item_id)
20
+ self[item_index(item_id)]
21
+ end
22
+
23
+ def at(path)
24
+ return super(path) if path.is_a? Integer
25
+ return self if path.empty?
26
+ expand_path(path)
27
+ end
28
+
29
+ def insert_definition(mode, item_id, definition)
30
+ HammerCLI.insert_relative(self, mode, item_index(item_id), *definition)
31
+ end
32
+
33
+ private
34
+
35
+ def expand_path(path)
36
+ path = [path] unless path.is_a? Array
37
+ item = find_item(path[0])
38
+ return item if path[1..-1].empty?
39
+ item.definition.at(path[1..-1])
40
+ end
41
+
42
+ def first_print?
43
+ @out.size.zero?
44
+ end
45
+
46
+ def item_index(item_id)
47
+ index = find_index do |item|
48
+ item.id == item_id
49
+ end
50
+ raise ArgumentError, "Help item '#{item_id}' not found" if index.nil?
51
+ index
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,38 @@
1
+ module HammerCLI
2
+ module Help
3
+ class AbstractItem
4
+ INDENT_STEP = 2
5
+
6
+ attr_reader :id
7
+ attr_accessor :definition
8
+
9
+ def initialize(options = {})
10
+ @id = options[:id]
11
+ @indentation = options[:indentation]
12
+ end
13
+
14
+ def build_string
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def self.indent(content, indentation = nil)
19
+ indentation ||= ' ' * INDENT_STEP
20
+ content = content.split("\n") unless content.is_a? Array
21
+ content.map do |line|
22
+ (indentation + line).rstrip
23
+ end.join("\n")
24
+ end
25
+
26
+ protected
27
+
28
+ def build_definition(content)
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def indent(content, indentation = nil)
33
+ indentation ||= @indentation
34
+ self.class.indent(content, indentation)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,53 @@
1
+ module HammerCLI
2
+ module Help
3
+ class List < AbstractItem
4
+ LIST_INDENT = 20
5
+
6
+ def initialize(items, options = {})
7
+ super(options)
8
+ @indent_size = options[:indent_size] || indent_size(items)
9
+ build_definition(items || [])
10
+ end
11
+
12
+ def build_string
13
+ out = StringIO.new
14
+ @definition.each do |item|
15
+ out.puts item.build_string
16
+ end
17
+ out.string
18
+ end
19
+
20
+ protected
21
+
22
+ def build_definition(items)
23
+ @definition = Definition.new
24
+ items.each do |item|
25
+ @definition << Text.new(format_item(item))
26
+ end
27
+ @definition
28
+ end
29
+
30
+ private
31
+
32
+ def indent_size(items)
33
+ items = normalize(items)
34
+ max_len = items.map { |i| i[0].to_s.length }.max
35
+ (max_len + INDENT_STEP > LIST_INDENT) ? (max_len + INDENT_STEP) : LIST_INDENT
36
+ end
37
+
38
+ def format_item(item)
39
+ col1, col2 = item
40
+ col2 = indent(col2.to_s, ' ' * @indent_size).lstrip
41
+ line = "%-#{@indent_size}s%s" % [col1, col2]
42
+ line.strip!
43
+ line
44
+ end
45
+
46
+ def normalize(items)
47
+ items.map do |i|
48
+ i.is_a?(Array) ? i : [i]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,36 @@
1
+ module HammerCLI
2
+ module Help
3
+ class Section < AbstractItem
4
+ attr_reader :label
5
+
6
+ def initialize(label, definition = nil, options = {})
7
+ super(options)
8
+ @label = label
9
+ @richtext = options[:richtext] || false
10
+ @id ||= label
11
+ build_definition(definition)
12
+ end
13
+
14
+ def build_string
15
+ out = StringIO.new
16
+ out.puts heading
17
+ out.puts indent(@definition.build_string)
18
+ out.string
19
+ end
20
+
21
+ protected
22
+
23
+ def build_definition(content)
24
+ @definition = content || Definition.new
25
+ end
26
+
27
+ private
28
+
29
+ def heading
30
+ label = "#{@label}:"
31
+ label = HighLine.color(label, :bold) if @richtext
32
+ label
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ module HammerCLI
2
+ module Help
3
+ class Text < AbstractItem
4
+ def initialize(text = nil, options = {})
5
+ super(options)
6
+ build_definition(text)
7
+ end
8
+
9
+ def build_string
10
+ @text
11
+ end
12
+
13
+ protected
14
+
15
+ def build_definition(content)
16
+ @text = content || ''
17
+ @definition = Definition.new([self])
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,78 +1,55 @@
1
+ require 'hammer_cli/help/definition'
2
+
1
3
  module HammerCLI
2
4
  module Help
3
5
  class TextBuilder
4
- INDENT_STEP = 2
5
- LIST_INDENT = 20
6
+ attr_accessor :definition
6
7
 
7
8
  def initialize(richtext = false)
8
- @out = StringIO.new
9
9
  @richtext = richtext
10
+ @definition = HammerCLI::Help::Definition.new
10
11
  end
11
12
 
12
13
  def string
13
- @out.string
14
+ @definition.build_string
14
15
  end
15
16
 
16
- def text(content)
17
- puts unless first_print?
18
- puts content
17
+ def text(content, options = {})
18
+ @definition << HammerCLI::Help::Text.new(content, options)
19
19
  end
20
20
 
21
- def list(items)
21
+ def list(items, options = {})
22
22
  return if items.empty?
23
-
24
- items = normalize_list(items)
25
- max_len = items.map { |i| i[0].to_s.length }.max
26
- indent_size = (max_len + INDENT_STEP > LIST_INDENT) ? (max_len + INDENT_STEP) : LIST_INDENT
27
-
28
- puts unless first_print?
29
- items.each do |col1, col2|
30
- # handle multiple lines in the second column
31
- col2 = indent(col2.to_s, ' ' * indent_size).lstrip
32
-
33
- line = "%-#{indent_size}s%s" % [col1, col2]
34
- line.strip!
35
- puts line
36
- end
23
+ @definition << HammerCLI::Help::List.new(items, options)
37
24
  end
38
25
 
39
- def section(label, &block)
40
- puts unless first_print?
41
- heading(label)
42
-
26
+ def section(label, options = {}, &block)
43
27
  sub_builder = TextBuilder.new(@richtext)
44
28
  yield(sub_builder) if block_given?
45
- puts indent(sub_builder.string)
46
- end
47
-
48
- def indent(content, indentation = nil)
49
- indentation ||= " " * INDENT_STEP
50
- content = content.split("\n") unless content.is_a? Array
51
- content.map do |line|
52
- (indentation + line).rstrip
53
- end.join("\n")
29
+ options[:richtext] ||= @richtext
30
+ @definition << HammerCLI::Help::Section.new(label, sub_builder.definition, options)
54
31
  end
55
32
 
56
- protected
57
-
58
- def heading(label)
59
- label = "#{label}:"
60
- label = HighLine.color(label, :bold) if @richtext
61
- puts label
33
+ def find_item(item_id)
34
+ @definition.find_item(item_id)
62
35
  end
63
36
 
64
- def puts(*args)
65
- @out.puts(*args)
37
+ def at(path = [])
38
+ item = path.empty? ? self : @definition.at(path)
39
+ sub_builder = TextBuilder.new(@richtext)
40
+ sub_builder.definition = item.definition
41
+ yield(sub_builder)
42
+ item.definition = sub_builder.definition
66
43
  end
67
44
 
68
- def first_print?
69
- @out.size == 0
45
+ def insert(mode, item_id)
46
+ sub_builder = TextBuilder.new(@richtext)
47
+ yield(sub_builder)
48
+ @definition.insert_definition(mode, item_id, sub_builder.definition)
70
49
  end
71
50
 
72
- def normalize_list(items)
73
- items.map do |i|
74
- i.is_a?(Array) ? i : [i]
75
- end
51
+ def indent(content, indentation = nil)
52
+ HammerCLI::Help::AbstractItem.indent(content, indentation)
76
53
  end
77
54
  end
78
55
  end
@@ -146,4 +146,3 @@ include FastGettext::Translation
146
146
 
147
147
  domain = [HammerCLI::I18n::LocaleDomain.new, HammerCLI::I18n::SystemLocaleDomain.new].find { |d| d.available? }
148
148
  HammerCLI::I18n.add_domain(domain) if domain
149
-
@@ -1,17 +1,20 @@
1
1
  module HammerCLI
2
2
  module Options
3
3
  class OptionCollector
4
- attr_accessor :option_sources
4
+ attr_accessor :option_processor
5
5
 
6
- def initialize(recognised_options, option_sources)
6
+ def initialize(recognised_options, option_processor)
7
7
  @recognised_options = recognised_options
8
- @option_sources = option_sources
8
+
9
+ if !option_processor.is_a?(HammerCLI::Options::ProcessorList)
10
+ @option_processor = HammerCLI::Options::ProcessorList.new(option_processor)
11
+ else
12
+ @option_processor = option_processor
13
+ end
9
14
  end
10
15
 
11
16
  def all_options_raw
12
- @all_options_raw ||= @option_sources.inject({}) do |all_options, source|
13
- source.get_options(@recognised_options, all_options)
14
- end
17
+ @all_options_raw ||= @option_processor.process(@recognised_options, {})
15
18
  end
16
19
 
17
20
  def all_options
@@ -21,13 +24,13 @@ module HammerCLI
21
24
  def options
22
25
  @options ||= all_options.reject { |key, value| value.nil? && all_options_raw[key].nil? }
23
26
  end
24
-
27
+
25
28
  private
26
-
29
+
27
30
  def translate_nils(opts)
28
31
  Hash[ opts.map { |k,v| [k, translate_nil(v)] } ]
29
32
  end
30
-
33
+
31
34
  def translate_nil(value)
32
35
  value == HammerCLI::NilValue ? nil : value
33
36
  end
@@ -0,0 +1,17 @@
1
+ module HammerCLI
2
+ module Options
3
+ class OptionProcessor
4
+ def initialize(name: nil)
5
+ @name = name
6
+ end
7
+
8
+ def name
9
+ @name || self.class.name.split('::')[-1]
10
+ end
11
+
12
+ def process(defined_options, result)
13
+ result
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ module HammerCLI
2
+ module Options
3
+ class ProcessorList < Array
4
+ attr_reader :name
5
+
6
+ def initialize(sources = [], name: nil)
7
+ @name = name
8
+ self.push(*sources)
9
+ end
10
+
11
+ def insert_relative(mode, target_name, processor)
12
+ index = target_name.nil? ? nil : item_index(target_name)
13
+ HammerCLI.insert_relative(self, mode, index, processor)
14
+ end
15
+
16
+ def find_by_name(name)
17
+ self[item_index(name)]
18
+ end
19
+
20
+ def process(defined_options, result)
21
+ self.inject(result) do |all_options, processor|
22
+ processor.process(defined_options, all_options)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def item_index(target_name)
29
+ idx = find_index do |item|
30
+ item.respond_to?(:name) && (item.name == target_name)
31
+ end
32
+ raise ArgumentError, "Option processor '#{target_name}' not found" if idx.nil?
33
+ idx
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../option_processor'
2
+
3
+ module HammerCLI
4
+ module Options
5
+ module Sources
6
+ class Base < HammerCLI::Options::OptionProcessor
7
+ def process(defined_options, result)
8
+ get_options(defined_options, result)
9
+ end
10
+
11
+ def get_options(defined_options, result)
12
+ result
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end