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.
- checksums.yaml +4 -4
- data/README.md +3 -314
- data/bin/hammer +45 -6
- data/config/cli.modules.d/module_config_template.yml +4 -0
- data/config/cli_config.template.yml +9 -11
- data/doc/developer_docs.md +1 -0
- data/doc/i18n.md +85 -0
- data/doc/installation.md +321 -0
- data/lib/hammer_cli.rb +3 -0
- data/lib/hammer_cli/abstract.rb +15 -24
- data/lib/hammer_cli/apipie/command.rb +13 -7
- data/lib/hammer_cli/apipie/options.rb +14 -16
- data/lib/hammer_cli/apipie/read_command.rb +6 -1
- data/lib/hammer_cli/apipie/resource.rb +48 -58
- data/lib/hammer_cli/apipie/write_command.rb +5 -1
- data/lib/hammer_cli/completer.rb +77 -21
- data/lib/hammer_cli/connection.rb +44 -0
- data/lib/hammer_cli/exception_handler.rb +15 -4
- data/lib/hammer_cli/exceptions.rb +6 -0
- data/lib/hammer_cli/i18n.rb +95 -0
- data/lib/hammer_cli/logger.rb +3 -3
- data/lib/hammer_cli/main.rb +12 -11
- data/lib/hammer_cli/modules.rb +19 -6
- data/lib/hammer_cli/options/normalizers.rb +42 -7
- data/lib/hammer_cli/options/option_definition.rb +2 -2
- data/lib/hammer_cli/output.rb +1 -0
- data/lib/hammer_cli/output/adapter/abstract.rb +20 -0
- data/lib/hammer_cli/output/adapter/base.rb +49 -78
- data/lib/hammer_cli/output/adapter/csv.rb +5 -5
- data/lib/hammer_cli/output/adapter/table.rb +41 -10
- data/lib/hammer_cli/output/dsl.rb +1 -1
- data/lib/hammer_cli/output/field_filter.rb +21 -0
- data/lib/hammer_cli/output/fields.rb +44 -78
- data/lib/hammer_cli/output/formatters.rb +38 -0
- data/lib/hammer_cli/settings.rb +28 -6
- data/lib/hammer_cli/shell.rb +58 -57
- data/lib/hammer_cli/utils.rb +14 -0
- data/lib/hammer_cli/validator.rb +5 -5
- data/lib/hammer_cli/version.rb +1 -1
- data/locale/Makefile +64 -0
- data/locale/hammer-cli.pot +203 -0
- data/locale/zanata.xml +29 -0
- data/test/unit/apipie/command_test.rb +42 -25
- data/test/unit/apipie/read_command_test.rb +10 -7
- data/test/unit/apipie/write_command_test.rb +9 -8
- data/test/unit/completer_test.rb +206 -21
- data/test/unit/connection_test.rb +68 -0
- data/test/unit/fixtures/apipie/architectures.json +153 -0
- data/test/unit/fixtures/apipie/documented.json +79 -0
- data/test/unit/fixtures/json_input/invalid.json +12 -0
- data/test/unit/fixtures/json_input/valid.json +12 -0
- data/test/unit/history_test.rb +71 -0
- data/test/unit/main_test.rb +9 -0
- data/test/unit/modules_test.rb +22 -6
- data/test/unit/options/field_filter_test.rb +27 -0
- data/test/unit/options/normalizers_test.rb +53 -0
- data/test/unit/output/adapter/base_test.rb +162 -10
- data/test/unit/output/adapter/csv_test.rb +16 -3
- data/test/unit/output/adapter/table_test.rb +97 -13
- data/test/unit/output/dsl_test.rb +74 -6
- data/test/unit/output/fields_test.rb +93 -62
- data/test/unit/output/formatters_test.rb +47 -0
- data/test/unit/settings_test.rb +35 -4
- data/test/unit/utils_test.rb +45 -0
- metadata +85 -4
- 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 = " "*
|
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
|
-
|
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
|
29
|
-
|
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
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
43
|
-
|
44
|
-
puts format_value(field, data).to_s
|
45
|
-
end
|
37
|
+
def render_fields(fields, data)
|
38
|
+
output = ""
|
46
39
|
|
47
|
-
|
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
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
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,
|
77
|
+
def render_label(field, width)
|
87
78
|
if field.label
|
88
|
-
|
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
|
-
|
81
|
+
""
|
92
82
|
end
|
93
83
|
end
|
94
84
|
|
95
|
-
def
|
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
|
-
|
101
|
-
|
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
|
91
|
+
def label_width(fields)
|
119
92
|
fields.inject(0) do |result, f|
|
120
|
-
width =
|
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
|
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
|
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
|
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
|
+
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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 = ' | '
|
@@ -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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def get_value(data)
|
34
|
-
follow_path(data, path || [])
|
13
|
+
@label = options[:label]
|
14
|
+
@options = options
|
35
15
|
end
|
36
16
|
|
37
|
-
|
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
|
52
|
-
if
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
27
|
+
true
|
62
28
|
end
|
63
29
|
end
|
64
30
|
|
65
|
-
|
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
|
-
|
37
|
+
|
38
|
+
class ContainerField < Field
|
90
39
|
|
91
40
|
def initialize(options={}, &block)
|
92
41
|
super(options)
|
93
|
-
dsl = HammerCLI::Output::Dsl.new
|
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
|
68
|
+
class Date < Field
|
69
|
+
end
|
107
70
|
|
108
|
-
|
109
|
-
|
110
|
-
dsl = HammerCLI::Output::Dsl.new
|
111
|
-
dsl.build &block if block_given?
|
71
|
+
class Id < Field
|
72
|
+
end
|
112
73
|
|
113
|
-
|
114
|
-
|
74
|
+
class List < Field
|
75
|
+
end
|
115
76
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|