commander-openflighthpc 2.0.2 → 2.1.0

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -87
  3. data/commander-openflighthpc.gemspec +1 -0
  4. data/lib/commander.rb +3 -4
  5. data/lib/commander/cli.rb +15 -76
  6. data/lib/commander/command.rb +89 -175
  7. data/lib/commander/error_handler.rb +72 -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 +12 -6
  11. data/lib/commander/help_formatters/terminal/help.erb +10 -4
  12. data/lib/commander/runner.rb +98 -129
  13. data/lib/commander/version.rb +1 -1
  14. metadata +19 -44
  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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a830a7be0e3333c0ae748dac3a27d0dfc5065790a0b4cbe15e750ca9956d391a
4
- data.tar.gz: 1e2f63e323e25e41f78ae4991ddc0813cc03f6d4af3d9c41f99b92b1b8286f07
3
+ metadata.gz: 2de2c466191b2e6eec17147fa3f65c84910946e26986eac3b2b0542dc339ae6f
4
+ data.tar.gz: b4e6443b1c09467439509b0661d4c43c6c7c324d62d900ed111a41431d8cc742
5
5
  SHA512:
6
- metadata.gz: b098f2e2c8be6c35cb026a0b0e77a7afc87b8f1132b31bef900237d48c43cce9d37690e98b48829fa76e2bb4dd291d19ae6d1ff82e70f2b42cdf52c530955f27
7
- data.tar.gz: 255fcf43aa072382f0448cd49c177868e95e51607193972fcfcdbd7d8cd57f9111300fc9612416afaa74ac62aa58cce461c2ce43e33755473227872f52fc4612
6
+ metadata.gz: b5c5216978964ac2a4e100cd37d89835ca432372ee57f760f1f9d940d4319c2d3579f980d0adf6bbd9ef77c8de1c6b9476f1c4ac2f0e504a89e84f53d98c1086
7
+ data.tar.gz: 8b2650058b67db6b92d9ec19d924e171004019a6ddc730ed34f35144613bfd101509e45461b882e7213bcfcbd9899295cc10d32f5a2a76e24ac79b522d007ea8
data/README.md CHANGED
@@ -12,8 +12,6 @@ project that support the OpenFlightHPC tools.
12
12
  To ship a new release, do the following:
13
13
 
14
14
  * Increment the version number in `version.rb`
15
- * Run `rake build`
16
- * Run `rake release`
17
15
 
18
16
  Documentation from the upstream project follows.
19
17
 
@@ -23,42 +21,26 @@ Documentation from the upstream project follows.
23
21
 
24
22
  The complete solution for Ruby command-line executables.
25
23
  Commander bridges the gap between other terminal related libraries
26
- you know and love (OptionParser, HighLine), while providing many new
24
+ you know and love (Slop, HighLine), while providing many new
27
25
  features, and an elegant API.
28
26
 
29
27
  ## Features
30
28
 
31
- * Easier than baking cookies
32
- * Parses options using OptionParser
33
- * Auto-populates struct with options ( no more `{ |v| options[:recursive] = v }` )
34
29
  * Auto-generates help documentation via pluggable help formatters
35
30
  * Optional default command when none is present
36
31
  * Global / Command level options
37
- * Packaged with two help formatters (Terminal, TerminalCompact)
32
+ * Packaged with one help formatters (Terminal)
38
33
  * Imports the highline gem for interacting with the terminal
39
34
  * Adds additional user interaction functionality
40
35
  * Highly customizable progress bar with intuitive, simple usage
41
- * Multi-word command name support such as `drupal module install MOD`, rather than `drupal module_install MOD`
42
36
  * Sexy paging for long bodies of text
43
- * Support for MacOS text-to-speech
44
37
  * Command aliasing (very powerful, as both switches and arguments can be used)
45
- * Growl notification support for MacOS
46
38
  * Use the `commander` executable to initialize a commander driven program
47
39
 
48
40
  ## Installation
49
41
 
50
42
  $ gem install commander
51
43
 
52
- ## Quick Start
53
-
54
- To generate a quick template for a commander app, run:
55
-
56
- $ commander init yourfile.rb
57
-
58
- To generate a quick modular style template for a commander app, run:
59
-
60
- $ commander init --modular yourfile.rb
61
-
62
44
  ## Example
