hammer_cli 0.0.18 → 0.1.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -314
  3. data/bin/hammer +45 -6
  4. data/config/cli.modules.d/module_config_template.yml +4 -0
  5. data/config/cli_config.template.yml +9 -11
  6. data/doc/developer_docs.md +1 -0
  7. data/doc/i18n.md +85 -0
  8. data/doc/installation.md +321 -0
  9. data/lib/hammer_cli.rb +3 -0
  10. data/lib/hammer_cli/abstract.rb +15 -24
  11. data/lib/hammer_cli/apipie/command.rb +13 -7
  12. data/lib/hammer_cli/apipie/options.rb +14 -16
  13. data/lib/hammer_cli/apipie/read_command.rb +6 -1
  14. data/lib/hammer_cli/apipie/resource.rb +48 -58
  15. data/lib/hammer_cli/apipie/write_command.rb +5 -1
  16. data/lib/hammer_cli/completer.rb +77 -21
  17. data/lib/hammer_cli/connection.rb +44 -0
  18. data/lib/hammer_cli/exception_handler.rb +15 -4
  19. data/lib/hammer_cli/exceptions.rb +6 -0
  20. data/lib/hammer_cli/i18n.rb +95 -0
  21. data/lib/hammer_cli/logger.rb +3 -3
  22. data/lib/hammer_cli/main.rb +12 -11
  23. data/lib/hammer_cli/modules.rb +19 -6
  24. data/lib/hammer_cli/options/normalizers.rb +42 -7
  25. data/lib/hammer_cli/options/option_definition.rb +2 -2
  26. data/lib/hammer_cli/output.rb +1 -0
  27. data/lib/hammer_cli/output/adapter/abstract.rb +20 -0
  28. data/lib/hammer_cli/output/adapter/base.rb +49 -78
  29. data/lib/hammer_cli/output/adapter/csv.rb +5 -5
  30. data/lib/hammer_cli/output/adapter/table.rb +41 -10
  31. data/lib/hammer_cli/output/dsl.rb +1 -1
  32. data/lib/hammer_cli/output/field_filter.rb +21 -0
  33. data/lib/hammer_cli/output/fields.rb +44 -78
  34. data/lib/hammer_cli/output/formatters.rb +38 -0
  35. data/lib/hammer_cli/settings.rb +28 -6
  36. data/lib/hammer_cli/shell.rb +58 -57
  37. data/lib/hammer_cli/utils.rb +14 -0
  38. data/lib/hammer_cli/validator.rb +5 -5
  39. data/lib/hammer_cli/version.rb +1 -1
  40. data/locale/Makefile +64 -0
  41. data/locale/hammer-cli.pot +203 -0
  42. data/locale/zanata.xml +29 -0
  43. data/test/unit/apipie/command_test.rb +42 -25
  44. data/test/unit/apipie/read_command_test.rb +10 -7
  45. data/test/unit/apipie/write_command_test.rb +9 -8
  46. data/test/unit/completer_test.rb +206 -21
  47. data/test/unit/connection_test.rb +68 -0
  48. data/test/unit/fixtures/apipie/architectures.json +153 -0
  49. data/test/unit/fixtures/apipie/documented.json +79 -0
  50. data/test/unit/fixtures/json_input/invalid.json +12 -0
  51. data/test/unit/fixtures/json_input/valid.json +12 -0
  52. data/test/unit/history_test.rb +71 -0
  53. data/test/unit/main_test.rb +9 -0
  54. data/test/unit/modules_test.rb +22 -6
  55. data/test/unit/options/field_filter_test.rb +27 -0
  56. data/test/unit/options/normalizers_test.rb +53 -0
  57. data/test/unit/output/adapter/base_test.rb +162 -10
  58. data/test/unit/output/adapter/csv_test.rb +16 -3
  59. data/test/unit/output/adapter/table_test.rb +97 -13
  60. data/test/unit/output/dsl_test.rb +74 -6
  61. data/test/unit/output/fields_test.rb +93 -62
  62. data/test/unit/output/formatters_test.rb +47 -0
  63. data/test/unit/settings_test.rb +35 -4
  64. data/test/unit/utils_test.rb +45 -0
  65. metadata +85 -4
  66. data/test/unit/apipie/fake_api.rb +0 -101
