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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3febc8843b2d974230cdb5b51f165e65cb268e2b42e193acbbb8de5bef54e107
4
- data.tar.gz: eb05690fcc2789518b3bcb78720d863fcab1447ed38677022f86802f0d7a579c
3
+ metadata.gz: b083e62e1f920ec5cbd706e7fda06f9ea7377de383b36a9778c5b191e63b9fc4
4
+ data.tar.gz: a6de7d045ea35b3fefd4cc2ca77e64cd4bb5bd29e39eace7cd9047c372e684a0
5
5
  SHA512:
6
- metadata.gz: 4090ea8ea6a812911cf32fbe3a018a422abc3c8ca72117c09ff1583d114529fa61bde7681e979a6bd7c1a793a7766531e15ff18877dc323f28b7f5e7dd0d10fc
7
- data.tar.gz: 3327644d36fba11879f83b62468d2ebb3dd9e35ef24c2996878eaa63347c099104860f8498cbc84d8bde3aefdccbcdb912956d6fe459ff9e91038fc4b40a8783
6
+ metadata.gz: f37c40606f5ae5d2a0d05a13d0564e2f74d0a303a813bbf11c5f09eb37bdc53bbfd2541534a73c4fa399a8498428abff8fc3edf94f647447916a996af4168e99
7
+ data.tar.gz: 76a7afbab9913ff565e3d745f49ee532fd11cf84ad8624e858f309cdec856a6fe58b20bb9499ed882e9d1c3701620f2e8cb2cb0683db1128dbe2235e4b6b6f86
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,77 +21,59 @@ 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
- For more option examples view the `Commander::Command#option` method. Also
65
- an important feature to note is that action may be a class to instantiate,
66
- as well as an object, specifying a method to call, so view the RDoc for more information.
67
-
68
- ### Classic style
69
-
70
46
  ```ruby
71
47
  require 'rubygems'
72
- require 'commander/import'
48
+ require 'commander'
73
49
 
50
+ class MyApplication
74
51
  # :name is optional, otherwise uses the basename of this executable
75
- program :name, 'Foo Bar'
76
- program :version, '1.0.0'
77
- program :description, 'Stupid command that prints foo or bar.'
78
-
79
- command :foo do |c|
80
- c.syntax = 'foobar foo'
81
- c.description = 'Displays foo'
82
- c.action do |args, options|
83
- say 'foo'
52
+ program :name, 'Foo Bar'
53
+ program :version, '1.0.0'
54
+ program :description, 'Stupid command that prints foo or bar.'
55
+
56
+ command :foo do |c|
57
+ c.syntax = 'foobar foo'
58
+ c.description = 'Displays foo'
59
+ c.action do |args, options|
60
+ say 'foo'
61
+ end
84
62
  end
85
- end
86
63
 
87
- command :bar do |c|
88
- c.syntax = 'foobar bar [options]'
89
- 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'
92
- c.action do |args, options|
93
- options.default :prefix => '(', :suffix => ')'
94
- say "#{options.prefix}bar#{options.suffix}"
64
+ command :bar do |c|
65
+ c.syntax = 'foobar bar [options]'
66
+ c.description = 'Display bar with optional prefix and suffix'
67
+ c.slop.string '--prefix', 'Adds a prefix to bar'
68
+ c.slop.string '--suffix', 'Adds a suffix to bar', meta: 'CUSTOM_META'
69
+ c.action do |args, options, config|
70
+ options.default :prefix => '(', :suffix => ')'
71
+ say "#{options.prefix}bar#{options.suffix}"
72
+ end
95
73
  end
96
74
  end
75
+
76
+ MyApplication.run!(ARGV) if $0 == __FILE__
97
77
  ```
98
78
 
99
79
  Example output:
@@ -106,49 +86,27 @@ $ foobar bar --suffix '}' --prefix '{'
106
86
  # => {bar}
107
87
  ```
108
88
 
109
- ### Modular style
110
-
111
- **NOTE:** Make sure to use `require 'commander'` rather than `require 'commander/import'`, otherwise Commander methods will still be imported into the global namespace.
112
-
113
- ```ruby
114
- require 'rubygems'
115
- require 'commander'
89
+ ## Commander Goodies
116
90
 
117
- class MyApplication
118
- extend Commander::CLI
91
+ ### Option Parsing
119
92
 