63
45
 
64
46
  For more option examples view the `Commander::Command#option` method. Also
@@ -87,8 +69,8 @@ end
87
69
  command :bar do |c|
88
70
  c.syntax = 'foobar bar [options]'
89
71
  c.description = 'Display bar with optional prefix and suffix'
90
- c.option '--prefix STRING', String, 'Adds a prefix to bar'
91
- c.option '--suffix STRING', String, 'Adds a suffix to bar'
72
+ c.option '--prefix STRING', 'Adds a prefix to bar'
73
+ c.option '--suffix STRING', 'Adds a suffix to bar'
92
74
  c.action do |args, options|
93
75
  options.default :prefix => '(', :suffix => ')'
94
76
  say "#{options.prefix}bar#{options.suffix}"
@@ -142,8 +124,8 @@ to set defaults in a clean manner for options which have not been set.
142
124
 
143
125
  ```ruby
144
126
  command :foo do |c|
145
- c.option '--interval SECONDS', Integer, 'Interval in seconds'
146
- c.option '--timeout SECONDS', Integer, 'Timeout in seconds'
127
+ c.option '--interval SECONDS', 'Interval in seconds'
128
+ c.option '--timeout SECONDS', 'Timeout in seconds'
147
129
  c.action do |args, options|
148
130
  options.default \
149
131
  :interval => 2,
@@ -239,74 +221,12 @@ Which will output the rest of the help doc, along with:
239
221
 
240
222
  ### Global Options
241
223
 
242
- Although most switches will be at the command level, several are available by
243
- default at the global level, such as `--version`, and `--help`. Using
244
- `#global_option` you can add additional global options:
245
-
246
- ```ruby
247
- global_option('-c', '--config FILE', 'Load config data for your commands to use') { |file| ... }
248
- ```
249
-
250
- This method accepts the same syntax as `Commander::Command#option` so check it out for documentation.
251
-
252
- All global options regardless of providing a block are accessable at the command level. This
253
- means that instead of the following:
254
-
255
- ```ruby
256
- global_option('--verbose') { $verbose = true }
257
- ...
258
- c.action do |args, options|
259
- say 'foo' if $verbose
260
- ...
261
- ```
262
-
263
- You may:
264
-
265
- ```ruby
266
- global_option '--verbose'
267
- ...
268
- c.action do |args, options|
269
- say 'foo' if options.verbose
270
- ...
271
- ```
272
-
273
- ### Formatters
274
-
275
- Two core formatters are currently available, the default `Terminal` formatter
276
- as well as `TerminalCompact`. To utilize a different formatter simply use
277
- `:help_formatter` like below:
278
-
279
- ```ruby
280
- program :help_formatter, Commander::HelpFormatter::TerminalCompact
281
- ```
282
-
283
- Or utilize the help formatter aliases:
284
-
285
- ```ruby
286
- program :help_formatter, :compact
287
- ```
288
-
289
- This abstraction could be utilized to generate HTML documentation for your executable.
224
+ WIP: Update globals behaviour
290
225
 
291
226
  ### Tracing
292
227
 
293
228
  WIP: Update OpenFlight --trace behaviour
294
229
 
295
- ## Tips
296
-
297
- When adding a global or command option, `OptionParser` no longer implicitly adds a small
298
- switch unless explicitly stated.
299
-
300
- ## ASCII Tables
301
-
302
- For feature rich ASCII tables for your terminal app check out the terminal-table gem at http://github.com/tj/terminal-table
303
-
304
- +----------+-------+----+--------+-----------------------+
305
- | Terminal | Table | Is | Wicked | Awesome |
306
- +----------+-------+----+--------+-----------------------+
307
- | | | | | get it while its hot! |
308
- +----------+-------+----+--------+-----------------------+
309
-
310
230
  ## Running Specifications
311
231
 
312
232
  $ rake spec
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ['lib']
20
20
 
21
21
  s.add_runtime_dependency('highline', '> 1.7.2')
22
+ s.add_runtime_dependency('slop', '~> 4.8')
22
23
  s.add_runtime_dependency('paint', '~> 2.1.0')
23
24
 
24
25
  s.add_development_dependency('rspec', '~> 3.2')
data/lib/commander.rb CHANGED
@@ -23,11 +23,10 @@
23
23
 
