commander-openflighthpc 2.0.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -138
  3. data/commander-openflighthpc.gemspec +2 -1
  4. data/lib/commander.rb +3 -4
  5. data/lib/commander/cli.rb +15 -76
  6. data/lib/commander/command.rb +81 -176
  7. data/lib/commander/error_handler.rb +62 -0
  8. data/lib/commander/help_formatters.rb +0 -4
  9. data/lib/commander/help_formatters/terminal.rb +0 -5
  10. data/lib/commander/help_formatters/terminal/command_help.erb +14 -6
  11. data/lib/commander/help_formatters/terminal/help.erb +12 -4
  12. data/lib/commander/runner.rb +92 -130
  13. data/lib/commander/version.rb +1 -1
  14. metadata +21 -46
  15. data/bin/commander +0 -104
  16. data/lib/commander/blank.rb +0 -7
  17. data/lib/commander/core_ext.rb +0 -2
  18. data/lib/commander/core_ext/array.rb +0 -24
  19. data/lib/commander/core_ext/object.rb +0 -8
  20. data/lib/commander/help_formatters/terminal/subcommand_help.erb +0 -23
  21. data/lib/commander/help_formatters/terminal_compact.rb +0 -11
  22. data/lib/commander/help_formatters/terminal_compact/command_help.erb +0 -26
  23. data/lib/commander/help_formatters/terminal_compact/help.erb +0 -29
  24. data/lib/commander/help_formatters/terminal_compact/subcommand_help.erb +0 -15
  25. data/lib/commander/import.rb +0 -5
  26. data/lib/commander/patches/decimal-integer.rb +0 -17
  27. data/lib/commander/patches/help_formatter_binding.rb +0 -15
  28. data/lib/commander/patches/implicit-short-tags.rb +0 -75
  29. data/lib/commander/patches/priority_sort.rb +0 -21
  30. data/lib/commander/patches/validate_inputs.rb +0 -79
  31. data/lib/commander/platform.rb +0 -7
  32. data/spec/command_spec.rb +0 -157
  33. data/spec/configure_spec.rb +0 -37
  34. data/spec/core_ext/array_spec.rb +0 -18
  35. data/spec/core_ext/object_spec.rb +0 -19
  36. data/spec/help_formatters/terminal_compact_spec.rb +0 -69
  37. data/spec/help_formatters/terminal_spec.rb +0 -67
  38. data/spec/methods_spec.rb +0 -61
  39. data/spec/patches/validate_inputs_spec.rb +0 -84
  40. data/spec/runner_spec.rb +0 -672
  41. data/spec/spec_helper.rb +0 -79
  42. data/spec/ui_spec.rb +0 -30
@@ -0,0 +1,62 @@
1
+ require 'paint'
2
+
3
+ module Commander
4
+ ##
5
+ # Internal error class to delay rendering help text
6
+ # This is required as the help command points directly to stdout
7
+ # In general this has a bit of a code smell to it, and should
8
+ # not be used publicly
9
+ class InternalCallableError < StandardError
10
+ attr_accessor :callable
11
+
12
+ def initialize(msg = nil, &block)
13
+ super(msg)
14
+ self.callable = block
15
+ end
16
+
17
+ def call
18
+ callable.call if callable
19
+ end
20
+ end
21
+
22
+ ErrorHandler = Struct.new(:program_name, :trace) do
23
+ def parse_trace(*raw_args)
24
+ # Do not modify the original array
25
+ args = raw_args.dup
26
+
27
+ # Determines if there is a --trace flag before a --
28
+ trace_index = args.index do |a|
29
+ if a == '--trace'
30
+ self.trace = true
31
+ elsif a == '--'
32
+ break
33
+ else
34
+ false
35
+ end
36
+ end
37
+
38
+ # Removes the --trace flag if required
39
+ args.tap { |a| a.delete_at(trace_index) if trace_index }
40
+ end
41
+
42
+ def start
43
+ yield(self) if block_given?
44
+ rescue => e
45
+ $stderr.puts e.full_message if trace
46
+
47
+ error_msg = "#{Paint[program_name, '#2794d8']}: #{Paint[e.to_s, :red, :bright]}"
48
+ exit_code = e.respond_to?(:exit_code) ? e.exit_code.to_i : 1
49
+ case e
50
+ when InternalCallableError
51
+ # See: https://shapeshed.com/unix-exit-codes/
52
+ exit_code = 126
53
+ $stderr.puts error_msg
54
+ e.call
55
+ else
56
+ $stderr.puts error_msg
57
+ end
58
+ exit(exit_code)
59
+ end
60
+ end
61
+ end
62
+
@@ -2,16 +2,12 @@ module Commander
2
2
  module HelpFormatter
