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