24
24
  require 'highline'
25
25
  $terminal = HighLine.new
26
+
27
+ require 'commander/error_handler'
26
28
  require 'commander/version'
27
- require 'commander/blank'
28
- require 'commander/core_ext'
29
- require 'commander/runner'
30
29
  require 'commander/command'
30
+ require 'commander/runner'
31
31
  require 'commander/help_formatters'
32
- require 'commander/platform'
33
32
  require 'commander/cli'
data/lib/commander/cli.rb CHANGED
@@ -1,16 +1,21 @@
1
+ require 'slop'
2
+
1
3
  module Commander
2
4
  module CLI
3
5
  ##
4
6
  # Wrapper run command with error handling
5
7
  def run!(*args)
8
+ Commander.traceable_error_handler(*args) do |new_args|
9
+ run(*new_args)
10
+ end
11
+ end
12
+
13
+ def run(*args)
6
14
  instance = Runner.new(
7
15
  @program, commands, default_command,
8
- global_options, aliases, args
16
+ global_slop, aliases, args
9
17
  )
10
18
  instance.run
11
- rescue StandardError, Interrupt => e
12
- $stderr.puts e.backtrace.reverse if args.include?('--trace')
13
- error_handler(instance, e)
14
19
  end
15
20
 
16
21
  ##
@@ -24,8 +29,6 @@ module Commander
24
29
  # program :description, 'Commander utility program.'
25
30
  # program :help, 'Copyright', '2008 TJ Holowaychuk'
26
31
  # program :help, 'Anything', 'You want'
27
- # program :help_formatter, :compact
28
- # program :help_formatter, Commander::HelpFormatter::TerminalCompact
29
32
  #
30
33
  # # Get data
31
34
  # program :name # => 'Commander'
@@ -35,6 +38,7 @@ module Commander
35
38
  # :version (required) Program version triple, ex: '0.0.1'
36
39
  # :description (required) Program description
37
40
  # :name Program name, defaults to basename of executable
41
+ # :config An optional argument to be passed into the action
38
42
  # :help_formatter Defaults to Commander::HelpFormatter::Terminal
39
43
  # :help Allows addition of arbitrary global help blocks
40
44
  # :help_paging Flag for toggling help paging
@@ -80,37 +84,14 @@ module Commander
80
84
 
81
85
  ##
82
86
  # Hash of Global Options
83
- def global_options
84
- @global_options ||= begin
85
- @global_options = [] # Allows Recursive - Refactor
86
- global_option('-h', '--help', 'Display help documentation') do
87
- args = @args - %w(-h --help)
88
- command(:help).run(*args)
89
- exit 0
90
- end
91
- global_option('--version', 'Display version information') do
92
- say version
93
- exit 0
94
- end
87
+ #
88
+ def global_slop
89
+ @global_slop ||= Slop::Options.new.tap do |slop|
90
+ slop.bool '-h', '--help', 'Display help documentation'
91
+ slop.bool '--version', 'Display version information'
95
92
  end
96
93
  end
97
94
 
98
- ##
99
- # Add a global option; follows the same syntax as Command#option
100
- # This would be used for switches such as --version
101
- # NOTE: --trace is special and does not appear in the help
102
- # It is intended for debugging purposes
103
-
104
- def global_option(*args, &block)
105
- switches, description = Runner.separate_switches_from_description(*args)
106
- global_options << {
107
- args: args,
108
- proc: block,
109
- switches: switches,
110
- description: description,
111
- }
112
- end
113
-
114
95
  ##
115
96
  # Define and get a command by name
116
97
  #
@@ -134,47 +115,5 @@ module Commander
134
115
  commands[alias_name.to_s] = command name
135
116
  aliases[alias_name.to_s] = args
136
117
  end