3
3
  autoload :Base, 'commander/help_formatters/base'
4
4
  autoload :Terminal, 'commander/help_formatters/terminal'
5
- autoload :TerminalCompact, 'commander/help_formatters/terminal_compact'
6
5
 
7
6
  class Context
8
7
  def initialize(target)
9
8
  @target = target
10
9
  end
11
10
 
12
- # NOTE: `get_binding` has been stubbed! This version will be ignored
13
- # See patch for actual version
14
- prepend Patches::HelpFormatterBinding
15
11
  def get_binding
16
12
  @target.instance_eval { binding }.tap do |bind|
17
13
  decorate_binding(bind)
@@ -11,11 +11,6 @@ module Commander
11
11
  template(:command_help).result(Context.new(command).get_binding)
12
12
  end
13
13
 
14
- def render_subcommand(command)
15
- bind = ProgramContext.new(@runner).get_binding({cmd: command})
16
- template(:subcommand_help).result(bind)
17
- end
18
-
19
14
  def template(name)
20
15
  ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal', "#{name}.erb")), nil, '-')
21
16
  end
@@ -22,14 +22,22 @@
22
22
  <%= command %>
23
23
  <% end -%>
24
24
  <% end -%>
25
- <% unless @options.empty? -%>
25
+ <% unless slop.options.empty? -%>
26
26
 
27
27
  <%= $terminal.color "OPTIONS", :bold %>:
28
- <% for option in @options -%>
29
-
30
- <%= option[:switches].join ', ' %>
31
- <%= Commander::HelpFormatter.indent 8, option[:description] %><% if option[:default] %>
32
- <%= $terminal.color "Default", :bold %>: <%= option[:default] %><% end %>
28
+ <% slop.options.each do |option| -%>
29
+
30
+ <% tag = if [Slop::BoolOption, Slop::NullOption].include?(option.class)
31
+ nil
32
+ elsif meta = option.config[:meta]
33
+ meta
34
+ else
35
+ option.key.upcase
36
+ end
37
+ -%>
38
+ <%= option.flags.join ', ' %> <%= tag %>
39
+ <%= Commander::HelpFormatter.indent 8, option.desc %><% if option.default_value %>
40
+ <%= $terminal.color "Default", :bold %>: <%= option.default_value %><% end %>
33
41
  <% end -%>
34
42
  <% end -%>
35
43
 
@@ -28,12 +28,20 @@
28
28
  <%= "%-#{max_aliases_length}s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] -%>
29
29
  <% end %>
30
30
  <% end %>
31
- <% unless @help_options.empty? -%>
31
+ <% unless global_slop.options.empty? -%>
32
32
  <%= $terminal.color "GLOBAL OPTIONS", :bold %>:
33
- <% for option in @help_options -%>
33
+ <% global_slop.options.each do |global| -%>
34
34
 
35
- <%= option[:switches].join ', ' %>
36
- <%= option[:description] %>
35
+ <% tag = if [Slop::BoolOption, Slop::NullOption]
36
+ nil
37
+ elsif meta = option.config[:meta]
38
+ meta
39
+ else
40
+ option.key.upcase
41
+ end
42
+ -%>
43
+ <%= global.flags.join ', ' %> <%= tag %>
44
+ <%= Commander::HelpFormatter.indent 8, global.desc %>
37
45
  <% end -%>