@@ -35,6 +35,26 @@ module HammerCLI::Output::Adapter
35
35
  raise NotImplementedError
36
36
  end
37
37
 
38
+ protected
39
+
40
+ def field_filter
41
+ HammerCLI::Output::FieldFilter.new
42
+ end
43
+
44
+ def data_for_field(field, record)
45
+ path = field.path
46
+
47
+ path.inject(record) do |record, path_key|
48
+ if record.has_key? path_key.to_sym
49
+ record[path_key.to_sym]
50
+ elsif record.has_key? path_key.to_s
51
+ record[path_key.to_s]
52
+ else
53
+ return nil
54
+ end
55
+ end
56
+ end
57
+
38
58
  private
39
59
 
40
60
  def filter_formatters(formatters_map)
@@ -1,7 +1,7 @@
1
1
  module HammerCLI::Output::Adapter
2
2
  class Base < Abstract
3
3
 
4
- GROUP_INDENT = " "*2
4
+ GROUP_INDENT = " "*4
5
5
  LABEL_DIVIDER = ": "
6
6
 
7
7
  def tags
@@ -13,117 +13,88 @@ module HammerCLI::Output::Adapter
13
13
  end
14
14
 
15
15
  def print_collection(fields, collection)
16
- self.fields = fields
17
-
18
- collection.each do |d|
19
- fields.collect do |f|
20
- render_field(f, d)
21
- end
16
+ collection.each do |data|
17
+ puts render_fields(fields, data)
22
18
  puts
23
19
  end
24
20
  end
25
21
 
26
22
  protected
27
23
 
28
- def render_Field(field, data, indent="")
29
- puts indent.to_s+" "+format_value(field, data).to_s
24
+ def field_filter
25
+ filtered = []
26
+ filtered << Fields::Id unless @context[:show_ids]
27
+ HammerCLI::Output::FieldFilter.new(filtered)
30
28
  end
31
29
 
32
- def render_Label(field, data, indent="")
33
- render_label(field, indent)
34
- puts
35
- indent = indent.to_s + GROUP_INDENT
36
-
37
- field.output_definition.fields.collect do |f|
38
- render_field(f, data, indent)
30
+ def filter_fields(fields, data)
31
+ field_filter.filter(fields).reject do |field|
32
+ field_data = data_for_field(field, data)
33
+ not field.display?(field_data)
39
34
  end
40
35
  end
41
36
 
42
- def render_LabeledField(field, data, indent="")
43
- render_label(field, indent)
44
- puts format_value(field, data).to_s
45
- end
37
+ def render_fields(fields, data)
38
+ output = ""
46
39
 
47
- def render_KeyValue(field, data, indent="")
48
- render_label(field, indent)
49
- params = field.get_value(data) || []
50
- print "%{name} => %{value}" % params
51
- end
40
+ fields = filter_fields(fields, data)
52
41
 
53
- def render_Collection(field, data, indent="")
54
- render_label(field, indent)
55
- puts
56
- indent = indent.to_s + " "*(label_width_for_list(self.fields)+LABEL_DIVIDER.size)
42
+ label_width = label_width(fields)
57
43
 
58
- data = field.get_value(data) || []
59
- data.each do |d|
60
- field.output_definition.fields.collect do |f|
61
- render_field(f, d, indent)
62
- end
63
- puts
44
+ fields.collect do |field|
45
+ field_data = data_for_field(field, data)
46
+
47
+ next unless field.display?(field_data)
48
+ output += render_field(field, field_data, label_width)
49
+ output += "\n"
64
50
  end
51
+ output.rstrip
65
52
  end
66
53
 
67
- def render_Joint(field, data, indent="")
68
- render_label(field, indent)
69
- data = field.get_value(data)
70
- puts field.attributes.collect{|attr| data[attr] }.join(" ")
71
- end
54
+ def render_field(field, data, label_width)
72
55
 