137
-
138
- def error_handler(runner, e)
139
- error_msg = "#{Paint[runner.program(:name), '#2794d8']}: #{Paint[e.to_s, :red, :bright]}"
140
- exit_code = e.respond_to?(:exit_code) ? e.exit_code.to_i : 1
141
- case e
142
- when OptionParser::InvalidOption,
143
- Commander::Runner::InvalidCommandError,
144
- Commander::Patches::CommandUsageError
145
- # Display the error message for a specific command. Most likely due to
146
- # invalid command syntax
147
- if cmd = runner.active_command
148
- $stderr.puts error_msg
149
- $stderr.puts "\nUsage:\n\n"
150
- runner.command('help').run(cmd.name)
151
- # Display the main app help text when called without `--help`
152
- elsif runner.args_without_command_name.empty?
153
- $stderr.puts "Usage:\n\n"
154
- runner.command('help').run(:error)
155
- # Display the main app help text when called with arguments. Mostly
156
- # likely an invalid syntax error
157
- else
158
- $stderr.puts error_msg
159
- $stderr.puts "\nUsage:\n\n"
160
- runner.command('help').run(:error)
161
- end
162
- exit_code = 254
163
- # Display the help text for sub command groups when called without `--help`
164
- when SubCommandGroupError
165
- if cmd = runner.active_command
166
- $stderr.puts "Usage:\n\n"
167
- runner.command('help').run(cmd.name)
168
- end
169
- exit_code = 254
170
- when Interrupt
171
- $stderr.puts 'Received Interrupt!'
172
- exit_code = 255
173
- # Catch all error message for all other issues
174
- else
175
- $stderr.puts error_msg
176
- end
177
- exit(exit_code)
178
- end
179
118
  end
180
119
  end
@@ -1,60 +1,37 @@
1
- require 'optparse'
2
- require 'commander/patches/implicit-short-tags'
3
- require 'commander/patches/decimal-integer'
4
- require 'commander/patches/validate_inputs'
5
- require 'commander/patches/help_formatter_binding'
6
- require 'commander/patches/priority_sort'
7
-
8
- OptionParser.prepend Commander::Patches::ImplicitShortTags
9
- OptionParser.prepend Commander::Patches::DecimalInteger
1
+ require 'slop'
10
2
 
11
3
  module Commander
12
- class SubCommandGroupError < StandardError; end
13
-
14
4
  class Command
15
- prepend Patches::ValidateInputs
16
- prepend Patches::PrioritySort
17
-
18
- attr_accessor :name, :examples, :syntax, :description
19
- attr_accessor :summary, :proxy_options, :options, :hidden
20
- attr_reader :sub_command_group
5
+ class CommandUsageError < StandardError; end
21
6
 
22
- alias sub_command= hidden=
23
- alias sub_command hidden
7
+ attr_accessor :name, :examples, :syntax, :description, :priority
8
+ attr_accessor :summary, :options
24
9
 
25
10
  ##
26
- # Options struct.
27
-
28
- class Options
29
- include Blank
30
-
31
- def initialize
32
- @table = {}
33
- end
11
+ # Initialize new command with specified _name_.
34
12
 
35
- def __hash__
36
- @table
37
- end
13
+ def initialize(name)
14
+ @name, @examples, @when_called = name.to_s, [], []
15
+ @options = []
16
+ end
38
17
 
39
- def method_missing(meth, *args)
40
- meth.to_s =~ /=$/ ? @table[meth.to_s.chop.to_sym] = args.first : @table[meth]
41
- end
18
+ # Allows the commands to be sorted via priority
19
+ def <=>(other)
20
+ # Different classes can not be compared and thus are considered
21
+ # equal in priority
22
+ return 0 unless self.class == other.class
42
23
 
43
- def default(defaults = {})
44
- @table = defaults.merge! @table
45
- end
24
+ # Sort firstly based on the commands priority
25
+ comp = (self.priority || 0) <=> (other.priority || 0)
46
26
 
47
- def inspect
48
- "<Commander::Command::Options #{ __hash__.map { |k, v| "#{k}=#{v.inspect}" }.join(', ') }>"
49
- end
27
+ # Fall back on name comparison if priority is equal
28
+ comp == 0 ? self.name <=> other.name : comp
50
29
  end
51
30
 
52
- ##
53
- # Initialize new command with specified _name_.
54
-
55
- def initialize(name)
56
- @name, @examples, @when_called = name.to_s, [], []
57
- @options, @proxy_options = [], []
31
+ def run!(args, opts, config)
32
+ assert_correct_number_of_args!(args)
33
+ callee = @when_called.dup
34
+ callee.shift&.send(callee.shift || :call, args, opts, config)
58
35
  end
59
36
 
60
37
  ##
@@ -77,59 +54,32 @@ module Commander
77
54
  ##
78
55
  # Add an option.
