commander-openflighthpc 2.0.1 → 2.2.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.
- checksums.yaml +4 -4
- data/README.md +45 -138
- data/commander-openflighthpc.gemspec +2 -1
- data/lib/commander.rb +3 -4
- data/lib/commander/cli.rb +15 -76
- data/lib/commander/command.rb +81 -176
- data/lib/commander/error_handler.rb +62 -0
- data/lib/commander/help_formatters.rb +0 -4
- data/lib/commander/help_formatters/terminal.rb +0 -5
- data/lib/commander/help_formatters/terminal/command_help.erb +14 -6
- data/lib/commander/help_formatters/terminal/help.erb +12 -4
- data/lib/commander/runner.rb +92 -130
- data/lib/commander/version.rb +1 -1
- metadata +21 -46
- data/bin/commander +0 -104
- data/lib/commander/blank.rb +0 -7
- data/lib/commander/core_ext.rb +0 -2
- data/lib/commander/core_ext/array.rb +0 -24
- data/lib/commander/core_ext/object.rb +0 -8
- data/lib/commander/help_formatters/terminal/subcommand_help.erb +0 -23
- data/lib/commander/help_formatters/terminal_compact.rb +0 -11
- data/lib/commander/help_formatters/terminal_compact/command_help.erb +0 -26
- data/lib/commander/help_formatters/terminal_compact/help.erb +0 -29
- data/lib/commander/help_formatters/terminal_compact/subcommand_help.erb +0 -15
- data/lib/commander/import.rb +0 -5
- data/lib/commander/patches/decimal-integer.rb +0 -17
- data/lib/commander/patches/help_formatter_binding.rb +0 -15
- data/lib/commander/patches/implicit-short-tags.rb +0 -75
- data/lib/commander/patches/priority_sort.rb +0 -21
- data/lib/commander/patches/validate_inputs.rb +0 -79
- data/lib/commander/platform.rb +0 -7
- data/spec/command_spec.rb +0 -157
- data/spec/configure_spec.rb +0 -37
- data/spec/core_ext/array_spec.rb +0 -18
- data/spec/core_ext/object_spec.rb +0 -19
- data/spec/help_formatters/terminal_compact_spec.rb +0 -69
- data/spec/help_formatters/terminal_spec.rb +0 -67
- data/spec/methods_spec.rb +0 -61
- data/spec/patches/validate_inputs_spec.rb +0 -84
- data/spec/runner_spec.rb +0 -672
- data/spec/spec_helper.rb +0 -79
- data/spec/ui_spec.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b083e62e1f920ec5cbd706e7fda06f9ea7377de383b36a9778c5b191e63b9fc4
|
4
|
+
data.tar.gz: a6de7d045ea35b3fefd4cc2ca77e64cd4bb5bd29e39eace7cd9047c372e684a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 (
|
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
|
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
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
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
|
-
|
118
|
-
extend Commander::CLI
|
91
|
+
### Option Parsing
|
119
92
|
|
120
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
141
|
-
|
104
|
+
# Interger Value
|
105
|
+
c.slop.integer '--integer-value', 'Takes the input and type casts it to an integer'
|
142
106
|
|
143
|
-
|
144
|
-
|
145
|
-
c.option '--
|
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
|
-
|
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
|
-
|
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
|
-
|
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', '
|
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::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
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
data/lib/commander/command.rb
CHANGED
@@ -1,60 +1,37 @@
|
|
1
|
-
require '
|
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
|
-
|
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
|
-
|
23
|
-
|
7
|
+
attr_accessor :name, :examples, :syntax, :description, :priority
|
8
|
+
attr_accessor :summary, :options
|
24
9
|
|
25
10
|
##
|
26
|
-
#
|
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
|
-
|
36
|
-
|
37
|
-
|
13
|
+
def initialize(name)
|
14
|
+
@name, @examples, @when_called = name.to_s, [], []
|
15
|
+
@options = []
|
16
|
+
end
|
38
17
|
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
44
|
-
|
45
|
-
end
|
24
|
+
# Sort firstly based on the commands priority
|
25
|
+
comp = (self.priority || 0) <=> (other.priority || 0)
|
46
26
|
|
47
|
-
|
48
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
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
|
-
#
|
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
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
-
#
|
164
|
-
#
|
107
|
+
# Flags the command not to appear in general help text
|
108
|
+
#
|
165
109
|
|
166
|
-
def
|
167
|
-
|
110
|
+
def hidden(set = true)
|
111
|
+
@hidden ||= set
|
168
112
|
end
|
169
113
|
|
170
|
-
def
|
171
|
-
|
172
|
-
self.action { raise SubCommandGroupError } if value
|
114
|
+
def inspect
|
115
|
+
"<Commander::Command:#{name}>"
|
173
116
|
end
|
174
117
|
|
175
|
-
def
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
-
|
190
|
-
|
191
|
-
|
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
|
-
|
136
|
+
def primary_command_word
|
137
|
+
name.split.last
|
138
|
+
end
|
222
139
|
|
223
|
-
def
|
224
|
-
|
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
|
-
|
237
|
-
|
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
|
240
|
-
|
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
|
-
|
250
|
-
|
251
|
-
# and work with option values.
|
154
|
+
def required_argument_count
|
155
|
+
total_argument_count - optional_argument_count
|
156
|
+
end
|
252
157
|
|
253
|
-
def
|
254
|
-
|
158
|
+
def too_many_args?(args)
|
159
|
+
!variable_arg? && args.length > total_argument_count
|
255
160
|
end
|
256
161
|
|
257
|
-
def
|
258
|
-
|
162
|
+
def too_few_args?(args)
|
163
|
+
args.length < required_argument_count
|
259
164
|
end
|
260
165
|
end
|
261
166
|
end
|