hammer_cli 0.0.18 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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