38
46
  <% end -%>
39
47
  <% if program :help -%>
@@ -1,4 +1,4 @@
1
- require 'paint'
1
+ require 'ostruct'
2
2
 
3
3
  module Commander
4
4
  class Runner
@@ -14,9 +14,8 @@ module Commander
14
14
  attr_reader :commands
15
15
 
16
16
  ##
17
- # Global options.
18
-
19
- attr_reader :options
17
+ # The global Slop Options
18
+ attr_reader :global_slop
20
19
 
21
20
  ##
22
21
  # Hash of help formatter aliases.
@@ -33,8 +32,8 @@ module Commander
33
32
 
34
33
  def initialize(*inputs)
35
34
  @program, @commands, @default_command, \
36
- @options, @aliases, @args = inputs.map(&:dup)
37
- @args.reject! { |a| a == '--trace' }
35
+ @global_slop, @aliases, @args = inputs.map(&:dup)
36
+
38
37
  @commands['help'] ||= Command.new('help').tap do |c|
39
38
  c.syntax = "#{program(:name)} help [command]"
40
39
  c.description = 'Display global or [command] help documentation'
@@ -49,15 +48,58 @@ module Commander
49
48
 
50
49
  ##
51
50
  # Run command parsing and execution process
52
- # NOTE: This method does not have error handling, see: run!
51
+ INBUILT_ERRORS = [
52
+ OptionParser::InvalidOption,
53
+ Command::CommandUsageError,
54
+ InvalidCommandError
55
+ ]
53
56
 
54
57
  def run
55
58
  require_program :version, :description
56
59
 
57
- parse_global_options
58
- remove_global_options options, @args
60
+ # Determine where the arguments/ options start
61
+ remaining_args = if alias? command_name_from_args
62
+ @aliases[command_name_from_args.to_s] + args_without_command_name
63
+ else
64
+ args_without_command_name
65
+ end
59
66
 
60
- run_active_command
67
+ # Combines the global and command options into a single parser
68
+ global_opts = global_slop.options
69
+ command_opts = active_command? ? active_command.slop.options : []
70
+ opts = [*global_opts, *command_opts]
71
+ parser = Slop::Parser.new(opts)
72
+
73
+ # Parsers the arguments/opts and fetches the config
74
+ parser.parse(remaining_args)
75
+ opts = OpenStruct.new parser.parse(remaining_args).to_h
76
+ remaining_args = parser.arguments
77
+ config = program(:config).dup
78
+
79
+ if opts.version
80
+ # Return the version
81
+ say version
82
+ exit 0
83
+ elsif opts.help && active_command?
84
+ # Return help for the active_command
85
+ run_help_command([active_command!.name])
86
+ elsif active_command?
87
+ # Run the active_command
88
+ active_command.run!(remaining_args, opts, config)
89
+ else
90
+ # Return generic help
91
+ run_help_command('')
92
+ end
93
+ rescue => original
94
+ error = original
95
+ if INBUILT_ERRORS.include?(error.class)
96
+ error = InternalCallableError.new(error.message) do
97
+ $stderr.puts "\nUsage:\n\n"
98
+ name = active_command? ? active_command.name : :error
99
+ run_help_command([name])
100
+ end
101
+ end
102
+ raise error
61
103
  end
62
104
 
63
105
  ##
@@ -100,20 +142,39 @@ module Commander
100
142
  @aliases.include? name.to_s
101
143
  end
102
144
 
103
- ##
104
- # Check if a command _name_ exists.
105
-
106
- def command_exists?(name)
107
- @commands[name.to_s]
108
- end
109
-
110
145
  #:stopdoc:
111
146
 
112
147
  ##