73
- def find_renderer(field)
74
- field.class.ancestors.each do |cls|
75
- render_method = "render_"+field_type(cls)
76
- return method(render_method) if respond_to?(render_method, true)
77
- end
78
- raise "No renderer found for field %s" % field.class
79
- end
56
+ if field.is_a? Fields::ContainerField
57
+ output = ""
58
+
59
+ idx = 0
60
+ data = [data] unless data.is_a? Array
61
+ data.each do |d|
62
+ idx += 1
63
+ fields_output = render_fields(field.fields, d).indent_with(GROUP_INDENT)
64
+ fields_output = fields_output.sub(/^[ ]{4}/, " %-3s" % "#{idx})") if field.is_a? Fields::Collection
80
65
 
81
- def render_field(field, data, indent="")
82
- renderer = find_renderer(field)
83
- renderer.call(field, data, indent)
66
+ output += fields_output
67
+ output += "\n"
68
+ end
69
+
70
+ render_label(field, label_width) + "\n" + output.rstrip
71
+ else
72
+ render_label(field, label_width) +
73
+ render_value(field, data)
74
+ end
84
75
  end
85
76
 
86
- def render_label(field, indent="")
77
+ def render_label(field, width)
87
78
  if field.label
88
- w = label_width_for_list(self.fields) - indent.size + 1
89
- print indent.to_s+"%#{-w}s" % (field.label.to_s+LABEL_DIVIDER)
79
+ "%-#{width}s" % (field.label.to_s + LABEL_DIVIDER)
90
80
  else
91
- print indent.to_s
81
+ ""
92
82
  end
93
83
  end
94
84
 
95
- def format_value(field, data)
96
- format_method = "format_"+field_type(field.class)
97
-
98
- value = field.get_value(data)
85
+ def render_value(field, data)
99
86
  formatter = @formatters.formatter_for_type(field.class)
100
- value = formatter.format(value) if formatter
101
- value
102
- end
103
-
104
- def field_type(field_class)
105
- field_class.name.split("::")[-1]
106
- end
107
-
108
- def label_width_for(field)
109
- if field.respond_to?(:output_definition)
110
- label_width_for_list(field.output_definition.fields)+GROUP_INDENT.size
111
- elsif field.respond_to?(:label)
112
- field.label.size+LABEL_DIVIDER.size rescue 0
113
- else
114
- 0
115
- end
87
+ data = formatter.format(data) if formatter
88
+ data.to_s
116
89
  end
117
90
 
118
- def label_width_for_list(fields)
91
+ def label_width(fields)
119
92
  fields.inject(0) do |result, f|
120
- width = label_width_for(f)
93
+ width = f.label.to_s.size + LABEL_DIVIDER.size
121
94
  (width > result) ? width : result
122
95
  end
123
96
  end
124
97
 
125
- attr_accessor :fields
126
-
127
98
  end
128
99
 
129
100
  HammerCLI::Output::Output.register_adapter(:base, Base)
@@ -28,9 +28,9 @@ module HammerCLI::Output::Adapter
28
28
  collection.each do |d|
29
29
  csv << fields.inject([]) do |row, f|
30
30
  unless f.class <= Fields::Id && !@context[:show_ids]
31
- value = (f.get_value(d) || '')
31
+ value = data_for_field(f, d)
32
32
  formatter = @formatters.formatter_for_type(f.class)
33
- row << (formatter ? formatter.format(value) : value)
33
+ row << ((formatter ? formatter.format(value) : value) || '')
34
34
  end
35
35
  row
36
36
  end
@@ -44,16 +44,16 @@ module HammerCLI::Output::Adapter
44
44
  id = msg_params["id"] || msg_params[:id]
45
45
  name = msg_params["name"] || msg_params[:name]
46
46
 
47
- labels = ["Message"]
47
+ labels = [_("Message")]
48
48
  data = [msg.format(msg_params)]
49
49
 
50
50
  if id
51
- labels << "Id"
51
+ labels << _("Id")
52
52
  data << id
53
53
  end
54
54
 
55
55
  if name
56
- labels << "Name"
56
+ labels << _("Name")
57
57
  data << name
58
58
  end
59
59
 