79
56
  #
80
- # Options are parsed via OptionParser so view it
81
- # for additional usage documentation. A block may optionally be
82
- # passed to handle the option, otherwise the _options_ struct seen below
83
- # contains the results of this option. This handles common formats such as:
84
- #
85
- # -h, --help options.help # => bool
86
- # --[no-]feature options.feature # => bool
87
- # --large-switch options.large_switch # => bool
88
- # --file FILE options.file # => file passed
89
- # --list WORDS options.list # => array
90
- # --date [DATE] options.date # => date or nil when optional argument not set
91
- #
92
- # === Examples
93
- #
94
- # command :something do |c|
95
- # c.option '--recursive', 'Do something recursively'
96
- # c.option '--file FILE', 'Specify a file'
97
- # c.option('--info', 'Display info') { puts "handle with block" }
98
- # c.option '--[no-]feature', 'With or without feature'
99
- # c.option '--list FILES', Array, 'List the files specified'
100
- #
101
- # c.when_called do |args, options|
102
- # do_something_recursively if options.recursive
103
- # do_something_with_file options.file if options.file
104
- # end
105
- # end
106
- #
107
- # === Help Formatters
108
- #
109
- # This method also parses the arguments passed in order to determine
110
- # which were switches, and which were descriptions for the
111
- # option which can later be used within help formatters
112
- # using option[:switches] and option[:description].
113
- #
114
- # === Input Parsing
115
- #
116
- # Since Commander utilizes OptionParser you can pre-parse and evaluate
117
- # option arguments. Simply require 'optparse/time', or 'optparse/date', as these
118
- # objects must respond to #parse.
119
- #
120
- # c.option '--time TIME', Time
121
- # c.option '--date [DATE]', Date
57
+ # This is the legacy `option` method which now wraps `slop`
122
58
  #
123
59
 
124
60
  def option(*args, default: nil, &block)
61
+ # Split the description from the switchers
125
62
  switches, description = Runner.separate_switches_from_description(*args)
126
- proc = block || option_proc(switches)
127
- @options << {
128
- args: args,
129
- proc: proc,
130
- switches: switches,
131
- description: description
132
- }.tap { |o| o[:default] = default unless default.nil? }
63
+
64
+ # Other switches are normally short tags and something like below
65
+ # In this case the VALUE needs to be ignored
66
+ # -k VALUE
67
+ other_switches = switches.dup.tap(&:pop).map do |string|
68
+ string.split(' ').first
69
+ end
70
+
71
+ long_switch, meta = switches.last.split(' ', 2)
72
+
73
+ # The meta flag is the VALUE from denotes if its a boolean or
74
+ # string method
75
+ method = meta.nil? ? :bool : :string
76
+
77
+ # Adds the option to Slop
78
+ slop.send(method, *other_switches, long_switch, description, default: default)
79
+ end
80
+
81
+ def slop
82
+ @slop ||= Slop::Options.new
133
83
  end
134
84
 
135
85
  ##
@@ -139,16 +89,10 @@ module Commander
139
89
  # === Examples
140
90
  #
141
91
  # # Simple block handling
142
- # c.when_called do |args, options|
92
+ # c.when_called do |args, options, config|
143
93
  # # do something
144
94
  # end
145
95
  #
146
- # # Create inst of Something and pass args / options
147
- # c.when_called MyLib::Command::Something
148
- #
149
- # # Create inst of Something and use arbitrary method
150
- # c.when_called MyLib::Command::Something, :some_method
151
- #
152
96
  # # Pass an object to handle callback (requires method symbol)
153
97
  # c.when_called SomeObject, :some_method
154
98
  #
@@ -160,102 +104,72 @@ module Commander
160
104
  alias action when_called
161
105
 
162
106
  ##
163
- # Handles displaying subcommand help. By default it will set the action to
164
- # display the subcommand if the action hasn't already been set
165
-
166
- def sub_command_group?
167
- !!@sub_command_group
168
- end
169
-
170
- def sub_command_group=(value)
171
- @sub_command_group = value
172
- self.action { raise SubCommandGroupError } if value
173
- end
107
+ # Causes the option parsing to be skipped. The flags will be passed
108
+ # down within the args instead
109
+ #
174
110
 
