hammer_cli 0.0.12 → 0.0.13
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 +5 -1
- data/lib/hammer_cli/abstract.rb +40 -4
- data/lib/hammer_cli/apipie/resource.rb +2 -2
- data/lib/hammer_cli/completer.rb +193 -0
- data/lib/hammer_cli/exception_handler.rb +17 -0
- data/lib/hammer_cli/main.rb +25 -47
- data/lib/hammer_cli/options/normalizers.rb +22 -0
- data/lib/hammer_cli/options/option_definition.rb +9 -0
- data/lib/hammer_cli/output/adapter/table.rb +42 -4
- data/lib/hammer_cli/shell.rb +104 -15
- data/lib/hammer_cli/version.rb +1 -1
- data/lib/hammer_cli.rb +1 -0
- data/test/unit/apipie/command_test.rb +7 -6
- data/test/unit/apipie/read_command_test.rb +1 -1
- data/test/unit/apipie/write_command_test.rb +3 -1
- data/test/unit/completer_test.rb +178 -0
- data/test/unit/exception_handler_test.rb +13 -0
- data/test/unit/options/option_definition_test.rb +11 -1
- data/test/unit/output/adapter/table_test.rb +19 -0
- data/test/unit/output/output_test.rb +1 -1
- metadata +65 -63
- data/lib/hammer_cli/autocompletion.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c64a799978261a9f78c6ce7642a7cb82473cbb0a
|
4
|
+
data.tar.gz: 5893a4bfe3980b8794ea988c3895871625f1de47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18db270151afec8b1da636d4a3aa9088090c1b4de705102d8ca877c2c5cccbba65acbaa21f12dcbd2abb685f1bb2e684d6324afab555e6172857544c1c704215
|
7
|
+
data.tar.gz: 2c860c0406767e951e0d63d11d2c2e4e5c9d992148710110941982f3a0a9e295a8faf0c2983de1a97b5ba864b5f0aec66c851918e561132f99253c6b83e2edee
|
data/bin/hammer
CHANGED
@@ -37,6 +37,9 @@ if preparser.verbose?
|
|
37
37
|
root_logger.appenders = root_logger.appenders << ::Logging.appenders.stderr(:layout => HammerCLI::Logger::COLOR_LAYOUT)
|
38
38
|
end
|
39
39
|
|
40
|
+
hammer_version = Gem.loaded_specs['hammer_cli'].version.to_s
|
41
|
+
logger.info "Initialization of Hammer CLI (#{hammer_version}) has started..."
|
42
|
+
|
40
43
|
# log which config was loaded (now when we have logging)
|
41
44
|
HammerCLI::Settings.path_history.each do |path|
|
42
45
|
logger.info "Configuration from the file #{path} has been loaded"
|
@@ -56,7 +59,8 @@ modules.each do |m|
|
|
56
59
|
handler.handle_exception(e)
|
57
60
|
exit HammerCLI::EX_SOFTWARE
|
58
61
|
end
|
59
|
-
|
62
|
+
module_version = Gem.loaded_specs[m].version.to_s
|
63
|
+
logger.info "Extension module #{m} (#{module_version}) loaded"
|
60
64
|
end
|
61
65
|
|
62
66
|
exit HammerCLI::MainCommand.run || HammerCLI::EX_OK
|
data/lib/hammer_cli/abstract.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'hammer_cli/autocompletion'
|
2
1
|
require 'hammer_cli/exception_handler'
|
3
2
|
require 'hammer_cli/logger_watch'
|
4
3
|
require 'hammer_cli/options/option_definition'
|
@@ -11,7 +10,6 @@ module HammerCLI
|
|
11
10
|
|
12
11
|
class AbstractCommand < Clamp::Command
|
13
12
|
|
14
|
-
extend Autocompletion
|
15
13
|
class << self
|
16
14
|
attr_accessor :validation_block
|
17
15
|
end
|
@@ -25,8 +23,6 @@ module HammerCLI
|
|
25
23
|
raise "exit code must be integer" unless exit_code.is_a? Integer
|
26
24
|
return exit_code
|
27
25
|
rescue => e
|
28
|
-
# do not catch Clamp errors
|
29
|
-
raise if e.class <= Clamp::UsageError || e.class <= Clamp::HelpWanted
|
30
26
|
handle_exception e
|
31
27
|
end
|
32
28
|
|
@@ -122,6 +118,34 @@ module HammerCLI
|
|
122
118
|
|
123
119
|
protected
|
124
120
|
|
121
|
+
def interactive?
|
122
|
+
if context[:interactive].nil?
|
123
|
+
return STDOUT.tty? && (HammerCLI::Settings.get(:ui, :interactive) != false)
|
124
|
+
else
|
125
|
+
return context[:interactive]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def ask_username
|
130
|
+
ask("Username: ") if interactive?
|
131
|
+
end
|
132
|
+
|
133
|
+
def ask_password
|
134
|
+
ask("Password for '%s': " % username) {|q| q.echo = false} if interactive?
|
135
|
+
end
|
136
|
+
|
137
|
+
def username(ask_interactively=true)
|
138
|
+
context[:username] ||= ENV['FOREMAN_USERNAME'] || HammerCLI::Settings.get(:foreman, :username)
|
139
|
+
context[:username] ||= ask_username if ask_interactively
|
140
|
+
context[:username]
|
141
|
+
end
|
142
|
+
|
143
|
+
def password(ask_interactively=true)
|
144
|
+
context[:password] ||= ENV['FOREMAN_PASSWORD'] || HammerCLI::Settings.get(:foreman, :password)
|
145
|
+
context[:password] ||= ask_password if ask_interactively
|
146
|
+
context[:password]
|
147
|
+
end
|
148
|
+
|
125
149
|
def print_record(definition, record)
|
126
150
|
output.print_record(definition, record)
|
127
151
|
end
|
@@ -181,13 +205,25 @@ module HammerCLI
|
|
181
205
|
end
|
182
206
|
end
|
183
207
|
|
208
|
+
def self.define_simple_writer_for(attribute, &block)
|
209
|
+
define_method(attribute.write_method) do |value|
|
210
|
+
value = instance_exec(value, &block) if block
|
211
|
+
if attribute.respond_to?(:context_target) && attribute.context_target
|
212
|
+
context[attribute.context_target] = value
|
213
|
+
end
|
214
|
+
attribute.of(self).set(value)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
184
218
|
def self.option(switches, type, description, opts = {}, &block)
|
185
219
|
formatter = opts.delete(:format)
|
220
|
+
context_target = opts.delete(:context_target)
|
186
221
|
|
187
222
|
HammerCLI::Options::OptionDefinition.new(switches, type, description, opts).tap do |option|
|
188
223
|
declared_options << option
|
189
224
|
|
190
225
|
option.value_formatter = formatter
|
226
|
+
option.context_target = context_target
|
191
227
|
block ||= option.default_conversion_block
|
192
228
|
|
193
229
|
define_accessors_for(option, &block)
|
@@ -70,8 +70,8 @@ module HammerCLI::Apipie
|
|
70
70
|
def resource_config
|
71
71
|
config = {}
|
72
72
|
config[:base_url] = HammerCLI::Settings.get(:foreman, :host)
|
73
|
-
config[:username] =
|
74
|
-
config[:password] =
|
73
|
+
config[:username] = username
|
74
|
+
config[:password] = password
|
75
75
|
config
|
76
76
|
end
|
77
77
|
|
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
|
3
|
+
module HammerCLI
|
4
|
+
|
5
|
+
class CompleterLine < Array
|
6
|
+
|
7
|
+
def initialize(line)
|
8
|
+
@line = line
|
9
|
+
super(line.split)
|
10
|
+
end
|
11
|
+
|
12
|
+
def finished?
|
13
|
+
(@line[-1,1] == " ") || @line.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class Completer
|
19
|
+
|
20
|
+
def initialize(cmd_class)
|
21
|
+
@command = cmd_class
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def complete(line)
|
26
|
+
line = CompleterLine.new(line)
|
27
|
+
|
28
|
+
cmd, remaining = find_last_cmd(line)
|
29
|
+
|
30
|
+
opt, value = option_to_complete(cmd, remaining)
|
31
|
+
if opt
|
32
|
+
return complete_attribute(opt, value)
|
33
|
+
else
|
34
|
+
param, value = param_to_complete(cmd, remaining)
|
35
|
+
if param
|
36
|
+
if remaining.finished?
|
37
|
+
return complete_attribute(param, value) + complete_command(cmd, remaining)
|
38
|
+
else
|
39
|
+
return complete_attribute(param, value)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
return complete_command(cmd, remaining)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def complete_attribute(attribute, value)
|
52
|
+
if attribute.respond_to?(:complete)
|
53
|
+
filter(attribute.complete(value), value)
|
54
|
+
else
|
55
|
+
[]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def param_to_complete(cmd, line)
|
61
|
+
params = cmd.parameters.select do |p|
|
62
|
+
(p.attribute_name != 'subcommand_name') and (p.attribute_name != 'subcommand_arguments')
|
63
|
+
end
|
64
|
+
|
65
|
+
return [nil, nil] if params.empty?
|
66
|
+
|
67
|
+
# select param candidates
|
68
|
+
param_candidates = []
|
69
|
+
line.reverse.each do |word|
|
70
|
+
break if word.start_with?('-')
|
71
|
+
param_candidates.unshift(word)
|
72
|
+
end
|
73
|
+
|
74
|
+
param = nil
|
75
|
+
|
76
|
+
if line.finished?
|
77
|
+
# "--option " or "--option xx " or "xx "
|
78
|
+
value = nil
|
79
|
+
param_index = param_candidates.size
|
80
|
+
else
|
81
|
+
# "--opt" or "--option xx" or "xx yy"
|
82
|
+
value = param_candidates.last
|
83
|
+
param_index = param_candidates.size - 1
|
84
|
+
end
|
85
|
+
|
86
|
+
if param_index >= 0
|
87
|
+
if params.size > param_index
|
88
|
+
param = params[param_index]
|
89
|
+
elsif params.last.multivalued?
|
90
|
+
param = params.last
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
return [param, value]
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def option_to_complete(cmd, line)
|
99
|
+
return [nil, nil] if line.empty?
|
100
|
+
|
101
|
+
if line.finished?
|
102
|
+
# last word must be option and can't be flag -> we complete the value
|
103
|
+
# "--option " nebo "--option xx "
|
104
|
+
opt = cmd.find_option(line[-1])
|
105
|
+
return [opt, nil] if opt and not opt.flag?
|
106
|
+
else
|
107
|
+
# we complete the value in the second case
|
108
|
+
# "--opt" or "--option xx" or "xx yy"
|
109
|
+
opt = cmd.find_option(line[-2])
|
110
|
+
return [opt, line[-1]] if opt and not opt.flag?
|
111
|
+
end
|
112
|
+
return [nil, nil]
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def find_last_cmd(line)
|
117
|
+
cmd = @command
|
118
|
+
subcommands = sub_command_map(cmd)
|
119
|
+
|
120
|
+
# if the last word is not finished we have to select it's parent
|
121
|
+
# -> shorten the line
|
122
|
+
words = line.dup
|
123
|
+
words.pop unless line.finished?
|
124
|
+
|
125
|
+
cmd_idx = 0
|
126
|
+
words.each_with_index do |word, idx|
|
127
|
+
unless word.start_with?('-')
|
128
|
+
break unless subcommands.has_key? word
|
129
|
+
|
130
|
+
cmd = subcommands[word]
|
131
|
+
cmd_idx = idx+1
|
132
|
+
subcommands = sub_command_map(cmd)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# cut processed part of the line and return remaining
|
137
|
+
remaining = line.dup
|
138
|
+
remaining.shift(cmd_idx) if cmd_idx > 0
|
139
|
+
return [cmd, remaining]
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def complete_command(command, remaining)
|
144
|
+
completions = []
|
145
|
+
completions += sub_command_names(command)
|
146
|
+
completions += command_options(command)
|
147
|
+
completions = Completer::finalize_completions(completions)
|
148
|
+
|
149
|
+
if remaining.finished?
|
150
|
+
return completions
|
151
|
+
else
|
152
|
+
return filter(completions, remaining.last)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def filter(completions, last_word)
|
158
|
+
if last_word.to_s != ""
|
159
|
+
completions.select{|name| name.start_with? last_word }
|
160
|
+
else
|
161
|
+
completions
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
def self.finalize_completions(completions)
|
167
|
+
completions.collect{|name| name+' ' }
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
def sub_command_map(cmd_class)
|
172
|
+
cmd_class.recognised_subcommands.inject({}) do |cmd_map, cmd|
|
173
|
+
cmd.names.each do |name|
|
174
|
+
cmd_map.update(name => cmd.subcommand_class)
|
175
|
+
end
|
176
|
+
cmd_map
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
def sub_command_names(cmd_class)
|
182
|
+
sub_command_map(cmd_class).keys.flatten
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
def command_options(cmd_class)
|
187
|
+
cmd_class.recognised_options.inject([]) do |opt_switches, opt|
|
188
|
+
opt_switches += opt.switches
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
@@ -12,6 +12,8 @@ module HammerCLI
|
|
12
12
|
def mappings
|
13
13
|
[
|
14
14
|
[Exception, :handle_general_exception], # catch all
|
15
|
+
[Clamp::HelpWanted, :handle_help_wanted],
|
16
|
+
[Clamp::UsageError, :handle_usage_exception],
|
15
17
|
[RestClient::ResourceNotFound, :handle_not_found],
|
16
18
|
[RestClient::Unauthorized, :handle_unauthorized],
|
17
19
|
]
|
@@ -41,6 +43,10 @@ module HammerCLI
|
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
46
|
+
def print_message(msg)
|
47
|
+
output.print_message(msg)
|
48
|
+
end
|
49
|
+
|
44
50
|
def log_full_error(e)
|
45
51
|
backtrace = e.backtrace || []
|
46
52
|
@logger.error "\n\n#{e.class} (#{e.message}):\n " +
|
@@ -54,6 +60,17 @@ module HammerCLI
|
|
54
60
|
HammerCLI::EX_SOFTWARE
|
55
61
|
end
|
56
62
|
|
63
|
+
def handle_usage_exception(e)
|
64
|
+
print_error "Error: %s\n\nSee: '%s --help'" % [e.message, e.command.invocation_path]
|
65
|
+
log_full_error e
|
66
|
+
HammerCLI::EX_USAGE
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_help_wanted(e)
|
70
|
+
print_message e.command.help
|
71
|
+
HammerCLI::EX_OK
|
72
|
+
end
|
73
|
+
|
57
74
|
def handle_not_found(e)
|
58
75
|
print_error e.message
|
59
76
|
log_full_error e
|
data/lib/hammer_cli/main.rb
CHANGED
@@ -7,70 +7,48 @@ module HammerCLI
|
|
7
7
|
option ["-v", "--verbose"], :flag, "be verbose"
|
8
8
|
option ["-c", "--config"], "CFG_FILE", "path to custom config file"
|
9
9
|
|
10
|
-
option ["-u", "--username"], "USERNAME", "username to access the remote system"
|
11
|
-
|
10
|
+
option ["-u", "--username"], "USERNAME", "username to access the remote system",
|
11
|
+
:context_target => :username
|
12
|
+
option ["-p", "--password"], "PASSWORD", "password to access the remote system",
|
13
|
+
:context_target => :password
|
12
14
|
|
13
15
|
option "--version", :flag, "show version" do
|
14
|
-
puts "hammer
|
16
|
+
puts "hammer (%s)" % HammerCLI.version
|
17
|
+
modules = HammerCLI::Settings.get(:modules) || []
|
18
|
+
modules.each do |m|
|
19
|
+
module_version = Gem.loaded_specs[m].version.to_s
|
20
|
+
puts " * #{m} (#{module_version})"
|
21
|
+
end
|
15
22
|
exit(HammerCLI::EX_OK)
|
16
23
|
end
|
17
24
|
|
18
|
-
option ["--show-ids"], :flag, "Show ids of associated resources"
|
25
|
+
option ["--show-ids"], :flag, "Show ids of associated resources",
|
26
|
+
:context_target => :show_ids
|
27
|
+
option ["--interactive"], "INTERACTIVE", "Explicitly turn interactive mode on/off",
|
28
|
+
:format => HammerCLI::Options::Normalizers::Bool.new,
|
29
|
+
:context_target => :interactive
|
19
30
|
|
20
31
|
option ["--csv"], :flag, "Output as CSV (same as --adapter=csv)"
|
21
|
-
option ["--output"], "ADAPTER", "Set output format. One of [%s]" %
|
22
|
-
|
23
|
-
|
32
|
+
option ["--output"], "ADAPTER", "Set output format. One of [%s]" %
|
33
|
+
HammerCLI::Output::Output.adapters.keys.join(', '),
|
34
|
+
:context_target => :adapter
|
35
|
+
option ["--csv-separator"], "SEPARATOR", "Character to separate the values",
|
36
|
+
:context_target => :csv_separator
|
24
37
|
|
25
|
-
option ["-P", "--ask-pass"], :flag, "Ask for password" do
|
26
|
-
context[:password] = get_password()
|
27
|
-
''
|
28
|
-
end
|
29
38
|
|
30
39
|
option "--autocomplete", "LINE", "Get list of possible endings" do |line|
|
31
|
-
|
32
|
-
line.
|
33
|
-
endings = self.class.autocomplete(line).map { |l| l[0] }
|
34
|
-
puts endings.join(' ')
|
35
|
-
exit(HammerCLI::EX_OK)
|
36
|
-
end
|
37
|
-
|
38
|
-
def show_ids=(show_ids)
|
39
|
-
context[:show_ids] = show_ids
|
40
|
-
end
|
40
|
+
# get rid of word 'hammer' on the line
|
41
|
+
line = line.to_s.gsub(/^\S+/, '')
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
def password=(p)
|
47
|
-
@password = p
|
48
|
-
context[:password] = p
|
43
|
+
completer = Completer.new(HammerCLI::MainCommand)
|
44
|
+
puts completer.complete(line).join(" ")
|
45
|
+
exit(HammerCLI::EX_OK)
|
49
46
|
end
|
50
47
|
|
51
48
|
def csv=(csv)
|
52
49
|
context[:adapter] = :csv
|
53
50
|
end
|
54
51
|
|
55
|
-
def csv_separator=(separator)
|
56
|
-
context[:csv_separator] = separator
|
57
|
-
end
|
58
|
-
|
59
|
-
def output=(adapter)
|
60
|
-
context[:adapter] = adapter
|
61
|
-
end
|
62
|
-
|
63
|
-
def username=(u)
|
64
|
-
@username = u
|
65
|
-
context[:username] = u
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def get_password(prompt="Enter Password ")
|
71
|
-
ask(prompt) {|q| q.echo = false}
|
72
|
-
end
|
73
|
-
|
74
52
|
end
|
75
53
|
|
76
54
|
end
|
@@ -11,6 +11,10 @@ module HammerCLI
|
|
11
11
|
def format(val)
|
12
12
|
raise NotImplementedError, "Class #{self.class.name} must implement method format."
|
13
13
|
end
|
14
|
+
|
15
|
+
def complete(val)
|
16
|
+
[]
|
17
|
+
end
|
14
18
|
end
|
15
19
|
|
16
20
|
|
@@ -62,6 +66,10 @@ module HammerCLI
|
|
62
66
|
raise ArgumentError, "value must be one of true/false, yes/no, 1/0"
|
63
67
|
end
|
64
68
|
end
|
69
|
+
|
70
|
+
def complete(value)
|
71
|
+
["yes ", "no "]
|
72
|
+
end
|
65
73
|
end
|
66
74
|
|
67
75
|
|
@@ -70,6 +78,16 @@ module HammerCLI
|
|
70
78
|
def format(path)
|
71
79
|
::File.read(::File.expand_path(path))
|
72
80
|
end
|
81
|
+
|
82
|
+
def complete(value)
|
83
|
+
Dir[value.to_s+'*'].collect do |file|
|
84
|
+
if ::File.directory?(file)
|
85
|
+
file+'/'
|
86
|
+
else
|
87
|
+
file+' '
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
73
91
|
end
|
74
92
|
|
75
93
|
|
@@ -91,6 +109,10 @@ module HammerCLI
|
|
91
109
|
end
|
92
110
|
end
|
93
111
|
|
112
|
+
def complete(value)
|
113
|
+
Completer::finalize_completions(@allowed_values)
|
114
|
+
end
|
115
|
+
|
94
116
|
private
|
95
117
|
|
96
118
|
def quoted_values
|
@@ -6,6 +6,15 @@ module HammerCLI
|
|
6
6
|
class OptionDefinition < Clamp::Option::Definition
|
7
7
|
|
8
8
|
attr_accessor :value_formatter
|
9
|
+
attr_accessor :context_target
|
10
|
+
|
11
|
+
def complete(value)
|
12
|
+
if value_formatter.nil?
|
13
|
+
[]
|
14
|
+
else
|
15
|
+
value_formatter.complete(value)
|
16
|
+
end
|
17
|
+
end
|
9
18
|
|
10
19
|
def help_lhs
|
11
20
|
super
|
@@ -12,26 +12,28 @@ module HammerCLI::Output::Adapter
|
|
12
12
|
print_collection(fields, [record].flatten(1))
|
13
13
|
end
|
14
14
|
|
15
|
-
def print_collection(
|
15
|
+
def print_collection(all_fields, collection)
|
16
|
+
|
17
|
+
fields = all_fields.reject { |f| f.class <= Fields::Id && !@context[:show_ids] }
|
16
18
|
|
17
19
|
rows = collection.collect do |d|
|
18
20
|
row = {}
|
19
21
|
fields.each do |f|
|
20
|
-
|
21
22
|
row[f.label.to_sym] = f.get_value(d) || ""
|
22
23
|
end
|
23
24
|
row
|
24
25
|
end
|
25
26
|
|
26
27
|
options = fields.collect do |f|
|
27
|
-
next if f.class <= Fields::Id && !@context[:show_ids]
|
28
28
|
{ f.label.to_sym => { :formatters => Array(@formatters.formatter_for_type(f.class)) } }
|
29
29
|
end
|
30
30
|
|
31
|
+
sort_order = fields.map { |f| f.label.upcase }
|
32
|
+
|
31
33
|
printer = TablePrint::Printer.new(rows, options)
|
32
34
|
TablePrint::Config.max_width = 40
|
33
35
|
|
34
|
-
output = printer.table_print
|
36
|
+
output = sort_columns(printer.table_print, sort_order)
|
35
37
|
dashes = /\n([-|]+)\n/.match(output)
|
36
38
|
|
37
39
|
puts dashes[1] if dashes
|
@@ -46,6 +48,42 @@ module HammerCLI::Output::Adapter
|
|
46
48
|
puts '-' * size
|
47
49
|
end
|
48
50
|
|
51
|
+
private
|
52
|
+
|
53
|
+
def sort_columns(output, sort_order)
|
54
|
+
return output if sort_order.length == 1 # don't sort one column
|
55
|
+
delimiter = ' | '
|
56
|
+
lines = output.split("\n")
|
57
|
+
out = []
|
58
|
+
|
59
|
+
headers = lines.first.split(delimiter).map(&:strip)
|
60
|
+
|
61
|
+
# create mapping table for column indexes
|
62
|
+
sort_map = []
|
63
|
+
sort_order.each { |c| sort_map << headers.index(c) }
|
64
|
+
|
65
|
+
lines.each do |line|
|
66
|
+
columns = line.split(delimiter)
|
67
|
+
if columns.length == 1 # dashes
|
68
|
+
columns = columns.first.split('-|-')
|
69
|
+
if columns.length == 1
|
70
|
+
out << columns.first
|
71
|
+
else # new style dashes
|
72
|
+
new_row = []
|
73
|
+
sort_map.each { |i| new_row << columns[i] }
|
74
|
+
out << new_row.join('-|-')
|
75
|
+
end
|
76
|
+
else
|
77
|
+
# reorder row
|
78
|
+
new_row = []
|
79
|
+
sort_map.each { |i| new_row << columns[i] }
|
80
|
+
out << new_row.join(delimiter)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
out.join("\n")
|
85
|
+
end
|
86
|
+
|
49
87
|
end
|
50
88
|
|
51
89
|
HammerCLI::Output::Output.register_adapter(:table, Table)
|