@@ -4,6 +4,9 @@ module HammerCLI::Output::Adapter
4
4
 
5
5
  class Table < Abstract
6
6
 
7
+ MAX_COLUMN_WIDTH = 80
8
+ MIN_COLUMN_WIDTH = 5
9
+
7
10
  def tags
8
11
  [:screen, :flat]
9
12
  end
@@ -13,25 +16,29 @@ module HammerCLI::Output::Adapter
13
16
  end
14
17
 
15
18
  def print_collection(all_fields, collection)
16
-
17
- fields = all_fields.reject { |f| f.class <= Fields::Id && !@context[:show_ids] }
19
+ fields = field_filter.filter(all_fields)
18
20
 
19
21
  rows = collection.collect do |d|
20
22
  row = {}
21
23
  fields.each do |f|
22
- row[f.label.to_sym] = f.get_value(d) || ""
24
+ row[label_for(f)] = data_for_field(f, d) || ""
23
25
  end
24
26
  row
25
27
  end
26
28
 
27
29
  options = fields.collect do |f|
28
- { f.label.to_sym => { :formatters => Array(@formatters.formatter_for_type(f.class)) } }
30
+ { label_for(f) => {
31
+ :formatters => Array(@formatters.formatter_for_type(f.class)),
32
+ :width => max_width_for(f)
33
+ }
34
+ }
29
35
  end
30
36
 
31
37
  sort_order = fields.map { |f| f.label.upcase }
32
38
 
33
39
  printer = TablePrint::Printer.new(rows, options)
34
- TablePrint::Config.max_width = 40
40
+ TablePrint::Config.max_width = MAX_COLUMN_WIDTH
41
+ TablePrint::Config.multibyte = true
35
42
 
36
43
  output = sort_columns(printer.table_print, sort_order)
37
44
  dashes = /\n([-|]+)\n/.match(output)
@@ -41,15 +48,39 @@ module HammerCLI::Output::Adapter
41
48
  puts dashes[1] if dashes
42
49
  end
43
50
 
44
- def print_heading(heading, size)
45
- size = heading.size if heading.size > size
46
- puts '-' * size
47
- puts ' ' * ((size-heading.size)/2) + heading
48
- puts '-' * size
51
+ protected
52
+
53
+ def field_filter
54
+ filtered = [Fields::ContainerField]
55
+ filtered << Fields::Id unless @context[:show_ids]
56
+ HammerCLI::Output::FieldFilter.new(filtered)
49
57
  end
50
58
 
51
59
  private
52
60
 
61
+ def label_for(field)
62
+ width = width_for(field)
63
+ if width
64
+ "%-#{width}s" % field.label.to_s
65
+ else
66
+ field.label.to_s
67
+ end
68
+ end
69
+
70
+ def max_width_for(field)
71
+ width = width_for(field)
72
+ width ||= field.parameters[:max_width]
73
+ width = MIN_COLUMN_WIDTH if width && width < MIN_COLUMN_WIDTH
74
+ width
75
+ end
76
+
77
+ def width_for(field)
78
+ width = field.parameters[:width]
79
+ width = MIN_COLUMN_WIDTH if width && width < MIN_COLUMN_WIDTH
80
+ width
81
+ end
82
+
83
+
53
84
  def sort_columns(output, sort_order)
54
85
  return output if sort_order.length == 1 # don't sort one column
55
86
  delimiter = ' | '
@@ -22,7 +22,7 @@ module HammerCLI::Output
22
22
  options[:path] << key if !key.nil?
23
23
 
24
24
  options[:label] = label
25
- type ||= Fields::DataField
25
+ type ||= Fields::Field
26
26
  custom_field type, options, &block
27
27
  end
28
28
 
@@ -0,0 +1,21 @@
1
+ module HammerCLI::Output
2
+
3
+ class FieldFilter
4
+
5
+ def initialize(field_classes=[])
6
+ @field_classes = field_classes
7
+ end
8
+
9
+ def filter(fields)
10
+ fields = fields.clone
11
+ @field_classes.each do |cls|
12
+ fields.reject! do |f|
13
+ f.is_a? cls
14
+ end
15
+ end
16
+ fields
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -4,93 +4,42 @@ module Fields
4
4
 