113
148
  # Get active command within arguments passed to this runner.
149
+ # It will try an run the default if arguments have been provided
150
+ # It can not run a default command that is flags-only
151
+ # This is to provide consistent behaviour to --help
152
+ #
114
153
 
115
154
  def active_command
116
- @__active_command ||= command(command_name_from_args)
155
+ @__active_command ||= begin
156
+ if named_command = command(command_name_from_args)
157
+ named_command
158
+ elsif default_command? && flagless_args_string.length > 0
159
+ default_command
160
+ end
161
+ end
162
+ end
163
+
164
+ def active_command!
165
+ active_command.tap { |c| require_valid_command(c) }
166
+ end
167
+
168
+ def active_command?
169
+ active_command ? true : false
170
+ end
171
+
172
+ def default_command
173
+ @__default_command ||= command(@default_command)
174
+ end
175
+
176
+ def default_command?
177
+ default_command ? true : false
117
178
  end
118
179
 
119
180
  ##
@@ -121,15 +182,20 @@ module Commander
121
182
  # Supports multi-word commands, using the largest possible match.
122
183
 
123
184
  def command_name_from_args
124
- @__command_name_from_args ||= (valid_command_names_from(*@args.dup).sort.last || @default_command)
185
+ @__command_name_from_args ||= valid_command_names_from(*@args.dup).sort.last
186
+ end
187
+
188
+ def flagless_args_string
189
+ @flagless_args_string ||= @args.reject { |value| value =~ /^-/ }.join ' '
125
190
  end
126
191
 
127
192
  ##
128
193
  # Returns array of valid command names found within _args_.
129
194
 
130
195
  def valid_command_names_from(*args)