175
- def configure_sub_command(runner)
176
- @sub_command_group = true
177
- if @when_called.empty?
178
- action do |args, opts|
179
- unless args.empty?
180
- raise Commander::Runner::InvalidCommandError,
181
- "unrecognized subcommand '#{args[0]}'"
182
- end
183
- runner.command('help').run(ARGV[0])
184
- end
185
- end
111
+ def skip_option_parsing(set = true)
112
+ @skip_option_parsing ||= set
186
113
  end
187
114
 
188
115
  ##
189
- # Run the command with _args_.
190
- #
191
- # * parses options, call option blocks
192
- # * invokes when_called proc
116
+ # Flags the command not to appear in general help text
193
117
  #
194
118
 
195
- def run(*args)
196
- call parse_options_and_call_procs(*args)
119
+ def hidden(set = true)
120
+ @hidden ||= set
197
121
  end
198
122
 
199
- #:stopdoc:
200
-
201
- ##
202
- # Parses options and calls associated procs,
203
- # returning the arguments remaining.
123
+ def inspect
124
+ "<Commander::Command:#{name}>"
125
+ end
204
126
 
205
- def parse_options_and_call_procs(*args)
206
- opt = @options.each_with_object(OptionParser.new) do |option, opts|
207
- opts.on(*option[:args], &option[:proc])
208
- opts
127
+ def assert_correct_number_of_args!(args)
128
+ return if primary_command_word == 'help'
129
+ too_many = too_many_args?(args)
130
+ if too_many
131
+ raise CommandUsageError, "excess arguments for command '#{primary_command_word}'"
132
+ elsif too_few_args?(args)
133
+ raise CommandUsageError, "insufficient arguments for command '#{primary_command_word}'"
209
134
  end
210
- default_opt = @options.each_with_object([]) do |h, arr|
211
- if h.key?(:default)
212
- arr.push(h[:switches][0].split[0])
213
- arr.push(h[:default].to_s)
135
+ end
136
+
137
+ def syntax_parts
138
+ @syntax_parts ||= syntax.split.tap do |parts|
139
+ while part = parts.shift do
140
+ break if part == primary_command_word || parts.length == 0
214
141
  end
215
142
  end
216
- opt.parse! default_opt
217
- opt.parse! args
218
143
  end
219
144
 
220
- ##
221
- # Call the commands when_called block with _args_.
145
+ def primary_command_word
146
+ name.split.last
147
+ end
222
148
 
223
- def call(args = [])
224
- callee = @when_called.dup
225
- object = callee.shift
226
- meth = callee.shift || :call
227
- options = proxy_option_struct
228
- case object
229
- when Proc then object.call(args, options)
230
- when Class then meth != :call ? object.new.send(meth, args, options) : object.new(args, options)
231
- else object.send(meth, args, options) if object
232
- end
149
+ def total_argument_count
150
+ syntax_parts.length
233
151
  end
234
152
 
235
- ##
236
- # Creates an Options instance populated with the option values
237
- # collected by the #option_proc.
238
-
239
- def proxy_option_struct
240
- proxy_options.each_with_object(Options.new) do |(option, value), options|
241
- # options that are present will evaluate to true
242
- value = true if value.nil?
243
- options.__send__ :"#{option}=", value
244
- options
245
- end
153
+ def optional_argument_count
154
+ syntax_parts.select do |part|
155
+ part[0] == '[' && part[-1] == ']'
156
+ end.length
246
157
  end
247
158
 
248
- ##
249
- # Option proxy proc used when a block is not explicitly passed
250
- # via the #option method. This allows commander to auto-populate
251
- # and work with option values.
159
+ def variable_arg?
160
+ syntax_parts.any? {|part| part[-4..-1] == '...]' || part[-3..-1] == '...'}
161
+ end
252
162
 
253
- def option_proc(switches)
254
- ->(value) { proxy_options << [Runner.switch_to_sym(switches.last), value] }
163
+ def required_argument_count
164
+ total_argument_count - optional_argument_count
255
165
  end
256
166
 
257
- def inspect
258
- "<Commander::Command:#{name}>"
167
+ def too_many_args?(args)
168
+ !variable_arg? && args.length > total_argument_count
169
+ end
170
+
171
+ def too_few_args?(args)
172
+ args.length < required_argument_count
259
173
  end
260
174
  end
261
175
  end