commander-openflighthpc 2.0.2 → 2.1.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 +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