slop 2.4.4 → 3.0.0.rc1
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.
- data/CHANGES.md +0 -15
- data/README.md +46 -206
- data/lib/slop.rb +399 -888
- data/lib/slop/commands.rb +154 -0
- data/lib/slop/option.rb +149 -0
- data/slop.gemspec +1 -1
- data/test/commands_test.rb +58 -119
- data/test/option_test.rb +65 -159
- data/test/slop_test.rb +174 -497
- metadata +7 -5
data/CHANGES.md
CHANGED
@@ -1,18 +1,3 @@
|
|
1
|
-
2.4.4 (2012-02-07)
|
2
|
-
------------------
|
3
|
-
|
4
|
-
* Ensure options taking arguments to not parsing an argument which
|
5
|
-
is an option (#56)
|
6
|
-
* Ensure `:as => Range` returns a type of Range even if the value looks
|
7
|
-
like an Integer (#48)
|
8
|
-
|
9
|
-
2.4.3 (2012-01-16)
|
10
|
-
------------------
|
11
|
-
|
12
|
-
* Allow the `:as` option to accept an object responding to :call for
|
13
|
-
custom type conversions (#45)
|
14
|
-
* Ensure negative integers are not parsed as possible options (#46)
|
15
|
-
|
16
1
|
2.4.2 (2011-12-18)
|
17
2
|
------------------
|
18
3
|
|
data/README.md
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
Slop
|
2
2
|
====
|
3
3
|
|
4
|
-
Slop is a simple
|
5
|
-
|
6
|
-
Note that this is the `v2` branch. If you are looking for version 3 of Slop
|
7
|
-
please check out the [master branch](https://github.com/injekt/slop).
|
4
|
+
Slop is a simple option parser with an easy to remember syntax and friendly API.
|
8
5
|
|
9
6
|
Installation
|
10
7
|
------------
|
@@ -25,224 +22,67 @@ Usage
|
|
25
22
|
```ruby
|
26
23
|
# parse assumes ARGV, otherwise you can pass it your own Array
|
27
24
|
opts = Slop.parse do
|
28
|
-
|
29
|
-
on :
|
30
|
-
on :
|
31
|
-
on :
|
32
|
-
on '-D', '--debug', 'Enable debug' # The prefixed -'s are optional
|
33
|
-
end
|
34
|
-
|
35
|
-
# if ARGV is `-v --name 'lee jarvis' -s male`
|
36
|
-
opts.verbose? #=> true
|
37
|
-
opts.name? #=> true
|
38
|
-
opts[:name] #=> 'lee jarvis'
|
39
|
-
opts.age? #=> false
|
40
|
-
opts[:age] #=> nil
|
41
|
-
```
|
42
|
-
|
43
|
-
For more information about creating options, see the
|
44
|
-
[Creating Options](https://github.com/injekt/slop/wiki/Creating-Options---v2)
|
45
|
-
wiki page.
|
46
|
-
|
47
|
-
You can also return your options as a Hash
|
48
|
-
|
49
|
-
```ruby
|
50
|
-
opts.to_hash #=> { :name => 'Lee Jarvis', :verbose => true, :age => nil, :sex => 'male' }
|
51
|
-
```
|
52
|
-
|
53
|
-
If you want some pretty output for the user to see your options, you can just
|
54
|
-
send the Slop object to `puts` or use the `help` method.
|
55
|
-
|
56
|
-
```ruby
|
57
|
-
puts opts
|
58
|
-
puts opts.help
|
59
|
-
```
|
60
|
-
|
61
|
-
Will output something like
|
62
|
-
|
63
|
-
```
|
64
|
-
-v, --verbose Enable verbose mode
|
65
|
-
-n, --name Your name
|
66
|
-
-a, --age Your age
|
67
|
-
```
|
68
|
-
|
69
|
-
You can also add a banner using the `banner` method
|
70
|
-
|
71
|
-
```ruby
|
72
|
-
opts = Slop.parse do
|
73
|
-
banner "Usage: foo.rb [options]"
|
25
|
+
banner "ruby foo.rb [options]\n"
|
26
|
+
on :name=, 'Your name'
|
27
|
+
on :p, :password, 'Your password', :argument => :optional
|
28
|
+
on :v :verbose, 'Enable verbose mode'
|
74
29
|
end
|
75
|
-
```
|
76
|
-
|
77
|
-
Helpful Help
|
78
|
-
------------
|
79
|
-
|
80
|
-
Long form:
|
81
|
-
|
82
|
-
```ruby
|
83
|
-
Slop.parse do
|
84
|
-
...
|
85
|
-
on :h, :help, 'Print this help message', :tail => true do
|
86
|
-
puts help
|
87
|
-
exit
|
88
|
-
end
|
89
|
-
end
|
90
|
-
```
|
91
|
-
|
92
|
-
Shortcut:
|
93
30
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
31
|
+
# if ARGV is `--name Lee -v`
|
32
|
+
opts.verbose? #=> true
|
33
|
+
opts.password? #=> false
|
34
|
+
opts[:name] #=> 'lee'
|
98
35
|
```
|
99
36
|
|
100
|
-
|
101
|
-
-------
|
102
|
-
|
103
|
-
Slop's pretty good at parsing, let's take a look at what it'll extract for you
|
37
|
+
Slop supports several methods of writing options:
|
104
38
|
|
105
39
|
```ruby
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
on 'password='
|
111
|
-
end
|
112
|
-
```
|
113
|
-
|
114
|
-
Now throw some options at it:
|
40
|
+
# These options all do the same thing
|
41
|
+
on '-n', '--name', 'Your name', :argument => true
|
42
|
+
on 'n', :name=, 'Your name'
|
43
|
+
on :n, '--name=', 'Your name'
|
115
44
|
|
45
|
+
# As do these
|
46
|
+
on 'p', '--password', 'Your password', :argument => :optional
|
47
|
+
on :p, :password, 'Your password', :optional_argument => true
|
48
|
+
on '-p', 'password=?', 'Your password'
|
116
49
|
```
|
117
|
-
-s ftp://foobar.com -p1234 --username=FooBar --password 'hello there'
|
118
|
-
```
|
119
|
-
|
120
|
-
Here's what we'll get back
|
121
|
-
|
122
|
-
```
|
123
|
-
{
|
124
|
-
:server => "ftp://foobar.com",
|
125
|
-
:port => 1234,
|
126
|
-
:username => "FooBar",
|
127
|
-
:password => "hello there"
|
128
|
-
}
|
129
|
-
```
|
130
|
-
|
131
|
-
Events
|
132
|
-
------
|
133
50
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
```ruby
|
138
|
-
Slop.parse do
|
139
|
-
on :V, :version, 'Print the version' do
|
140
|
-
puts 'Version 1.0.0'
|
141
|
-
exit
|
142
|
-
end
|
143
|
-
end
|
144
|
-
```
|
145
|
-
|
146
|
-
Now when using the `--version` option on the command line, the trigger will
|
147
|
-
be called and its contents executed.
|
148
|
-
|
149
|
-
Yielding Non Options
|
150
|
-
--------------------
|
151
|
-
|
152
|
-
If you pass a block to `Slop#parse`, Slop will yield non-options as
|
153
|
-
they're found, just like
|
154
|
-
[OptionParser](http://rubydoc.info/stdlib/optparse/1.9.2/OptionParser:order)
|
155
|
-
does it.
|
156
|
-
|
157
|
-
```ruby
|
158
|
-
opts = Slop.new do
|
159
|
-
on :n, :name, :optional => false
|
160
|
-
end
|
161
|
-
|
162
|
-
opts.parse do |arg|
|
163
|
-
puts arg
|
164
|
-
end
|
165
|
-
|
166
|
-
# if ARGV is `foo --name Lee bar`
|
167
|
-
foo
|
168
|
-
bar
|
169
|
-
```
|
170
|
-
|
171
|
-
Negative Options
|
172
|
-
----------------
|
173
|
-
|
174
|
-
Slop also allows you to prefix `--no-` to an option which will force the option
|
175
|
-
to return a false value.
|
176
|
-
|
177
|
-
```ruby
|
178
|
-
opts = Slop.parse do
|
179
|
-
on :v, :verbose, :default => true
|
180
|
-
end
|
181
|
-
|
182
|
-
# with no command line options
|
183
|
-
opts[:verbose] #=> true
|
184
|
-
|
185
|
-
# with `--no-verbose`
|
186
|
-
opts[:verbose] #=> false
|
187
|
-
opts.verbose? #=> false
|
188
|
-
```
|
189
|
-
|
190
|
-
Short Switches
|
191
|
-
--------------
|
51
|
+
For more information about creating options, see the
|
52
|
+
[Creating Options](https://github.com/injekt/slop/wiki/Creating-Options)
|
53
|
+
wiki page.
|
192
54
|
|
193
|
-
|
194
|
-
parse `-abc` as the options `a` `b` and `c` and set their values to true. If
|
195
|
-
you would like to disable this, you can pass `multiple_switches => false` to
|
196
|
-
a new Slop object. In which case Slop will then parse `-fbar` as the option
|
197
|
-
`f` with the argument value `bar`.
|
55
|
+
You can also return your options as a Hash:
|
198
56
|
|
199
57
|
```ruby
|
200
|
-
|
201
|
-
on :a, 'First switch'
|
202
|
-
on :b, 'Second switch'
|
203
|
-
on :c, 'Third switch'
|
204
|
-
end
|
205
|
-
|
206
|
-
# Using `-ac`
|
207
|
-
opts[:a] #=> true
|
208
|
-
opts[:b] #=> false
|
209
|
-
opts[:c] #=> true
|
210
|
-
|
211
|
-
Slop.parse(:multiple_switches => false) do
|
212
|
-
on :a, 'Some switch', true
|
213
|
-
end
|
214
|
-
|
215
|
-
# Using `ahello`
|
216
|
-
opts[:a] #=> 'hello'
|
58
|
+
opts.to_hash #=> { :name => 'lee', :verbose => nil, :password => nil }
|
217
59
|
```
|
218
60
|
|
219
|
-
|
220
|
-
|
61
|
+
Printing Help
|
62
|
+
-------------
|
221
63
|
|
222
|
-
|
64
|
+
Slop attempts to build a good looking help string to print to your users. You
|
65
|
+
can see this by calling `opts.help` or simply `puts opts`.
|
223
66
|
|
224
|
-
|
225
|
-
|
226
|
-
opt :people, true, :as => Array
|
227
|
-
end
|
228
|
-
|
229
|
-
# ARGV is `--people lee,john,bill`
|
230
|
-
opts[:people] #=> ['lee', 'john', 'bill']
|
231
|
-
```
|
67
|
+
Configuration Options
|
68
|
+
---------------------
|
232
69
|
|
233
|
-
|
234
|
-
[this wiki page](https://github.com/injekt/slop/wiki/Lists---v2) for more info.
|
70
|
+
All of these options can be sent to `Slop.new` or `Slop.parse` in Hash form.
|
235
71
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
72
|
+
* `strict` - Enable strict mode. When processing unknown options, Slop will
|
73
|
+
raise an `InvalidOptionError`. **default:** *false*.
|
74
|
+
* `help` - Automatically add the `--help` option. **default:** *false*.
|
75
|
+
* `banner` - Set this options banner text. **default:** *nil*.
|
76
|
+
* `ignore_case` - When enabled, `-A` will look for the `-a` option if `-A`
|
77
|
+
does not exist. **default:** *false*.
|
78
|
+
* `autocreate` - Autocreate options on the fly. **default:** *false*.
|
79
|
+
* `arguments` - Force all options to expect arguments. **default:** *false*.
|
80
|
+
* `optional_arguments` - Force all options to accept optional arguments.
|
81
|
+
**default:** *false*.
|
82
|
+
* `multiple_switches` - When disabled, Slop will parse `-abc` as the option `a`
|
83
|
+
with the argument `bc` rather than 3 separate options. **default:** *true*.
|
84
|
+
* `longest_flag` - The longest string flag, used to aid configuring help
|
85
|
+
text. **default:** *0*.
|
246
86
|
|
247
87
|
Features
|
248
88
|
--------
|
@@ -251,7 +91,7 @@ Check out the following wiki pages for more features:
|
|
251
91
|
|
252
92
|
* [Ranges](https://github.com/injekt/slop/wiki/Ranges)
|
253
93
|
* [Auto Create](https://github.com/injekt/slop/wiki/Auto-Create)
|
254
|
-
* [Commands](https://github.com/injekt/slop/wiki/Commands
|
94
|
+
* [Commands](https://github.com/injekt/slop/wiki/Commands)
|
255
95
|
|
256
96
|
Woah woah, why you hating on OptionParser?
|
257
97
|
------------------------------------------
|
@@ -293,4 +133,4 @@ opts = Slop.parse do
|
|
293
133
|
end
|
294
134
|
|
295
135
|
opts.to_hash #=> { :name => 'lee', :age => 105 }
|
296
|
-
```
|
136
|
+
```
|
data/lib/slop.rb
CHANGED
@@ -1,710 +1,258 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'slop/option'
|
2
|
+
require 'slop/commands'
|
3
3
|
|
4
|
-
|
5
|
-
VERSION = '
|
4
|
+
class Slop
|
5
|
+
VERSION = '3.0.0.rc1'
|
6
6
|
|
7
|
-
#
|
8
|
-
# inherit from this class
|
7
|
+
# The main Error class, all Exception classes inherit from this class.
|
9
8
|
class Error < StandardError; end
|
10
9
|
|
11
|
-
# Raised when an option
|
10
|
+
# Raised when an option argument is expected but none are given.
|
12
11
|
class MissingArgumentError < Error; end
|
13
12
|
|
14
|
-
# Raised when an option is required but not
|
13
|
+
# Raised when an option is expected/required but not present.
|
15
14
|
class MissingOptionError < Error; end
|
16
15
|
|
17
|
-
# Raised when an
|
18
|
-
# options argument does not match this regexp
|
16
|
+
# Raised when an argument does not match its intended match constraint.
|
19
17
|
class InvalidArgumentError < Error; end
|
20
18
|
|
21
|
-
# Raised when
|
22
|
-
# or unspecified option is used
|
19
|
+
# Raised when an invalid option is found and the strict flag is enabled.
|
23
20
|
class InvalidOptionError < Error; end
|
24
21
|
|
25
|
-
#
|
26
|
-
class
|
22
|
+
# Raised when an invalid command is found and the strict flag is enabled.
|
23
|
+
class InvalidCommandError < Error; end
|
24
|
+
|
25
|
+
# Returns a default Hash of configuration options this Slop instance uses.
|
26
|
+
DEFAULT_OPTIONS = {
|
27
|
+
:strict => false,
|
28
|
+
:help => false,
|
29
|
+
:banner => nil,
|
30
|
+
:ignore_case => false,
|
31
|
+
:autocreate => false,
|
32
|
+
:arguments => false,
|
33
|
+
:optional_arguments => false,
|
34
|
+
:multiple_switches => true,
|
35
|
+
:longest_flag => 0
|
36
|
+
}
|
27
37
|
|
28
|
-
|
29
|
-
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# @param [String, #to_s] long The long flag representing this Option
|
34
|
-
# without the prefix (ie: `foo`)
|
35
|
-
#
|
36
|
-
# @param [String] description This options description
|
37
|
-
#
|
38
|
-
# @param [Boolean] argument True if this option takes an argument
|
39
|
-
#
|
40
|
-
# @option options [Boolean] :optional
|
41
|
-
# * When true, this option takes an optional argument, ie an argument
|
42
|
-
# does not **have** to be supplied.
|
43
|
-
#
|
44
|
-
# @option options [Boolean] :argument
|
45
|
-
# * True if this option takes an argument.
|
46
|
-
#
|
47
|
-
# @option options [Object] :default
|
48
|
-
# * The default value for this option when no argument is given
|
49
|
-
#
|
50
|
-
# @option options [Proc, #call] :callback
|
51
|
-
# * The callback object, used instead of passing a block to this option
|
52
|
-
#
|
53
|
-
# @option options [String, #to_s] :delimiter (',')
|
54
|
-
# * A delimiter string when processing this option as a list
|
55
|
-
#
|
56
|
-
# @option options [Integer] :limit (0)
|
57
|
-
# * A limit, used when processing this option as a list
|
58
|
-
#
|
59
|
-
# @option options [Boolean] :tail (false)
|
60
|
-
# * When true, this option will be grouped at the bottom of the help
|
61
|
-
# text instead of in order of processing
|
62
|
-
#
|
63
|
-
# @option options [Regexp] :match
|
64
|
-
# * A regular expression this option should match
|
38
|
+
class << self
|
39
|
+
|
40
|
+
# items - The Array of items to extract options from (default: ARGV).
|
41
|
+
# config - The Hash of configuration options to send to Slop.new().
|
42
|
+
# block - An optional block used to add options.
|
65
43
|
#
|
66
|
-
#
|
67
|
-
# * Used by `omit_exec` for omitting execution of this options callback
|
68
|
-
# if another option exists
|
44
|
+
# Examples:
|
69
45
|
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
# will be displayed for this option
|
46
|
+
# Slop.parse(ARGV, :help => true) do
|
47
|
+
# on '-n', '--name', 'Your username', :argument => true
|
48
|
+
# end
|
74
49
|
#
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
def initialize(slop, short, long, description, argument, options, &blk)
|
79
|
-
@slop = slop
|
80
|
-
|
81
|
-
self.short_flag = short
|
82
|
-
self.long_flag = long
|
83
|
-
self.description = description
|
84
|
-
|
85
|
-
@argument = argument
|
86
|
-
@options = options
|
87
|
-
|
88
|
-
self.tail = @options[:tail]
|
89
|
-
self.match = @options[:match]
|
90
|
-
self.help = @options.fetch(:help, true)
|
91
|
-
self.required = @options[:required]
|
92
|
-
|
93
|
-
@delimiter = @options.fetch(:delimiter, ',')
|
94
|
-
@limit = @options.fetch(:limit, 0)
|
95
|
-
|
96
|
-
@argument_type = @options[:as]
|
97
|
-
@argument_value = nil
|
98
|
-
|
99
|
-
self.forced = false
|
100
|
-
self.count = 0
|
101
|
-
|
102
|
-
@callback = block_given? ? blk : @options[:callback]
|
103
|
-
|
104
|
-
if long_flag && long_flag.size > @slop.longest_flag
|
105
|
-
@slop.longest_flag = long_flag.size
|
106
|
-
@slop.longest_flag += help.size if help.respond_to?(:to_str)
|
107
|
-
end
|
50
|
+
# Returns a new instance of Slop.
|
51
|
+
def parse(items = ARGV, config = {}, &block)
|
52
|
+
init_and_parse(items, false, config, &block)
|
108
53
|
end
|
109
54
|
|
110
|
-
#
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
@options[:optional] || @options[:optional_argument]
|
118
|
-
end
|
119
|
-
|
120
|
-
# @return [String] either the long or short flag for this option
|
121
|
-
def key
|
122
|
-
long_flag || short_flag
|
55
|
+
# items - The Array of items to extract options from (default: ARGV).
|
56
|
+
# config - The Hash of configuration options to send to Slop.new().
|
57
|
+
# block - An optional block used to add options.
|
58
|
+
#
|
59
|
+
# Returns a new instance of Slop.
|
60
|
+
def parse!(items = ARGV, config = {}, &block)
|
61
|
+
init_and_parse(items, true, config, &block)
|
123
62
|
end
|
124
63
|
|
125
|
-
#
|
64
|
+
# Build a Slop object from a option specification.
|
126
65
|
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
66
|
+
# This allows you to design your options via a simple String rather
|
67
|
+
# than programatically. Do note though that with this method, you're
|
68
|
+
# unable to pass any advanced options to the on() method when creating
|
69
|
+
# options.
|
130
70
|
#
|
131
|
-
#
|
132
|
-
|
133
|
-
if @argument_type.to_s.downcase == 'array'
|
134
|
-
@argument_value ||= []
|
135
|
-
|
136
|
-
if value.respond_to?(:to_str)
|
137
|
-
@argument_value.concat value.split(@delimiter, @limit)
|
138
|
-
end
|
139
|
-
else
|
140
|
-
@argument_value = value
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# @return [Object] the argument value after it's been cast
|
145
|
-
# according to the `:as` option
|
146
|
-
def argument_value
|
147
|
-
return @argument_value if forced
|
148
|
-
type = @argument_type.to_s.downcase
|
149
|
-
# Check for count first to prefer 0 over nil
|
150
|
-
return count if type == 'count'
|
151
|
-
|
152
|
-
value = @argument_value || @options[:default]
|
153
|
-
return if value.nil?
|
154
|
-
|
155
|
-
if @argument_type.respond_to?(:call)
|
156
|
-
@argument_type.call(value)
|
157
|
-
else
|
158
|
-
case type
|
159
|
-
when 'array'
|
160
|
-
arg_value(@argument_value)
|
161
|
-
when 'range'
|
162
|
-
arg_value(value_to_range(value))
|
163
|
-
when 'float'
|
164
|
-
arg_value(value.to_s.to_f)
|
165
|
-
when 'string', 'str'
|
166
|
-
arg_value(value.to_s)
|
167
|
-
when 'symbol', 'sym'
|
168
|
-
arg_value(value.to_s.to_sym)
|
169
|
-
when 'integer', 'int'
|
170
|
-
arg_value(value.to_s.to_i)
|
171
|
-
else
|
172
|
-
value
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
# Force an argument value, used when the desired argument value
|
178
|
-
# is negative (false or nil)
|
71
|
+
# string - The optspec String
|
72
|
+
# config - A Hash of configuration options to pass to Slop.new
|
179
73
|
#
|
180
|
-
#
|
181
|
-
def force_argument_value(value)
|
182
|
-
@argument_value = value
|
183
|
-
self.forced = true
|
184
|
-
end
|
185
|
-
|
186
|
-
# Execute the block or callback object associated with this Option
|
74
|
+
# Examples:
|
187
75
|
#
|
188
|
-
#
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
def omit_exec?(items)
|
197
|
-
items.any? do |item|
|
198
|
-
item.to_s.sub(/\A--?/, '') == @options[:unless].to_s.sub(/\A--?/, '')
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
# This option in a nice pretty string, including a short flag, long
|
203
|
-
# flag, and description (if they exist).
|
76
|
+
# opts = Slop.optspec(<<-SPEC)
|
77
|
+
# ruby foo.rb [options]
|
78
|
+
# ---
|
79
|
+
# n,name= Your name
|
80
|
+
# a,age= Your age
|
81
|
+
# A,auth Sign in with auth
|
82
|
+
# p,passcode= Your secret pass code
|
83
|
+
# SPEC
|
204
84
|
#
|
205
|
-
#
|
206
|
-
#
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
85
|
+
# opts.fetch_option(:name).description #=> "Your name"
|
86
|
+
#
|
87
|
+
# Returns a new instance of Slop.
|
88
|
+
def optspec(string, config = {})
|
89
|
+
config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/]
|
90
|
+
lines = string.split("\n").reject(&:empty?)
|
91
|
+
opts = Slop.new(config)
|
92
|
+
|
93
|
+
lines.each do |line|
|
94
|
+
opt, description = line.split(' ', 2)
|
95
|
+
short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
|
96
|
+
opt = opts.on(short, long, description)
|
97
|
+
|
98
|
+
if long && long[-1] == ?$
|
99
|
+
long.sub!(/\=$/, '')
|
100
|
+
opt.config[:argument] = true
|
218
101
|
end
|
219
|
-
diff = @slop.longest_flag - size
|
220
|
-
out += " " * (diff + 6)
|
221
|
-
else
|
222
|
-
out += " " * (@slop.longest_flag + 8)
|
223
102
|
end
|
224
103
|
|
225
|
-
|
226
|
-
end
|
227
|
-
|
228
|
-
# @return [String]
|
229
|
-
def inspect
|
230
|
-
"#<Slop::Option short_flag=#{short_flag.inspect} " +
|
231
|
-
"long_flag=#{long_flag.inspect} argument=#{@argument.inspect} " +
|
232
|
-
"description=#{description.inspect}>"
|
104
|
+
opts
|
233
105
|
end
|
234
106
|
|
235
107
|
private
|
236
108
|
|
237
|
-
|
238
|
-
value if accepts_optional_argument? || expects_argument?
|
239
|
-
end
|
240
|
-
|
241
|
-
def value_to_range(value)
|
242
|
-
case value.to_s
|
243
|
-
when /\A(-?\d+?)(\.\.\.?|-|,)(-?\d+)\z/
|
244
|
-
Range.new($1.to_i, $3.to_i, $2 == '...')
|
245
|
-
when /\A-?\d+\z/
|
246
|
-
Range.new(value.to_i, value.to_i)
|
247
|
-
else
|
248
|
-
value
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
end
|
253
|
-
|
254
|
-
# Used to hold a list of Option objects. This class inherits from Array
|
255
|
-
# and overwrites `Array#[]` so we can fetch Option objects via their
|
256
|
-
# short or long flags
|
257
|
-
class Options < Array
|
258
|
-
|
259
|
-
# Fetch an Option object. This method overrides Array#[] to provide
|
260
|
-
# a nicer interface for fetching options via their short or long flag.
|
261
|
-
# The reason we don't use a Hash here is because an option cannot be
|
262
|
-
# identified by a single label. Instead this method tests against
|
263
|
-
# a short flag first, followed by a long flag. When passing this
|
264
|
-
# method an Integer, it will work as an Array usually would, fetching
|
265
|
-
# the Slop::Option at this index.
|
109
|
+
# Convenience method used by ::parse and ::parse!.
|
266
110
|
#
|
267
|
-
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
#
|
271
|
-
#
|
272
|
-
#
|
273
|
-
def
|
274
|
-
if
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
[option.short_flag, option.long_flag].include? flag.to_s
|
279
|
-
end
|
280
|
-
end
|
111
|
+
# items - The Array of items to parse.
|
112
|
+
# delete - When true, executes #parse! over #parse.
|
113
|
+
# config - The Hash of configuration options to pass to Slop.new.
|
114
|
+
# block - The optional block to pass to Slop.new
|
115
|
+
#
|
116
|
+
# Returns a newly created instance of Slop.
|
117
|
+
def init_and_parse(items, delete, config, &block)
|
118
|
+
config, items = items, ARGV if items.is_a?(Hash) && config.empty?
|
119
|
+
slop = Slop.new(config, &block)
|
120
|
+
delete ? slop.parse!(items) : slop.parse(items)
|
121
|
+
slop
|
281
122
|
end
|
282
123
|
end
|
283
124
|
|
284
|
-
#
|
285
|
-
|
286
|
-
# @param [Array] items Items to parse into options.
|
287
|
-
# @example Specifying three options to parse:
|
288
|
-
# opts = Slops.parse do
|
289
|
-
# on :v, :verbose, 'Enable verbose mode'
|
290
|
-
# on :n, :name, 'Your name'
|
291
|
-
# on :a, :age, 'Your age'
|
292
|
-
# end
|
293
|
-
# @return [Slop] Returns an instance of Slop
|
294
|
-
def self.parse(items=ARGV, options={}, &block)
|
295
|
-
initialize_and_parse items, false, options, &block
|
296
|
-
end
|
297
|
-
|
298
|
-
# Identical to {Slop.parse}, but removes parsed options from the
|
299
|
-
# original Array
|
300
|
-
#
|
301
|
-
# @return [Slop] Returns an instance of Slop
|
302
|
-
def self.parse!(items=ARGV, options={}, &block)
|
303
|
-
initialize_and_parse items, true, options, &block
|
304
|
-
end
|
305
|
-
|
306
|
-
# Build options from an optspec string
|
307
|
-
#
|
308
|
-
# @param [String] optspec The option spec string
|
309
|
-
# @param [Array] options A list of options to forward to Slop.new
|
310
|
-
# @return [Slop] A new instance of Slop
|
311
|
-
def self.optspec(optspec, *options)
|
312
|
-
if optspec[/^--+$/]
|
313
|
-
banner, optspec = optspec.split(/^--+$/, 2)
|
314
|
-
end
|
315
|
-
|
316
|
-
lines = optspec.split("\n").reject(&:empty?)
|
317
|
-
opts = Slop.new(banner, *options)
|
318
|
-
|
319
|
-
lines.each do |line|
|
320
|
-
opt, description = line.split(' ', 2)
|
321
|
-
short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
|
322
|
-
argument = long && long[-1] == ?$
|
323
|
-
long.sub!(/\=$/, '') if argument
|
324
|
-
opts.on short, long, description, argument
|
325
|
-
end
|
326
|
-
|
327
|
-
opts
|
328
|
-
end
|
125
|
+
# The Hash of configuration options for this Slop instance.
|
126
|
+
attr_reader :config
|
329
127
|
|
330
|
-
#
|
128
|
+
# The Array of Slop::Option objects tied to this Slop instance.
|
331
129
|
attr_reader :options
|
332
130
|
|
333
|
-
#
|
334
|
-
attr_reader :commands
|
335
|
-
|
336
|
-
# @overload banner=(string)
|
337
|
-
# Set the banner
|
338
|
-
# @param [String] string The text to set the banner to
|
339
|
-
attr_writer :banner
|
340
|
-
|
341
|
-
# @overload summary=(string)
|
342
|
-
# Set the summary
|
343
|
-
# @param [String] string The text to set the summary to
|
344
|
-
attr_writer :summary
|
345
|
-
|
346
|
-
# @overload description=(string)
|
347
|
-
# Set the description
|
348
|
-
# @param [String] string The text to set the description to
|
349
|
-
attr_writer :description
|
350
|
-
|
351
|
-
# @return [Integer] The length of the longest flag slop knows of
|
352
|
-
attr_accessor :longest_flag
|
353
|
-
|
354
|
-
# @return [Array] A list of aliases this command uses
|
355
|
-
attr_accessor :aliases
|
356
|
-
|
357
|
-
# @option opts [Boolean] :help
|
358
|
-
# * Automatically add the `help` option
|
359
|
-
#
|
360
|
-
# @option opts [Boolean] :strict
|
361
|
-
# * Raises when a non listed option is found, false by default
|
362
|
-
#
|
363
|
-
# @option opts [Boolean] :multiple_switches
|
364
|
-
# * Allows `-abc` to be processed as the options 'a', 'b', 'c' and will
|
365
|
-
# force their argument values to true. By default Slop with parse this
|
366
|
-
# as 'a' with the argument 'bc'
|
367
|
-
#
|
368
|
-
# @option opts [String] :banner
|
369
|
-
# * The banner text used for the help
|
370
|
-
#
|
371
|
-
# @option opts [Proc, #call] :on_empty
|
372
|
-
# * Any object that respondes to `call` which is executed when Slop has
|
373
|
-
# no items to parse
|
374
|
-
#
|
375
|
-
# @option opts [IO, #puts] :io ($stderr)
|
376
|
-
# * An IO object for writing to when :help => true is used
|
377
|
-
#
|
378
|
-
# @option opts [Boolean] :exit_on_help (true)
|
379
|
-
# * When false and coupled with the :help option, Slop will not exit
|
380
|
-
# inside of the `help` option
|
381
|
-
#
|
382
|
-
# @option opts [Boolean] :ignore_case (false)
|
383
|
-
# * Ignore options case
|
384
|
-
#
|
385
|
-
# @option opts [Proc, #call] :on_noopts
|
386
|
-
# * Trigger an event when no options are found
|
131
|
+
# Create a new instance of Slop and optionally build options via a block.
|
387
132
|
#
|
388
|
-
#
|
389
|
-
#
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
# @option opts [Boolean] :completion (true)
|
399
|
-
# * When true, commands will be auto completed. Ie `foobar` will be
|
400
|
-
# executed simply when `foo` `fo` or `foob` are used
|
401
|
-
#
|
402
|
-
# @option options [Boolean] :all_accept_arguments (false)
|
403
|
-
# * When true, every option added will take an argument, this saves
|
404
|
-
# having to enable it for every option
|
405
|
-
def initialize(*opts, &block)
|
406
|
-
sloptions = opts.last.is_a?(Hash) ? opts.pop : {}
|
407
|
-
sloptions[:banner] = opts.shift if opts[0].respond_to?(:to_str)
|
408
|
-
opts.each { |o| sloptions[o] = true }
|
409
|
-
|
410
|
-
@options = Options.new
|
411
|
-
@commands = {}
|
412
|
-
@execution_block = nil
|
413
|
-
|
414
|
-
@longest_flag = 0
|
415
|
-
@invalid_options = []
|
416
|
-
|
417
|
-
@banner = sloptions[:banner]
|
418
|
-
@strict = sloptions[:strict]
|
419
|
-
@ignore_case = sloptions[:ignore_case]
|
420
|
-
@multiple_switches = sloptions.fetch(:multiple_switches, true)
|
421
|
-
@autocreate = sloptions[:autocreate]
|
422
|
-
@completion = sloptions.fetch(:completion, true)
|
423
|
-
@arguments = sloptions[:arguments]
|
424
|
-
@on_empty = sloptions[:on_empty]
|
425
|
-
@io = sloptions.fetch(:io, $stderr)
|
426
|
-
@on_noopts = sloptions[:on_noopts] || sloptions[:on_optionless]
|
427
|
-
@sloptions = sloptions
|
133
|
+
# config - A Hash of configuration options.
|
134
|
+
# block - An optional block used to specify options.
|
135
|
+
def initialize(config = {}, &block)
|
136
|
+
@config = DEFAULT_OPTIONS.merge(config)
|
137
|
+
@options = []
|
138
|
+
@trash = []
|
139
|
+
@triggered_options = []
|
140
|
+
@unknown_options = []
|
141
|
+
@callbacks = {}
|
142
|
+
@separators = {}
|
428
143
|
|
429
144
|
if block_given?
|
430
145
|
block.arity == 1 ? yield(self) : instance_eval(&block)
|
431
146
|
end
|
432
147
|
|
433
|
-
if
|
434
|
-
on
|
435
|
-
|
436
|
-
exit unless sloptions[:exit_on_help] == false
|
148
|
+
if config[:help]
|
149
|
+
on('-h', '--help', 'Display this help message.', :tail => true) do
|
150
|
+
$stderr.puts help
|
437
151
|
end
|
438
152
|
end
|
439
153
|
end
|
440
154
|
|
441
|
-
# Set
|
155
|
+
# Set the banner.
|
442
156
|
#
|
443
|
-
#
|
444
|
-
# @example
|
445
|
-
# opts = Slop.parse do
|
446
|
-
# banner "Usage - ruby foo.rb [arguments]"
|
447
|
-
# end
|
448
|
-
# @return [String] The current banner
|
449
|
-
def banner(text=nil)
|
450
|
-
@banner = text if text
|
451
|
-
@banner
|
452
|
-
end
|
453
|
-
|
454
|
-
# Set or return the summary
|
157
|
+
# banner - The String to set the banner.
|
455
158
|
#
|
456
|
-
#
|
457
|
-
|
458
|
-
|
459
|
-
# summary "do stuff with more stuff"
|
460
|
-
# end
|
461
|
-
# @return [String] The current summary
|
462
|
-
def summary(text=nil)
|
463
|
-
@summary = text if text
|
464
|
-
@summary
|
159
|
+
# Returns nothing.
|
160
|
+
def banner=(banner)
|
161
|
+
config[:banner] = banner
|
465
162
|
end
|
466
163
|
|
467
|
-
#
|
164
|
+
# Get or set the banner.
|
468
165
|
#
|
469
|
-
#
|
470
|
-
# @example
|
471
|
-
# opts = Slop.parse do
|
472
|
-
# description "This command does a lot of stuff with other stuff."
|
473
|
-
# end
|
474
|
-
# @return [String] The current description
|
475
|
-
def description(text=nil)
|
476
|
-
@description = text if text
|
477
|
-
@description
|
478
|
-
end
|
479
|
-
|
480
|
-
# Parse a list of options, leaving the original Array unchanged
|
166
|
+
# banner - The String to set the banner.
|
481
167
|
#
|
482
|
-
#
|
483
|
-
def
|
484
|
-
|
168
|
+
# Returns the banner String.
|
169
|
+
def banner(banner = nil)
|
170
|
+
config[:banner] = banner if banner
|
171
|
+
config[:banner]
|
485
172
|
end
|
486
173
|
|
487
|
-
# Parse a list of
|
174
|
+
# Parse a list of items, executing and gathering options along the way.
|
488
175
|
#
|
489
|
-
#
|
490
|
-
|
491
|
-
parse_items items, true, &block
|
492
|
-
end
|
493
|
-
|
494
|
-
# Enumerable interface
|
495
|
-
def each(&block)
|
496
|
-
@options.each(&block)
|
497
|
-
end
|
498
|
-
|
499
|
-
# @param [Symbol] key Option symbol
|
500
|
-
# @example
|
501
|
-
# opts[:name] #=> "Emily"
|
502
|
-
# opts.get(:name) #=> "Emily"
|
503
|
-
# @return [Object] Returns the value associated with that option. If an
|
504
|
-
# option doesn't exist, a command will instead be searched for
|
505
|
-
def [](key)
|
506
|
-
option = @options[key]
|
507
|
-
option ? option.argument_value : @commands[key]
|
508
|
-
end
|
509
|
-
alias get []
|
510
|
-
|
511
|
-
# Specify an option with a short or long version, description and type
|
176
|
+
# items - The Array of items to extract options from (default: ARGV).
|
177
|
+
# block - An optional block which when used will yield non options.
|
512
178
|
#
|
513
|
-
#
|
514
|
-
|
515
|
-
|
516
|
-
# @option args [String] :description Option description for use in Slop#help
|
517
|
-
# @option args [Boolean] :argument Specifies whether this option requires
|
518
|
-
# an argument
|
519
|
-
# @option args [Hash] :options Optional option configurations.
|
520
|
-
# @example
|
521
|
-
# opts = Slop.parse do
|
522
|
-
# on :n, :name, 'Your username', true # Required argument
|
523
|
-
# on :a, :age, 'Your age (optional)', :optional => true
|
524
|
-
# on :g, :gender, 'Your gender', :optional => false
|
525
|
-
# on :V, :verbose, 'Run in verbose mode', :default => true
|
526
|
-
# on :P, :people, 'Your friends', true, :as => Array
|
527
|
-
# on :h, :help, 'Print this help screen' do
|
528
|
-
# puts help
|
529
|
-
# end
|
530
|
-
# end
|
531
|
-
# @return [Slop::Option]
|
532
|
-
def option(*args, &block)
|
533
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
534
|
-
short, long, desc, arg, extras = clean_options(args)
|
535
|
-
|
536
|
-
options.merge!(extras)
|
537
|
-
options[:argument] = true if @sloptions[:all_accept_arguments]
|
538
|
-
|
539
|
-
option = Option.new(self, short, long, desc, arg, options, &block)
|
540
|
-
@options << option
|
541
|
-
|
542
|
-
option
|
179
|
+
# Returns an Array of original items.
|
180
|
+
def parse(items = ARGV, &block)
|
181
|
+
parse_items(items, false, &block)
|
543
182
|
end
|
544
|
-
alias opt option
|
545
|
-
alias on option
|
546
183
|
|
547
|
-
#
|
184
|
+
# Parse a list of items, executing and gathering options along the way.
|
185
|
+
# unlike parse() this method will remove any options and option arguments
|
186
|
+
# from the original Array.
|
548
187
|
#
|
549
|
-
#
|
550
|
-
#
|
551
|
-
# @example
|
552
|
-
# opts = Slop.new do
|
553
|
-
# command :create do
|
554
|
-
# on :v, :verbose
|
555
|
-
# end
|
556
|
-
# end
|
557
|
-
#
|
558
|
-
# # ARGV is `create -v`
|
559
|
-
# opts.commands[:create].verbose? #=> true
|
560
|
-
# @since 1.5.0
|
561
|
-
# @raise [ArgumentError] When this command already exists
|
562
|
-
# @return [Slop] a new instance of Slop namespaced to +label+
|
563
|
-
def command(label, options={}, &block)
|
564
|
-
if @commands.key?(label)
|
565
|
-
raise ArgumentError, "command `#{label}` already exists"
|
566
|
-
end
|
567
|
-
|
568
|
-
slop = Slop.new @sloptions.merge(options)
|
569
|
-
slop.aliases = Array(options.delete(:aliases) || options.delete(:alias))
|
570
|
-
@commands[label] = slop
|
571
|
-
|
572
|
-
slop.aliases.each { |a| @commands[a] = @commands[label] }
|
573
|
-
|
574
|
-
if block_given?
|
575
|
-
block.arity == 1 ? yield(slop) : slop.instance_eval(&block)
|
576
|
-
end
|
577
|
-
|
578
|
-
slop
|
579
|
-
end
|
580
|
-
|
581
|
-
# Trigger an event when Slop has no values to parse
|
188
|
+
# items - The Array of items to extract options from (default: ARGV).
|
189
|
+
# block - An optional block which when used will yield non options.
|
582
190
|
#
|
583
|
-
#
|
584
|
-
|
585
|
-
|
586
|
-
# Slop.parse do
|
587
|
-
# on_empty { puts 'No argument given!' }
|
588
|
-
# end
|
589
|
-
# @since 1.5.0
|
590
|
-
def on_empty(obj=nil, &block)
|
591
|
-
@on_empty ||= (obj || block)
|
191
|
+
# Returns an Array of original items with options removed.
|
192
|
+
def parse!(items = ARGV, &block)
|
193
|
+
parse_items(items, true, &block)
|
592
194
|
end
|
593
|
-
alias on_empty= on_empty
|
594
195
|
|
595
|
-
#
|
196
|
+
# Add an Option.
|
596
197
|
#
|
597
|
-
#
|
598
|
-
# responding to `call`)
|
599
|
-
# @example
|
600
|
-
# Slop.parse do
|
601
|
-
# on_noopts { puts 'No options here!' }
|
602
|
-
# end
|
603
|
-
# @since 1.6.0
|
604
|
-
def on_noopts(obj=nil, &block)
|
605
|
-
@on_noopts ||= (obj || block)
|
606
|
-
end
|
607
|
-
alias on_optionless on_noopts
|
608
|
-
|
609
|
-
# Add an execution block (for commands)
|
198
|
+
# objects - An Array with an optional Hash as the last element.
|
610
199
|
#
|
611
|
-
#
|
612
|
-
# opts = Slop.new do
|
613
|
-
# command :foo do
|
614
|
-
# on :v, :verbose
|
200
|
+
# Examples:
|
615
201
|
#
|
616
|
-
#
|
617
|
-
#
|
618
|
-
# end
|
619
|
-
# opts.parse %w[foo --verbose] #=> true
|
202
|
+
# on '-u', '--username=', 'Your username'
|
203
|
+
# on :v, :verbose, 'Enable verbose mode'
|
620
204
|
#
|
621
|
-
#
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
if block_given?
|
627
|
-
@execution_block = block
|
628
|
-
elsif @execution_block.respond_to?(:call)
|
629
|
-
@execution_block.call(self, args)
|
630
|
-
end
|
205
|
+
# Returns the created instance of Slop::Option.
|
206
|
+
def on(*objects, &block)
|
207
|
+
option = build_option(objects, &block)
|
208
|
+
options << option
|
209
|
+
option
|
631
210
|
end
|
211
|
+
alias option on
|
212
|
+
alias opt on
|
632
213
|
|
633
|
-
#
|
214
|
+
# Fetch an options argument value.
|
634
215
|
#
|
635
|
-
#
|
636
|
-
# opts.to_hash #=> { :name => 'Emily' }
|
216
|
+
# key - The Symbol or String option short or long flag.
|
637
217
|
#
|
638
|
-
#
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
@options.reduce({}) do |hsh, option|
|
643
|
-
key = option.key
|
644
|
-
key = key.to_sym if symbols
|
645
|
-
hsh[key] = option.argument_value
|
646
|
-
hsh
|
647
|
-
end
|
218
|
+
# Returns the Object value for this option, or nil.
|
219
|
+
def [](key)
|
220
|
+
option = fetch_option(key)
|
221
|
+
option.value if option
|
648
222
|
end
|
649
|
-
alias
|
223
|
+
alias get []
|
650
224
|
|
651
|
-
#
|
652
|
-
|
653
|
-
|
654
|
-
# opts = Slop.new do
|
655
|
-
# on :n, :name, 'Persons name', true
|
656
|
-
# on :a, :age, 'Persons age', true, :as => :int
|
657
|
-
# on :s, :sex, 'Persons sex m/f', true, :match => /^[mf]$/
|
658
|
-
# on :A, :admin, 'Enable admin mode'
|
659
|
-
# end
|
660
|
-
#
|
661
|
-
# opts.parse %w[ --name Lee --age 22 -s m --admin ]
|
662
|
-
#
|
663
|
-
# person = opts.to_struct("Person")
|
664
|
-
# person.class #=> Struct::Person
|
665
|
-
# person.name #=> 'Lee'
|
666
|
-
# person.age #=> 22
|
667
|
-
# person.sex #=> m
|
668
|
-
# person.admin #=> true
|
669
|
-
#
|
670
|
-
# @param [String] name The name of this class
|
671
|
-
# @return [Class] The new class, or nil if there are no options
|
672
|
-
# @since 2.0.0
|
673
|
-
def to_struct(name=nil)
|
674
|
-
hash = to_hash
|
675
|
-
Struct.new(name, *hash.keys).new(*hash.values) unless hash.empty?
|
225
|
+
# Returns a new Hash with option flags as keys and option values as values.
|
226
|
+
def to_hash
|
227
|
+
Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
|
676
228
|
end
|
229
|
+
alias to_h to_hash
|
677
230
|
|
678
|
-
#
|
231
|
+
# Check for an options presence.
|
679
232
|
#
|
680
|
-
#
|
681
|
-
# opts = Slop.new do
|
682
|
-
# on :n, :name, 'Your name', true
|
683
|
-
# on :p, :password, 'Your password', true
|
684
|
-
# on :A, 'Use auth?'
|
685
|
-
# end
|
233
|
+
# Examples:
|
686
234
|
#
|
687
|
-
# opts.parse %w
|
688
|
-
# opts.
|
235
|
+
# opts.parse %w( --foo )
|
236
|
+
# opts.present?(:foo) #=> true
|
237
|
+
# opts.present?(:bar) #=> false
|
689
238
|
#
|
690
|
-
#
|
691
|
-
|
692
|
-
|
693
|
-
@options.select { |opt| not present?(opt.key) }.map(&:key)
|
239
|
+
# Returns true if all of the keys are present in the parsed arguments.
|
240
|
+
def present?(*keys)
|
241
|
+
keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
|
694
242
|
end
|
695
243
|
|
696
|
-
#
|
244
|
+
# Convenience method for present?(:option).
|
697
245
|
#
|
698
|
-
#
|
246
|
+
# Examples:
|
699
247
|
#
|
700
|
-
#
|
701
|
-
# #== ruby foo.rb -v
|
248
|
+
# opts.parse %( --verbose )
|
702
249
|
# opts.verbose? #=> true
|
703
|
-
# opts.
|
704
|
-
#
|
705
|
-
#
|
706
|
-
|
707
|
-
|
250
|
+
# opts.other? #=> false
|
251
|
+
#
|
252
|
+
# Returns true if this option is present. If this method does not end
|
253
|
+
# with a ? character it will instead call super().
|
254
|
+
def method_missing(method, *args, &block)
|
255
|
+
meth = method.to_s
|
708
256
|
if meth[-1] == ??
|
709
257
|
present?(meth.chop)
|
710
258
|
else
|
@@ -712,342 +260,305 @@ class Slop
|
|
712
260
|
end
|
713
261
|
end
|
714
262
|
|
715
|
-
# Override this method so we can check if an option? method exists
|
263
|
+
# Override this method so we can check if an option? method exists.
|
264
|
+
#
|
265
|
+
# Returns true if this option key exists in our list of options.
|
716
266
|
def respond_to?(method)
|
717
267
|
method = method.to_s
|
718
|
-
if method[-1] == ??
|
268
|
+
if method[-1] == ?? && options.any? { |o| o.key == method.chop }
|
719
269
|
true
|
720
270
|
else
|
721
271
|
super
|
722
272
|
end
|
723
273
|
end
|
724
274
|
|
725
|
-
#
|
275
|
+
# Fetch a list of options which were missing from the parsed list.
|
276
|
+
#
|
277
|
+
# Examples:
|
726
278
|
#
|
727
|
-
#
|
728
|
-
#
|
279
|
+
# opts = Slop.new do
|
280
|
+
# on :n, :name=
|
281
|
+
# on :p, :password=
|
282
|
+
# end
|
729
283
|
#
|
730
|
-
#
|
731
|
-
#
|
732
|
-
#
|
733
|
-
|
734
|
-
|
284
|
+
# opts.parse %w[ --name Lee ]
|
285
|
+
# opts.missing #=> ['password']
|
286
|
+
#
|
287
|
+
# Returns an Array of Strings representing missing options.
|
288
|
+
def missing
|
289
|
+
(options - @triggered_options).map(&:key)
|
735
290
|
end
|
736
291
|
|
737
|
-
#
|
292
|
+
# Fetch a Slop::Option object.
|
738
293
|
#
|
739
|
-
#
|
740
|
-
#
|
741
|
-
#
|
742
|
-
#
|
743
|
-
#
|
744
|
-
#
|
745
|
-
#
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
if options.size > 0
|
754
|
-
parts << "options:"
|
294
|
+
# key - The Symbol or String option key.
|
295
|
+
#
|
296
|
+
# Examples:
|
297
|
+
#
|
298
|
+
# opts.on(:foo, 'Something fooey', :argument => :optional)
|
299
|
+
# opt = opts.fetch_option(:foo)
|
300
|
+
# opt.class #=> Slop::Option
|
301
|
+
# opt.accepts_optional_argument? #=> true
|
302
|
+
#
|
303
|
+
# Returns an Option or nil if none were found.
|
304
|
+
def fetch_option(key)
|
305
|
+
options.find { |option| [option.long, option.short].include?(clean(key)) }
|
306
|
+
end
|
755
307
|
|
756
|
-
|
757
|
-
|
758
|
-
|
308
|
+
# Add a callback.
|
309
|
+
#
|
310
|
+
# label - The Symbol identifier to attach this callback.
|
311
|
+
#
|
312
|
+
# Returns nothing.
|
313
|
+
def add_callback(label, &block)
|
314
|
+
(@callbacks[label] ||= []) << block
|
315
|
+
end
|
759
316
|
|
760
|
-
|
761
|
-
|
317
|
+
# Add string separators between options.
|
318
|
+
#
|
319
|
+
# text - The String text to print.
|
320
|
+
def separator(text)
|
321
|
+
@separators[options.size] = text
|
322
|
+
end
|
762
323
|
|
763
|
-
|
324
|
+
# Print a handy Slop help string.
|
325
|
+
#
|
326
|
+
# Returns the banner followed by available option help strings.
|
327
|
+
def to_s
|
328
|
+
heads = options.reject(&:tail?)
|
329
|
+
tails = (options - heads)
|
330
|
+
opts = (heads + tails).select(&:help).map(&:to_s)
|
331
|
+
optstr = opts.map.with_index { |o, i|
|
332
|
+
(str = @separators[i + 1]) ? [o, str].join("\n") : o
|
333
|
+
}.join("\n")
|
334
|
+
config[:banner] ? config[:banner] + "\n" + optstr : optstr
|
764
335
|
end
|
765
336
|
alias help to_s
|
766
337
|
|
767
|
-
#
|
768
|
-
# settings revealed
|
338
|
+
# Returns the String inspection text.
|
769
339
|
def inspect
|
770
|
-
"#<Slop
|
771
|
-
options.map(&:inspect).join("\n ") + "\n>"
|
340
|
+
"#<Slop #{config.inspect} #{options.map(&:inspect)}>"
|
772
341
|
end
|
773
342
|
|
774
343
|
private
|
775
344
|
|
776
|
-
|
777
|
-
private
|
778
|
-
|
779
|
-
def initialize_and_parse(items, delete, options, &block)
|
780
|
-
if items.is_a?(Hash) && options.empty?
|
781
|
-
options = items
|
782
|
-
items = ARGV
|
783
|
-
end
|
784
|
-
|
785
|
-
slop = new(options, &block)
|
786
|
-
delete ? slop.parse!(items) : slop.parse(items)
|
787
|
-
slop
|
788
|
-
end
|
789
|
-
end
|
790
|
-
|
791
|
-
# traverse through the list of items sent to parse() or parse!() and
|
792
|
-
# attempt to do the following:
|
345
|
+
# Parse a list of items and process their values.
|
793
346
|
#
|
794
|
-
#
|
795
|
-
#
|
796
|
-
#
|
797
|
-
#
|
798
|
-
#
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
elsif not items.any? {|i| i.to_s[/\A--?/] } and @on_noopts.respond_to?(:call)
|
804
|
-
@on_noopts.call self
|
805
|
-
return items
|
806
|
-
elsif execute_command(items, delete)
|
347
|
+
# items - The Array of items to process.
|
348
|
+
# delete - True to remove any triggered options and arguments from the
|
349
|
+
# original list of items.
|
350
|
+
# block - An optional block which when passed will yields non-options.
|
351
|
+
#
|
352
|
+
# Returns the original Array of items.
|
353
|
+
def parse_items(items, delete, &block)
|
354
|
+
if items.empty? && @callbacks[:empty]
|
355
|
+
@callbacks[:empty].each { |cb| cb.call(self) }
|
807
356
|
return items
|
808
357
|
end
|
809
358
|
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
359
|
+
items.each_with_index { |item, index|
|
360
|
+
@trash << index && break if item == '--'
|
361
|
+
autocreate(items, index) if config[:autocreate]
|
362
|
+
process_item(items, index, &block) unless @trash.include?(index)
|
363
|
+
}.reject!.with_index { |item, index| delete && @trash.include?(index) }
|
815
364
|
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
autocreate(flag, index, items) if @autocreate
|
822
|
-
option, argument = extract_option(item, flag)
|
365
|
+
required_options = options.select { |opt| opt.required? && opt.count < 1 }
|
366
|
+
if required_options.any?
|
367
|
+
raise MissingOptionError,
|
368
|
+
"Missing required option(s): #{required_options.map(&:key).join(', ')}"
|
369
|
+
end
|
823
370
|
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
end
|
371
|
+
if @unknown_options.any?
|
372
|
+
raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
|
373
|
+
end
|
828
374
|
|
829
|
-
|
830
|
-
|
831
|
-
trash << index
|
832
|
-
next if option.forced
|
833
|
-
option.argument_value = true
|
834
|
-
|
835
|
-
if option.expects_argument? or option.accepts_optional_argument?
|
836
|
-
argument ||= items.at(index + 1)
|
837
|
-
trash << index + 1
|
838
|
-
|
839
|
-
if not option.accepts_optional_argument? and argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
|
840
|
-
raise MissingArgumentError, "'#{option.key}' expects an argument, none given"
|
841
|
-
end
|
842
|
-
|
843
|
-
if argument && argument !~ /^--?[a-zA-Z_-]+/
|
844
|
-
if option.match and not argument.match(option.match)
|
845
|
-
raise InvalidArgumentError, "'#{argument}' does not match #{option.match.inspect}"
|
846
|
-
end
|
847
|
-
|
848
|
-
option.argument_value = argument
|
849
|
-
option.call option.argument_value unless option.omit_exec?(items)
|
850
|
-
else
|
851
|
-
option.argument_value = nil
|
852
|
-
check_optional_argument!(option, flag)
|
853
|
-
end
|
854
|
-
else
|
855
|
-
option.call unless option.omit_exec?(items)
|
856
|
-
end
|
857
|
-
else
|
858
|
-
@invalid_options << flag if item[/\A--?/] and @strict
|
859
|
-
block.call(item) if block_given? and not trash.include?(index)
|
860
|
-
end
|
375
|
+
if @triggered_options.empty? && @callbacks[:no_options]
|
376
|
+
@callbacks[:no_options].each { |cb| cb.call(self) }
|
861
377
|
end
|
862
378
|
|
863
|
-
items.reject!.with_index { |o, i| trash.include?(i) } if delete
|
864
|
-
raise_if_invalid_options!
|
865
|
-
raise_if_missing_required_options!(items)
|
866
379
|
items
|
867
380
|
end
|
868
381
|
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
382
|
+
# Process a list item, figure out if it's an option, execute any
|
383
|
+
# callbacks, assign any option arguments, and do some sanity checks.
|
384
|
+
#
|
385
|
+
# items - The Array of items to process.
|
386
|
+
# index - The current Integer index of the item we want to process.
|
387
|
+
# block - An optional block which when passed will yield non options.
|
388
|
+
#
|
389
|
+
# Returns nothing.
|
390
|
+
def process_item(items, index, &block)
|
391
|
+
item = items[index]
|
392
|
+
option, argument = extract_option(item) if item[0, 1] == '-'
|
876
393
|
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
raise InvalidOptionError, message
|
882
|
-
end
|
394
|
+
if option
|
395
|
+
option.count += 1 unless item[0, 5] == '--no-'
|
396
|
+
@trash << index
|
397
|
+
@triggered_options << option
|
883
398
|
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
end
|
889
|
-
end
|
890
|
-
end
|
399
|
+
if config[:multiple_switches] && argument
|
400
|
+
execute_multiple_switches(option, argument, index)
|
401
|
+
elsif option.expects_argument?
|
402
|
+
argument ||= items.at(index + 1)
|
891
403
|
|
892
|
-
|
893
|
-
|
894
|
-
#
|
895
|
-
# Raises if a flag expects an argument or strict mode is enabled and a
|
896
|
-
# flag was not found
|
897
|
-
def enable_multiple_switches(item)
|
898
|
-
item[1..-1].each_char do |switch|
|
899
|
-
option = @options[switch]
|
900
|
-
|
901
|
-
if option
|
902
|
-
if option.expects_argument?
|
903
|
-
raise MissingArgumentError, "'-#{switch}' expects an argument, used in multiple_switch context"
|
404
|
+
if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
|
405
|
+
raise MissingArgumentError, "#{option.key} expects an argument"
|
904
406
|
end
|
905
407
|
|
906
|
-
option
|
907
|
-
|
408
|
+
execute_option(option, argument, index)
|
409
|
+
elsif option.accepts_optional_argument?
|
410
|
+
argument ||= items.at(index + 1)
|
411
|
+
|
412
|
+
if argument && argument !~ /\A--?/
|
413
|
+
execute_option(option, argument, index)
|
414
|
+
else
|
415
|
+
option.call(nil)
|
416
|
+
end
|
908
417
|
else
|
909
|
-
|
418
|
+
option.call(nil)
|
910
419
|
end
|
420
|
+
else
|
421
|
+
@unknown_options << item if config[:strict] && item =~ /\A--?/
|
422
|
+
block.call(item) if block && !@trash.include?(index)
|
911
423
|
end
|
912
424
|
end
|
913
425
|
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
426
|
+
# Execute an option, firing off callbacks and assigning arguments.
|
427
|
+
#
|
428
|
+
# option - The Slop::Option object found by #process_item.
|
429
|
+
# argument - The argument Object to assign to this option.
|
430
|
+
# index - The current Integer index of the object we're processing.
|
431
|
+
# item - The optional String item we're processing.
|
432
|
+
#
|
433
|
+
# Returns nothing.
|
434
|
+
def execute_option(option, argument, index, item = nil)
|
435
|
+
if !option
|
436
|
+
if config[:multiple_switches] && config[:strict]
|
437
|
+
raise InvalidOptionError, "Unknown option -#{item}"
|
438
|
+
end
|
439
|
+
return
|
440
|
+
end
|
918
441
|
|
919
|
-
|
920
|
-
|
921
|
-
lines << line
|
922
|
-
line = ''
|
923
|
-
end
|
442
|
+
@trash << index + 1
|
443
|
+
option.value = argument
|
924
444
|
|
925
|
-
|
926
|
-
|
927
|
-
|
445
|
+
if option.match? && !argument.match(option.config[:match])
|
446
|
+
raise InvalidArgumentError, "#{argument} is an invalid argument"
|
447
|
+
end
|
928
448
|
|
929
|
-
|
930
|
-
end.join("\n")
|
449
|
+
option.call(option.value)
|
931
450
|
end
|
932
451
|
|
933
|
-
#
|
934
|
-
#
|
935
|
-
#
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
452
|
+
# Execute a `-abc` type option where a, b and c are all options. This
|
453
|
+
# method is only executed if the multiple_switches argument is true.
|
454
|
+
#
|
455
|
+
# option - The first Option object.
|
456
|
+
# argument - The argument to this option. (Split into multiple Options).
|
457
|
+
# index - The index of the current item being processed.
|
458
|
+
#
|
459
|
+
# Returns nothing.
|
460
|
+
def execute_multiple_switches(option, argument, index)
|
461
|
+
execute_option(option, argument, index)
|
462
|
+
argument.split('').each do |key|
|
463
|
+
opt = fetch_option(key)
|
464
|
+
opt.count = 1
|
465
|
+
execute_option(opt, argument, index, key)
|
940
466
|
end
|
467
|
+
end
|
468
|
+
|
469
|
+
# Extract an option from a flag.
|
470
|
+
#
|
471
|
+
# flag - The flag key used to extract an option.
|
472
|
+
#
|
473
|
+
# Returns an Array of [option, argument].
|
474
|
+
def extract_option(flag)
|
475
|
+
option = fetch_option(flag)
|
476
|
+
option ||= fetch_option(flag.downcase) if config[:ignore_case]
|
941
477
|
|
942
478
|
unless option
|
943
|
-
case
|
944
|
-
when /\A-[
|
945
|
-
|
946
|
-
enable_multiple_switches(item)
|
947
|
-
else
|
948
|
-
flag, argument = flag.split('', 2)
|
949
|
-
option = @options[flag]
|
950
|
-
end
|
951
|
-
when /\A--([^=]+)=(.+)\z/
|
952
|
-
option, argument = @options[$1], $2
|
953
|
-
when /\A--no-(.+)\z/
|
954
|
-
option = @options[$1]
|
955
|
-
option.force_argument_value(false) if option
|
479
|
+
case flag
|
480
|
+
when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
|
481
|
+
option, argument = fetch_option($1), ($2 || false)
|
956
482
|
end
|
957
483
|
end
|
958
484
|
|
959
485
|
[option, argument]
|
960
486
|
end
|
961
487
|
|
962
|
-
#
|
963
|
-
#
|
964
|
-
#
|
965
|
-
#
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
@io.puts "Command '#{str}' is ambiguous:"
|
977
|
-
@io.puts " " + cmds.map(&:to_s).sort.join(', ')
|
978
|
-
else
|
979
|
-
command = cmds.shift
|
980
|
-
end
|
981
|
-
end
|
982
|
-
end
|
983
|
-
|
984
|
-
if command
|
985
|
-
items.shift
|
986
|
-
opts = @commands[command]
|
987
|
-
delete ? opts.parse!(items) : opts.parse(items)
|
988
|
-
opts.execute(items.reject { |i| i == '--' })
|
488
|
+
# Autocreate an option on the fly. See the :autocreate Slop config option.
|
489
|
+
#
|
490
|
+
# items - The Array of items we're parsing.
|
491
|
+
# index - The current Integer index for the item we're processing.
|
492
|
+
#
|
493
|
+
# Returns nothing.
|
494
|
+
def autocreate(items, index)
|
495
|
+
flag = items[index]
|
496
|
+
unless present?(flag)
|
497
|
+
option = build_option(Array(flag))
|
498
|
+
argument = items[index + 1]
|
499
|
+
option.config[:argument] = (argument && argument !~ /\A--?/)
|
500
|
+
option.config[:autocreated] = true
|
501
|
+
@options << option
|
989
502
|
end
|
990
503
|
end
|
991
504
|
|
992
|
-
#
|
993
|
-
#
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
505
|
+
# Build an option from a list of objects.
|
506
|
+
#
|
507
|
+
# objects - An Array of objects used to build this option.
|
508
|
+
#
|
509
|
+
# Returns a new instance of Slop::Option.
|
510
|
+
def build_option(objects, &block)
|
511
|
+
config = {}
|
512
|
+
config[:argument] = true if @config[:arguments]
|
513
|
+
config[:optional_argument] = true if @config[:optional_arguments]
|
514
|
+
|
515
|
+
short = extract_short_flag(objects, config)
|
516
|
+
long = extract_long_flag(objects, config)
|
517
|
+
desc = objects[0].respond_to?(:to_str) ? objects.shift : nil
|
518
|
+
config = config.merge!(objects.last) if objects.last.is_a?(Hash)
|
519
|
+
|
520
|
+
Option.new(self, short, long, desc, config, &block)
|
1001
521
|
end
|
1002
522
|
|
1003
|
-
#
|
1004
|
-
#
|
1005
|
-
#
|
1006
|
-
#
|
1007
|
-
|
1008
|
-
|
1009
|
-
def clean_options(args)
|
1010
|
-
options = []
|
1011
|
-
extras = {}
|
1012
|
-
|
1013
|
-
if klass = args.find { |a| a.is_a?(Class) }
|
1014
|
-
extras[:as] = klass
|
1015
|
-
args.delete klass
|
1016
|
-
end
|
523
|
+
# Extract the short flag from an item.
|
524
|
+
#
|
525
|
+
# objects - The Array of objects passed from #build_option.
|
526
|
+
# config - The Hash of configuration options built in #build_option.
|
527
|
+
def extract_short_flag(objects, config)
|
528
|
+
flag = clean(objects.first)
|
1017
529
|
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
short.chop!
|
530
|
+
if flag.size == 2 && flag[-1, 1] == '='
|
531
|
+
config[:argument] = true
|
532
|
+
flag.chop!
|
1022
533
|
end
|
1023
534
|
|
1024
|
-
if
|
1025
|
-
|
1026
|
-
|
1027
|
-
else
|
1028
|
-
options.push nil
|
535
|
+
if flag.size == 1
|
536
|
+
objects.shift
|
537
|
+
flag
|
1029
538
|
end
|
539
|
+
end
|
1030
540
|
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
extras[:argument] = true if long.to_s[-1, 1] == '='
|
1043
|
-
options.push args.shift.to_s.sub(/\A--?/, '').sub(/\=\z/, '')
|
1044
|
-
else
|
1045
|
-
options.push nil
|
1046
|
-
end
|
541
|
+
# Extract the long flag from an item.
|
542
|
+
#
|
543
|
+
# objects - The Array of objects passed from #build_option.
|
544
|
+
# config - The Hash of configuration options built in #build_option.
|
545
|
+
def extract_long_flag(objects, config)
|
546
|
+
flag = objects.first.to_s
|
547
|
+
if flag =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\??\z/
|
548
|
+
config[:argument] = true if flag[-1, 1] == '='
|
549
|
+
config[:optional_argument] = true if flag[-2, 2] == '=?'
|
550
|
+
objects.shift
|
551
|
+
clean(flag).sub(/\=\??\z/, '')
|
1047
552
|
end
|
553
|
+
end
|
1048
554
|
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
555
|
+
# Remove any leading -- characters from a string.
|
556
|
+
#
|
557
|
+
# object - The Object we want to cast to a String and clean.
|
558
|
+
#
|
559
|
+
# Returns the newly cleaned String with leading -- characters removed.
|
560
|
+
def clean(object)
|
561
|
+
object.to_s.sub(/\A--?/, '')
|
1052
562
|
end
|
1053
|
-
|
563
|
+
|
564
|
+
end
|