5
5
  class Field
6
6
 
7
- def initialize(options={})
8
- end
9
-
10
- def get_value(data)
11
- end
12
-
13
- end
14
-
15
- class LabeledField < Field
16
-
17
7
  attr_reader :label
18
-
19
- def initialize(options={})
20
- @label = options[:label]
21
- end
22
- end
23
-
24
- class DataField < LabeledField
25
-
26
8
  attr_reader :path
27
9
 
28
10
  def initialize(options={})
11
+ @hide_blank = options[:hide_blank].nil? ? false : options[:hide_blank]
29
12
  @path = options[:path] || []
30
- super(options)
31
- end
32
-
33
- def get_value(data)
34
- follow_path(data, path || [])
13
+ @label = options[:label]
14
+ @options = options
35
15
  end
36
16
 
37
- private
38
-
39
- def follow_path(record, path)
40
- record = symbolize_hash_keys(record)
41
-
42
- path.inject(record) do |record, path_key|
43
- if record.has_key? path_key.to_sym
44
- record[path_key.to_sym]
45
- else
46
- return nil
47
- end
48
- end
17
+ def hide_blank?
18
+ @hide_blank
49
19
  end
50
20
 
51
- def symbolize_hash_keys(h)
52
- if h.is_a? Hash
53
- return h.inject({}) do |result,(k,v)|
54
- # symbolizing empty string fails in ruby 1.8
55
- result.update k.to_sym => symbolize_hash_keys(v) unless k.to_s.empty?
56
- result
57
- end
58
- elsif h.is_a? Array
59
- return h.collect{|item| symbolize_hash_keys(item)}
21
+ def display?(value)
22
+ if not hide_blank?
23
+ true
24
+ elsif value.nil?
25
+ false
60
26
  else
61
- h
27
+ true
62
28
  end
63
29
  end
64
30
 
65
- end
66
-
67
- class Date < DataField
68
- end
69
-
70
- class Id < DataField
71
- end
72
-
73
- class List < DataField
74
- end
75
-
76
- class KeyValue < DataField
77
- end
78
-
79
-
80
- class Joint < DataField
81
- def initialize(options={}, &block)
82
- super(options)
83
- @attributes = options[:attributes] || []
31
+ def parameters
32
+ @options
84
33
  end
85
34
 
86
- attr_reader :attributes
87
35
  end
88
36
 
89
- class Label < LabeledField
37
+
38
+ class ContainerField < Field
90
39
 
91
40
  def initialize(options={}, &block)
92
41
  super(options)
93
- dsl = HammerCLI::Output::Dsl.new :path => options[:path]
42
+ dsl = HammerCLI::Output::Dsl.new
94
43
  dsl.build &block if block_given?
95
44
 
96
45
  self.output_definition.append dsl.fields
@@ -101,23 +50,40 @@ module Fields
101
50
  @output_definition
102
51
  end
103
52
 
53
+ def fields
54
+ @output_definition.fields
55
+ end
56
+
57
+ def display?(value)
58
+ if not hide_blank?
59
+ true
60
+ elsif value.nil? || value.empty?
61
+ false
62
+ else
63
+ true
64
+ end
65
+ end
104
66
  end
105
67
 
106
- class Collection < DataField
68
+ class Date < Field
69
+ end
107
70
 
108
- def initialize(options={}, &block)
109
- super(options)
110
- dsl = HammerCLI::Output::Dsl.new
111
- dsl.build &block if block_given?
71
+ class Id < Field
72
+ end
112
73
 
113
- self.output_definition.append dsl.fields
114
- end
74
+ class List < Field
75
+ end
115
76
 
116
- def output_definition
117
- @output_definition ||= HammerCLI::Output::Definition.new
118
- @output_definition
119
- end
77
+ class LongText < Field
78
+ end
79
+
80
+ class KeyValue < Field
81
+ end
82
+
83
+ class Label < ContainerField
84
+ end
120
85
 
86
+ class Collection < ContainerField
121
87
  end
122
88
 
123
89
  end