120
- program :name, 'Foo Bar'
121
- program :version, '1.0.0'
122
- program :description, 'Stupid command that prints foo or bar.'
93
+ Option parsing is done using [Simple Lightweight Option Parsing](https://github.com/leejarvis/slop) which provides a rich interface for different option types. The main three being:
123
94
 
124
- command :foo do |c|
125
- c.syntax = 'foobar foo'
126
- c.description = 'Displays foo'
127
- c.action do |args, options|
128
- say 'foo'
129
- end
130
- end
131
- end
132
-
133
- MyApplication.run!(ARGV) if $0 == __FILE__
134
95
  ```
96
+ command do |c|
97
+ # Boolean Flag
98
+ c.slop.bool '--boolean-flag', 'Sets the :boolean_flag option to true'
135
99
 
136
- ## Commander Goodies
137
-
138
- ### Option Defaults
100
+ # String Value
101
+ c.slop.string '--string-value', 'Takes a string from the command line'
102
+ c.slop.string '--flag', 'Sets the meta variable to META', meta: 'META'
139
103
 
140
- The options struct passed to `#action` provides a `#default` method, allowing you
141
- to set defaults in a clean manner for options which have not been set.
104
+ # Interger Value
105
+ c.slop.integer '--integer-value', 'Takes the input and type casts it to an integer'
142
106
 
143
- ```ruby
144
- command :foo do |c|
145
- c.option '--interval SECONDS', Integer, 'Interval in seconds'
146
- c.option '--timeout SECONDS', Integer, 'Timeout in seconds'
147
- c.action do |args, options|
148
- options.default \
149
- :interval => 2,
150
- :timeout => 60
151
- end
107
+ # Legacy syntax (boolean and string values only)
108
+ c.option '--legacy-bool', 'A boolean flag using the legacy syntax'
109
+ c.option '--legacy-string LEGACY_STRING', 'A string flag using the legacy syntax'
152
110
  end
153
111
  ```
154
112
 
@@ -239,74 +197,23 @@ Which will output the rest of the help doc, along with:
239
197
 
240
198
  ### Global Options
241
199
 
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:
200
+ Global options work in a similar way to command level options. They both are configured using `Slop`. Global options are available on all commands. They are configure on the `global_slop` directive.
245
201
 
246
- ```ruby
247
- global_option('-c', '--config FILE', 'Load config data for your commands to use') { |file| ... }
248
202
  ```
203
+ class MyApplication
204
+ program :name, 'Foo Bar'
249
205
 
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
- ```
206
+ ...
282
207
 
283
- Or utilize the help formatter aliases:
208
+ global_slop.string '--custom-global', 'Available on all commands'
209
+ end
284
210
 
285
- ```ruby
286
- program :help_formatter, :compact
287
211
  ```
288
212
 
289
- This abstraction could be utilized to generate HTML documentation for your executable.
290
-
291
213
  ### Tracing
292
214
 
293
215
  WIP: Update OpenFlight --trace behaviour
294
216
 
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
217
  ## Running Specifications
311
218
 
312
219
  $ rake spec
@@ -18,7 +18,8 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
19
  s.require_paths = ['lib']
20
20
 
21
- s.add_runtime_dependency('highline', '~> 1.7.2')
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')
@@ -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'
@@ -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::ErrorHandler.new(program(:name)).start do |handler|
9
+ run(*handler.parse_trace(*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,63 @@ 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
107
+ # Flags the command not to appear in general help text
108
+ #
165
109
 
166
- def sub_command_group?
167
- !!@sub_command_group
110
+ def hidden(set = true)
111
+ @hidden ||= set
168
112
  end
169
113
 
170
- def sub_command_group=(value)
171
- @sub_command_group = value
172
- self.action { raise SubCommandGroupError } if value
114
+ def inspect
115
+ "<Commander::Command:#{name}>"
173
116
  end
174
117
 
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
118
+ def assert_correct_number_of_args!(args)
119
+ return if primary_command_word == 'help'
120
+ too_many = too_many_args?(args)
121
+ if too_many
122
+ raise CommandUsageError, "excess arguments for command '#{primary_command_word}'"
123
+ elsif too_few_args?(args)
124
+ raise CommandUsageError, "insufficient arguments for command '#{primary_command_word}'"
185
125
  end
186
126
  end
187
127
 
188
- ##
189
- # Run the command with _args_.
190
- #
191
- # * parses options, call option blocks
192
- # * invokes when_called proc
193
- #
194
-
195
- def run(*args)
196
- call parse_options_and_call_procs(*args)
197
- end
198
-
199
- #:stopdoc:
200
-
201
- ##
202
- # Parses options and calls associated procs,
203
- # returning the arguments remaining.
204
-
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
209
- 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)
128
+ def syntax_parts
129
+ @syntax_parts ||= syntax.split.tap do |parts|
130
+ while part = parts.shift do
131
+ break if part == primary_command_word || parts.length == 0
214
132
  end
215
133
  end
216
- opt.parse! default_opt
217
- opt.parse! args
218
134
  end
219
135
 
220
- ##
221
- # Call the commands when_called block with _args_.
136
+ def primary_command_word
137
+ name.split.last
138
+ end
222
139
 
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
140
+ def total_argument_count
141
+ syntax_parts.length
233
142
  end
234
143
 
235
- ##
236
- # Creates an Options instance populated with the option values
237
- # collected by the #option_proc.
144
+ def optional_argument_count
145
+ syntax_parts.select do |part|
146
+ part[0] == '[' && part[-1] == ']'
147
+ end.length
148
+ end
238
149
 
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
150
+ def variable_arg?
151
+ syntax_parts.any? {|part| part[-4..-1] == '...]' || part[-3..-1] == '...'}
246
152
  end
247
153
 
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.
154
+ def required_argument_count
155
+ total_argument_count - optional_argument_count
156
+ end
252
157
 
253
- def option_proc(switches)
254
- ->(value) { proxy_options << [Runner.switch_to_sym(switches.last), value] }
158
+ def too_many_args?(args)
159
+ !variable_arg? && args.length > total_argument_count
255
160
  end
256
161
 
257
- def inspect
258
- "<Commander::Command:#{name}>"
162
+ def too_few_args?(args)
163
+ args.length < required_argument_count
259
164
  end
260
165
  end
261
166
  end