131
- arg_string = args.delete_if { |value| value =~ /^-/ }.join ' '
132
- commands.keys.find_all { |name| name if arg_string =~ /^#{name}\b/ }
196
+ commands.keys.find_all do |name|
197
+ name if flagless_args_string =~ /^#{name}(?![[:graph:]])/
198
+ end
133
199
  end
134
200
 
135
201
  ##
@@ -145,7 +211,7 @@ module Commander
145
211
  def args_without_command_name
146
212
  removed = []
147
213
  parts = command_name_from_args.split rescue []
148
- @args.dup.delete_if do |arg|
214
+ @args.reject do |arg|
149
215
  removed << arg if parts.include?(arg) && !removed.include?(arg)
150
216
  end
151
217
  end
@@ -155,37 +221,17 @@ module Commander
155
221
 
156
222
  def help_formatter_alias_defaults
157
223
  {
158
- compact: HelpFormatter::TerminalCompact,
159
224
  }
160
225
  end
161
226
 
162
- ##
163
- # Limit commands to those which are subcommands of the one that is active
164
- def limit_commands_to_subcommands(command)
165
- commands.select! do |other_sym, _|
166
- other = other_sym.to_s
167
- # Do not match sub-sub commands (matches for a second space)
168
- if /\A#{command.name}\s.*\s/.match?(other)
169
- false
170
- # Do match regular sub commands
171
- elsif /\A#{command.name}\s/.match?(other)
172
- true
173
- # Do not match any other commands
174
- else
175
- false
176
- end
177
- end
178
- end
179
-
180
227
  ##
181
228
  # Creates default commands such as 'help' which is
182
229
  # essentially the same as using the --help switch.
183
230
  def run_help_command(args)
184
231
  UI.enable_paging if program(:help_paging)
185
- @help_commands = @commands.dup
232
+ @help_commands = @commands.reject { |_, v| v.hidden(false) }.to_h
186
233
  if args.empty? || args[0] == :error
187
- @help_options = @options
188
- @help_commands.reject! { |k, v| !!v.hidden }
234
+ @help_options = []
189
235
  old_wrap = $terminal.wrap_at
190
236
  $terminal.wrap_at = nil
191
237
  program(:nobanner, true) if args[0] == :error
@@ -194,54 +240,17 @@ module Commander
194
240
  else
195
241
  command = command args.join(' ')
196
242
  require_valid_command command
197
- if command.sub_command_group?
198
- limit_commands_to_subcommands(command)
199
- say help_formatter.render_subcommand(command)
200
- else
201
- say help_formatter.render_command(command)
202
- end
243
+ say help_formatter.render_command(command)
203
244
  end
204
245
  end
205
246
 
206
247
  ##
207
248
  # Raises InvalidCommandError when a _command_ is not found.
208
249
 
209
- def require_valid_command(command = active_command)
250
+ def require_valid_command(command)
210
251
  fail InvalidCommandError, 'invalid command', caller if command.nil?
211
252
  end
212
253
 
213
- ##
214
- # Removes global _options_ from _args_. This prevents an invalid
215
- # option error from occurring when options are parsed
216
- # again for the command.
217
-
218
- def remove_global_options(options, args)
219
- # TODO: refactor with flipflop, please TJ ! have time to refactor me !
220
- options.each do |option|
221
- switches = option[:switches].dup
222
- next if switches.empty?
223
-
224
- if (switch_has_arg = switches.any? { |s| s =~ /[ =]/ })
225
- switches.map! { |s| s[0, s.index('=') || s.index(' ') || s.length] }
226
- end
227
-
228
- switches = expand_optionally_negative_switches(switches)
229
-
230
- past_switch, arg_removed = false, false
231
- args.delete_if do |arg|
232
- if switches.any? { |s| s == arg }
233
- arg_removed = !switch_has_arg
234
- past_switch = true
235
- elsif past_switch && !arg_removed && arg !~ /^-/
236
- arg_removed = true
237
- else
238
- arg_removed = true
239
- false
240
- end
241
- end
242
- end
243
- end
244
-
245
254
  # expand switches of the style '--[no-]blah' into both their
246
255
  # '--blah' and '--no-blah' variants, so that they can be
247
256
  # properly detected and removed
@@ -256,41 +265,6 @@ module Commander
256
265
  end
257
266
  end
258
267
 
259
- ##
260
- # Parse global command options.
261
-
262
- def parse_global_options
263
- parser = options.inject(OptionParser.new) do |options, option|
264
- options.on(*option[:args], &global_option_proc(option[:switches], &option[:proc]))
265
- end
266
-
267
- options = @args.dup
268
- begin
269
- parser.parse!(options)
270
- rescue OptionParser::InvalidOption => e
271
- # Remove the offending args and retry.
272
- options = options.reject { |o| e.args.include?(o) }
273
- retry
274
- end
275
- end
276
-
277
- ##
278
- # Returns a proc allowing for commands to inherit global options.
279
- # This functionality works whether a block is present for the global
280
- # option or not, so simple switches such as --verbose can be used
281
- # without a block, and used throughout all commands.
282
-
283
- def global_option_proc(switches, &block)
284
- lambda do |value|
285
- unless active_command.nil?
286
- active_command.proxy_options << [Runner.switch_to_sym(switches.last), value]
287
- end
288
- if block && !value.nil?
289
- instance_exec(value, &block)
290
- end
291
- end
292
- end
293
-
294
268
  ##
295
269
  # Raises a CommandError when the program any of the _keys_ are not present, or empty.
296
270
 
@@ -325,18 +299,6 @@ module Commander
325
299
  switch.scan(/[\-\]](\w+)/).join('_').to_sym rescue nil
326
300
  end
327
301
 
328
- ##
329
- # Run the active command.
330
-
331
- def run_active_command
332
- require_valid_command
333
- if alias? command_name_from_args
334
- active_command.run(*(@aliases[command_name_from_args.to_s] + args_without_command_name))
335
- else
336
- active_command.run(*args_without_command_name)
337
- end
338
- end
339
-
340
302
  def say(*args) #:nodoc:
341
303
  $terminal.say(*args)
342
304
  end