slop 3.6.0 → 4.0.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/.travis.yml +1 -4
- data/LICENSE +1 -1
- data/README.md +179 -131
- data/lib/slop.rb +34 -665
- data/lib/slop/error.rb +20 -0
- data/lib/slop/option.rb +84 -181
- data/lib/slop/options.rb +141 -0
- data/lib/slop/parser.rb +111 -0
- data/lib/slop/result.rb +79 -0
- data/lib/slop/types.rb +52 -0
- data/slop.gemspec +6 -3
- data/test/error_test.rb +31 -0
- data/test/option_test.rb +16 -137
- data/test/options_test.rb +79 -0
- data/test/parser_test.rb +65 -0
- data/test/result_test.rb +85 -0
- data/test/test_helper.rb +6 -0
- data/test/types_test.rb +78 -0
- metadata +30 -21
- data/CHANGES.md +0 -309
- data/lib/slop/commands.rb +0 -196
- data/test/commands_test.rb +0 -26
- data/test/helper.rb +0 -12
- data/test/slop_test.rb +0 -518
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52a2290f6608ad9ab25b6b4b07dd6ec213421edf
|
4
|
+
data.tar.gz: f752d959151a6f15f978922b389c2952553dfbc9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6d233481d6ce8340a7ee563fb615187f3152f27211bf00f07ab006ec140d7f61ab427c92c3cb6a228351494ad5a7a5c8ee65d297af9f42c888d61bf240c4b6c
|
7
|
+
data.tar.gz: 97fcb2ff7f34071efa6b0b5091c6cc6058cc0c1d06e7f9669a27df4afb924fe083b3b1813cebfc3b761911c1d7230a95720ef8a298dfd645dfd1f99d9403a5c4
|
data/.travis.yml
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -2,201 +2,249 @@ Slop
|
|
2
2
|
====
|
3
3
|
|
4
4
|
Slop is a simple option parser with an easy to remember syntax and friendly API.
|
5
|
-
|
5
|
+
|
6
|
+
Version 4 of Slop is aimed at Ruby 2.0 or later. Please use
|
7
|
+
[Version 3](https://github.com/leejarvis/slop/tree/v3) for Ruby 1.9 support.
|
6
8
|
|
7
9
|
[](http://travis-ci.org/leejarvis/slop)
|
8
10
|
|
11
|
+
Installation
|
12
|
+
------------
|
13
|
+
|
14
|
+
gem install slop
|
15
|
+
|
9
16
|
Usage
|
10
17
|
-----
|
11
18
|
|
12
19
|
```ruby
|
13
|
-
opts = Slop.parse do
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
on '
|
20
|
+
opts = Slop.parse do |o|
|
21
|
+
o.string '-h', '--host', 'a hostname'
|
22
|
+
o.integer '--port', 'custom port', default: 80
|
23
|
+
o.bool '-v', '--verbose', 'enable verbose mode'
|
24
|
+
o.bool '-q', '--quiet', 'suppress output (quiet mode)'
|
25
|
+
o.on '--version', 'print the version' do
|
26
|
+
puts Slop::VERSION
|
27
|
+
exit
|
28
|
+
end
|
19
29
|
end
|
20
30
|
|
21
|
-
|
22
|
-
opts.verbose? #=> true
|
23
|
-
opts.password? #=> false
|
24
|
-
opts[:name] #=> 'lee'
|
25
|
-
opts.to_hash #=> {:name=>"Lee", :password=>nil, :verbose=>true}
|
26
|
-
```
|
31
|
+
ARGV #=> -v --host 192.168.0.1
|
27
32
|
|
28
|
-
|
29
|
-
|
33
|
+
opts[:host] #=> 192.168.0.1
|
34
|
+
opts.verbose? #=> true
|
35
|
+
opts.quiet? #=> false
|
30
36
|
|
31
|
-
|
37
|
+
opts.to_hash #=> { host: "192.168.0.1", port: 80, verbose: true, quiet: false }
|
38
|
+
```
|
32
39
|
|
33
|
-
|
34
|
-
|
40
|
+
Option types
|
41
|
+
------------
|
35
42
|
|
36
|
-
|
37
|
-
can see this by calling `opts.help` or simply `puts opts`.
|
43
|
+
Built in Option types are as follows:
|
38
44
|
|
39
|
-
|
40
|
-
|
45
|
+
```ruby
|
46
|
+
o.string #=> Slop::StringOption, expects an argument
|
47
|
+
o.bool #=> Slop::BoolOption, no argument, aliased to BooleanOption
|
48
|
+
o.integer #=> Slop::IntegerOption, expects an argument, aliased to IntOption
|
49
|
+
o.array #=> Slop::ArrayOption, expects an argument
|
50
|
+
o.null #=> Slop::NullOption, no argument and ignored from `to_hash`
|
51
|
+
o.on #=> alias for o.null
|
52
|
+
```
|
41
53
|
|
42
|
-
|
54
|
+
You can see all built in types in `slop/types.rb`. Suggestions or pull requests
|
55
|
+
for more types are welcome.
|
43
56
|
|
44
|
-
|
45
|
-
|
46
|
-
| strict | Enable strict mode. Slop will raise an `InvalidOptionError` for unkown options. | `false` |
|
47
|
-
| help | Automatically add the `--help` option. | `false` |
|
48
|
-
| banner | Set the help banner text. | `nil` |
|
49
|
-
| ignore_case | When enabled, `-A` will look for the `-a` option if `-A` does not exist. | `false` |
|
50
|
-
| autocreate | Autocreate options on the fly. | `false` |
|
51
|
-
| arguments | Force all options to expect arguments. | `false` |
|
52
|
-
| optional_arguments | Force all options to accept optional arguments. | `false` |
|
53
|
-
| multiple_switches | When disabled, Slop will parse `-abc` as the option `a` with the argument `bc` rather than 3 separate options. | `true` |
|
57
|
+
Advanced Usage
|
58
|
+
--------------
|
54
59
|
|
55
|
-
|
56
|
-
|
60
|
+
This example is really just to describe how the underlying API works.
|
61
|
+
It's not necessarily the best way to do it.
|
57
62
|
|
58
63
|
```ruby
|
59
|
-
opts = Slop.
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
opts
|
64
|
-
|
65
|
-
opts
|
64
|
+
opts = Slop::Options.new
|
65
|
+
opts.banner = "usage: connect [options] ..."
|
66
|
+
opts.separator ""
|
67
|
+
opts.separator "Connection options:"
|
68
|
+
opts.string "-H", "--hostname", "a hostname"
|
69
|
+
opts.int "-p", "--port", "a port", default: 80
|
70
|
+
opts.separator ""
|
71
|
+
opts.separator "Extra options:"
|
72
|
+
opts.array "--files", "a list of files to import"
|
73
|
+
opts.bool "-v", "--verbose", "enable verbose mode"
|
74
|
+
|
75
|
+
parser = Slop::Parser.new(opts)
|
76
|
+
result = parser.parse(["--hostname", "192.168.0.1"])
|
77
|
+
|
78
|
+
result.to_hash #=> { hostname: "192.168.0.1", port: 80,
|
79
|
+
# files: [], verbose: false }
|
80
|
+
|
81
|
+
puts opts # prints out help
|
66
82
|
```
|
67
83
|
|
68
|
-
|
84
|
+
Arguments
|
85
|
+
---------
|
86
|
+
|
87
|
+
It's common to want to retrieve an array of arguments that were not processed
|
88
|
+
by the parser (i.e options or consumed arguments). You can do that with the
|
89
|
+
`Result#arguments` method:
|
69
90
|
|
70
91
|
```ruby
|
71
|
-
|
72
|
-
|
92
|
+
args = %w(connect --host google.com GET)
|
93
|
+
opts = Slop.parse args do |o|
|
94
|
+
o.string '--host'
|
73
95
|
end
|
74
|
-
|
75
|
-
opts
|
96
|
+
|
97
|
+
p opts.arguments #=> ["connect", "GET"] # also aliased to `args`
|
76
98
|
```
|
77
99
|
|
78
|
-
|
100
|
+
Arrays
|
79
101
|
------
|
80
102
|
|
103
|
+
Slop has a built in `ArrayOption` for handling array values:
|
104
|
+
|
81
105
|
```ruby
|
82
|
-
opts = Slop.parse do
|
83
|
-
|
106
|
+
opts = Slop.parse do |o|
|
107
|
+
# the delimiter defaults to ','
|
108
|
+
o.array '--files', 'a list of files', delimiter: ','
|
84
109
|
end
|
85
|
-
# ruby run.rb --range 1..10
|
86
|
-
opts[:range] #=> 1..10
|
87
|
-
# ruby run.rb --range 1...10
|
88
|
-
opts[:range] #=> 1...10
|
89
|
-
# ruby run.rb --range 1-10
|
90
|
-
opts[:range] #=> 1..10
|
91
|
-
# ruby run.rb --range 1,10
|
92
|
-
opts[:range] #=> 1..10
|
93
|
-
```
|
94
|
-
|
95
|
-
Autocreate
|
96
|
-
----------
|
97
110
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
```ruby
|
103
|
-
# ruby run.rb --foo bar --baz --name lee
|
104
|
-
opts = Slop.parse(autocreate: true)
|
105
|
-
opts.to_hash #=> {:foo=>"bar", :baz=>true, :name=>"lee"}
|
106
|
-
opts.fetch_option(:name).expects_argument? #=> true
|
111
|
+
# both of these will return o[:files] as ["foo.txt", "bar.rb"]:
|
112
|
+
# --files foo.txt,bar.rb
|
113
|
+
# --files foo.txt --files bar.rb
|
107
114
|
```
|
108
115
|
|
109
|
-
|
110
|
-
|
116
|
+
Custom option types
|
117
|
+
-------------------
|
111
118
|
|
112
|
-
Slop
|
119
|
+
Slop uses option type classes for every new option added. They default to the
|
120
|
+
`NullOption`. When you type `o.array` Slop looks for an option called
|
121
|
+
`Slop::ArrayOption`. This class must contain at least 1 method, `call`. This
|
122
|
+
method is executed at parse time, and the return value of this method is
|
123
|
+
used for the option value. We can use this to build custom option types:
|
113
124
|
|
114
125
|
```ruby
|
115
|
-
|
116
|
-
|
117
|
-
|
126
|
+
module Slop
|
127
|
+
class PathOption < Option
|
128
|
+
def call(value)
|
129
|
+
Pathname.new(value)
|
130
|
+
end
|
118
131
|
end
|
132
|
+
end
|
119
133
|
|
120
|
-
|
121
|
-
|
122
|
-
|
134
|
+
opts = Slop.parse %w(--path ~/) do |o|
|
135
|
+
o.path '--path', 'a custom path name'
|
136
|
+
end
|
123
137
|
|
124
|
-
|
125
|
-
|
138
|
+
p opts[:path] #=> #<Pathname:~/>
|
139
|
+
```
|
140
|
+
|
141
|
+
Custom options can also implement a `finish` method. This method by default
|
142
|
+
does nothing, but it's executed once *all* options have been parsed. This
|
143
|
+
allows us to go back and mutate state without having to rely on options
|
144
|
+
being parsed in a particular order. Here's an example:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
module Slop
|
148
|
+
class FilesOption < ArrayOption
|
149
|
+
def finish(opts)
|
150
|
+
if opts.expand?
|
151
|
+
self.value = value.map { |f| File.expand_path(f) }
|
152
|
+
end
|
126
153
|
end
|
127
154
|
end
|
128
155
|
end
|
129
156
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
157
|
+
opts = Slop.parse %w(--files foo.txt,bar.rb -e) do |o|
|
158
|
+
o.files '--files', 'an array of files'
|
159
|
+
o.bool '-e', '--expand', 'if used, list of files will be expanded'
|
160
|
+
end
|
161
|
+
|
162
|
+
p opts[:files] #=> ["/full/path/foo.txt", "/full/path/bar.rb"]
|
136
163
|
```
|
137
164
|
|
138
|
-
|
139
|
-
|
165
|
+
Errors
|
166
|
+
------
|
140
167
|
|
141
|
-
|
168
|
+
Slop will raise errors for the following:
|
142
169
|
|
143
|
-
|
144
|
-
|
145
|
-
opts = Slop.parse! do
|
146
|
-
on :foo
|
147
|
-
end
|
148
|
-
```
|
170
|
+
* An option used without an argument when it expects one: `Slop::MissingArgument`
|
171
|
+
* An option used that Slop doesn't know about: `Slop::UnknownOption`
|
149
172
|
|
150
|
-
|
173
|
+
These errors inherit from `Slop::Error`, so you can rescue them all.
|
174
|
+
Alternatively you can suppress these errors with the `suppress_errors` config
|
175
|
+
option:
|
151
176
|
|
152
|
-
```
|
153
|
-
|
154
|
-
|
177
|
+
```ruby
|
178
|
+
opts = Slop.parse suppress_errors: true do
|
179
|
+
o.string '-name'
|
180
|
+
end
|
155
181
|
|
156
|
-
|
157
|
-
opts.to_hash = { :foo => true }
|
182
|
+
# or per option:
|
158
183
|
|
159
|
-
|
184
|
+
opts = Slop.parse do
|
185
|
+
o.string '-host', suppress_errors: true
|
186
|
+
o.int '-port'
|
187
|
+
end
|
160
188
|
```
|
161
189
|
|
162
|
-
|
163
|
-
|
190
|
+
Printing help
|
191
|
+
-------------
|
164
192
|
|
165
|
-
|
166
|
-
|
167
|
-
gather a bunch of key/value options, usually you would do something like this:
|
193
|
+
The return value of `Slop.parse` is a `Slop::Result` which provides a nice
|
194
|
+
help string to display your options. Just `puts opts` or call `opts.to_s`:
|
168
195
|
|
169
196
|
```ruby
|
170
|
-
|
197
|
+
opts = Slop.parse do |o|
|
198
|
+
o.string '-h', '--host', 'hostname'
|
199
|
+
o.int '-p', '--port', 'port (default: 80)', default: 80
|
200
|
+
o.string '--username'
|
201
|
+
o.separator ''
|
202
|
+
o.separator 'other options:'
|
203
|
+
o.bool '--quiet', 'suppress output'
|
204
|
+
o.on '-v', '--version' do
|
205
|
+
puts "1.1.1"
|
206
|
+
end
|
207
|
+
end
|
171
208
|
|
172
|
-
|
209
|
+
puts opts
|
210
|
+
```
|
173
211
|
|
174
|
-
|
175
|
-
opt.on('-n', '--name NAME', 'Your name') do |name|
|
176
|
-
things[:name] = name
|
177
|
-
end
|
212
|
+
Output:
|
178
213
|
|
179
|
-
|
180
|
-
|
181
|
-
|
214
|
+
```
|
215
|
+
% ruby run.rb
|
216
|
+
usage: run.rb [options]
|
217
|
+
-h, --host hostname
|
218
|
+
-p, --port port (default: 80)
|
219
|
+
--username
|
220
|
+
|
221
|
+
other options:
|
222
|
+
--quiet suppress output
|
223
|
+
-v, --version
|
224
|
+
```
|
182
225
|
|
183
|
-
|
184
|
-
end
|
226
|
+
This method takes an optional `prefix` value, which defaults to `" " * 4`:
|
185
227
|
|
186
|
-
|
187
|
-
|
228
|
+
```
|
229
|
+
puts opts.to_s(prefix: " ")
|
188
230
|
```
|
189
231
|
|
190
|
-
|
191
|
-
|
232
|
+
It'll deal with aligning your descriptions according to the longest option
|
233
|
+
flag.
|
192
234
|
|
193
|
-
|
194
|
-
require 'slop'
|
235
|
+
Here's an example of adding your own help option:
|
195
236
|
|
196
|
-
|
197
|
-
|
198
|
-
|
237
|
+
```ruby
|
238
|
+
o.on '--help' do
|
239
|
+
puts o
|
240
|
+
exit
|
199
241
|
end
|
200
|
-
|
201
|
-
opts.to_hash #=> { :name => 'lee', :age => 105 }
|
202
242
|
```
|
243
|
+
|
244
|
+
Commands
|
245
|
+
--------
|
246
|
+
|
247
|
+
As of version 4, Slop does not have built in support for git-style subcommands.
|
248
|
+
You can use version 3 of Slop (see `v3` branch). I also expect there to be some
|
249
|
+
external libraries released soon that wrap around Slop to provide support for
|
250
|
+
this feature. I'll update this document when that happens.
|
data/lib/slop.rb
CHANGED
@@ -1,687 +1,56 @@
|
|
1
1
|
require 'slop/option'
|
2
|
-
require 'slop/
|
2
|
+
require 'slop/options'
|
3
|
+
require 'slop/parser'
|
4
|
+
require 'slop/result'
|
5
|
+
require 'slop/types'
|
6
|
+
require 'slop/error'
|
3
7
|
|
4
|
-
|
5
|
-
|
8
|
+
module Slop
|
9
|
+
# The current version of Slop, of course.
|
10
|
+
VERSION = '4.0.0'
|
6
11
|
|
7
|
-
|
8
|
-
|
9
|
-
# The main Error class, all Exception classes inherit from this class.
|
10
|
-
class Error < StandardError; end
|
11
|
-
|
12
|
-
# Raised when an option argument is expected but none are given.
|
13
|
-
class MissingArgumentError < Error; end
|
14
|
-
|
15
|
-
# Raised when an option is expected/required but not present.
|
16
|
-
class MissingOptionError < Error; end
|
17
|
-
|
18
|
-
# Raised when an argument does not match its intended match constraint.
|
19
|
-
class InvalidArgumentError < Error; end
|
20
|
-
|
21
|
-
# Raised when an invalid option is found and the strict flag is enabled.
|
22
|
-
class InvalidOptionError < Error; end
|
23
|
-
|
24
|
-
# Raised when an invalid command is found and the strict flag is enabled.
|
25
|
-
class InvalidCommandError < Error; end
|
26
|
-
|
27
|
-
# Returns a default Hash of configuration options this Slop instance uses.
|
28
|
-
DEFAULT_OPTIONS = {
|
29
|
-
:strict => false,
|
30
|
-
:help => false,
|
31
|
-
:banner => nil,
|
32
|
-
:ignore_case => false,
|
33
|
-
:autocreate => false,
|
34
|
-
:arguments => false,
|
35
|
-
:optional_arguments => false,
|
36
|
-
:multiple_switches => true,
|
37
|
-
:longest_flag => 0
|
38
|
-
}
|
39
|
-
|
40
|
-
class << self
|
41
|
-
|
42
|
-
# items - The Array of items to extract options from (default: ARGV).
|
43
|
-
# config - The Hash of configuration options to send to Slop.new().
|
44
|
-
# block - An optional block used to add options.
|
45
|
-
#
|
46
|
-
# Examples:
|
47
|
-
#
|
48
|
-
# Slop.parse(ARGV, :help => true) do
|
49
|
-
# on '-n', '--name', 'Your username', :argument => true
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# Returns a new instance of Slop.
|
53
|
-
def parse(items = ARGV, config = {}, &block)
|
54
|
-
parse! items.dup, config, &block
|
55
|
-
end
|
56
|
-
|
57
|
-
# items - The Array of items to extract options from (default: ARGV).
|
58
|
-
# config - The Hash of configuration options to send to Slop.new().
|
59
|
-
# block - An optional block used to add options.
|
60
|
-
#
|
61
|
-
# Returns a new instance of Slop.
|
62
|
-
def parse!(items = ARGV, config = {}, &block)
|
63
|
-
config, items = items, ARGV if items.is_a?(Hash) && config.empty?
|
64
|
-
slop = new config, &block
|
65
|
-
slop.parse! items
|
66
|
-
slop
|
67
|
-
end
|
68
|
-
|
69
|
-
# Build a Slop object from a option specification.
|
70
|
-
#
|
71
|
-
# This allows you to design your options via a simple String rather
|
72
|
-
# than programatically. Do note though that with this method, you're
|
73
|
-
# unable to pass any advanced options to the on() method when creating
|
74
|
-
# options.
|
75
|
-
#
|
76
|
-
# string - The optspec String
|
77
|
-
# config - A Hash of configuration options to pass to Slop.new
|
78
|
-
#
|
79
|
-
# Examples:
|
80
|
-
#
|
81
|
-
# opts = Slop.optspec(<<-SPEC)
|
82
|
-
# ruby foo.rb [options]
|
83
|
-
# ---
|
84
|
-
# n,name= Your name
|
85
|
-
# a,age= Your age
|
86
|
-
# A,auth Sign in with auth
|
87
|
-
# p,passcode= Your secret pass code
|
88
|
-
# SPEC
|
89
|
-
#
|
90
|
-
# opts.fetch_option(:name).description #=> "Your name"
|
91
|
-
#
|
92
|
-
# Returns a new instance of Slop.
|
93
|
-
def optspec(string, config = {})
|
94
|
-
warn "[DEPRECATED] `Slop.optspec` is deprecated and will be removed in version 4"
|
95
|
-
config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/]
|
96
|
-
lines = optspec.split("\n").reject(&:empty?)
|
97
|
-
opts = Slop.new(config)
|
98
|
-
|
99
|
-
lines.each do |line|
|
100
|
-
opt, description = line.split(' ', 2)
|
101
|
-
short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
|
102
|
-
opt = opts.on(short, long, description)
|
103
|
-
|
104
|
-
if long && long.end_with?('=')
|
105
|
-
long.sub!(/\=$/, '')
|
106
|
-
opt.config[:argument] = true
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
opts
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
# The Hash of configuration options for this Slop instance.
|
116
|
-
attr_reader :config
|
117
|
-
|
118
|
-
# The Array of Slop::Option objects tied to this Slop instance.
|
119
|
-
attr_reader :options
|
120
|
-
|
121
|
-
# The Hash of sub-commands for this Slop instance.
|
122
|
-
attr_reader :commands
|
123
|
-
|
124
|
-
# Create a new instance of Slop and optionally build options via a block.
|
125
|
-
#
|
126
|
-
# config - A Hash of configuration options.
|
127
|
-
# block - An optional block used to specify options.
|
128
|
-
def initialize(config = {}, &block)
|
129
|
-
@config = DEFAULT_OPTIONS.merge(config)
|
130
|
-
@options = []
|
131
|
-
@commands = {}
|
132
|
-
@trash = []
|
133
|
-
@triggered_options = []
|
134
|
-
@unknown_options = []
|
135
|
-
@callbacks = {}
|
136
|
-
@separators = {}
|
137
|
-
@runner = nil
|
138
|
-
@command = config.delete(:command)
|
139
|
-
|
140
|
-
if block_given?
|
141
|
-
block.arity == 1 ? yield(self) : instance_eval(&block)
|
142
|
-
end
|
143
|
-
|
144
|
-
if config[:help]
|
145
|
-
on('-h', '--help', 'Display this help message.', :tail => true) do
|
146
|
-
puts help
|
147
|
-
exit
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Is strict mode enabled?
|
153
|
-
#
|
154
|
-
# Returns true if strict mode is enabled, false otherwise.
|
155
|
-
def strict?
|
156
|
-
config[:strict]
|
157
|
-
end
|
158
|
-
|
159
|
-
# Set the banner.
|
160
|
-
#
|
161
|
-
# banner - The String to set the banner.
|
162
|
-
def banner=(banner)
|
163
|
-
config[:banner] = banner
|
164
|
-
end
|
165
|
-
|
166
|
-
# Get or set the banner.
|
167
|
-
#
|
168
|
-
# banner - The String to set the banner.
|
169
|
-
#
|
170
|
-
# Returns the banner String.
|
171
|
-
def banner(banner = nil)
|
172
|
-
config[:banner] = banner if banner
|
173
|
-
config[:banner]
|
174
|
-
end
|
175
|
-
|
176
|
-
# Set the description (used for commands).
|
177
|
-
#
|
178
|
-
# desc - The String to set the description.
|
179
|
-
def description=(desc)
|
180
|
-
config[:description] = desc
|
181
|
-
end
|
182
|
-
|
183
|
-
# Get or set the description (used for commands).
|
184
|
-
#
|
185
|
-
# desc - The String to set the description.
|
186
|
-
#
|
187
|
-
# Returns the description String.
|
188
|
-
def description(desc = nil)
|
189
|
-
config[:description] = desc if desc
|
190
|
-
config[:description]
|
191
|
-
end
|
192
|
-
|
193
|
-
# Add a new command.
|
194
|
-
#
|
195
|
-
# command - The Symbol or String used to identify this command.
|
196
|
-
# options - A Hash of configuration options (see Slop::new)
|
197
|
-
#
|
198
|
-
# Returns a new instance of Slop mapped to this command.
|
199
|
-
def command(command, options = {}, &block)
|
200
|
-
options = @config.merge(options)
|
201
|
-
@commands[command.to_s] = Slop.new(options.merge(:command => command.to_s), &block)
|
202
|
-
end
|
203
|
-
|
204
|
-
# Parse a list of items, executing and gathering options along the way.
|
205
|
-
#
|
206
|
-
# items - The Array of items to extract options from (default: ARGV).
|
207
|
-
# block - An optional block which when used will yield non options.
|
208
|
-
#
|
209
|
-
# Returns an Array of original items.
|
210
|
-
def parse(items = ARGV, &block)
|
211
|
-
parse! items.dup, &block
|
212
|
-
items
|
213
|
-
end
|
214
|
-
|
215
|
-
# Parse a list of items, executing and gathering options along the way.
|
216
|
-
# unlike parse() this method will remove any options and option arguments
|
217
|
-
# from the original Array.
|
218
|
-
#
|
219
|
-
# items - The Array of items to extract options from (default: ARGV).
|
220
|
-
# block - An optional block which when used will yield non options.
|
221
|
-
#
|
222
|
-
# Returns an Array of original items with options removed.
|
223
|
-
def parse!(items = ARGV, &block)
|
224
|
-
if items.empty? && @callbacks[:empty]
|
225
|
-
@callbacks[:empty].each { |cb| cb.call(self) }
|
226
|
-
return items
|
227
|
-
end
|
228
|
-
|
229
|
-
# reset the trash so it doesn't carry over if you parse multiple
|
230
|
-
# times with the same instance
|
231
|
-
@trash.clear
|
232
|
-
|
233
|
-
if cmd = @commands[items[0]]
|
234
|
-
items.shift
|
235
|
-
return cmd.parse! items
|
236
|
-
end
|
237
|
-
|
238
|
-
items.each_with_index do |item, index|
|
239
|
-
@trash << index && break if item == '--'
|
240
|
-
autocreate(items, index) if config[:autocreate]
|
241
|
-
process_item(items, index, &block) unless @trash.include?(index)
|
242
|
-
end
|
243
|
-
items.reject!.with_index { |item, index| @trash.include?(index) }
|
244
|
-
|
245
|
-
missing_options = options.select { |opt| opt.required? && opt.count < 1 }
|
246
|
-
if missing_options.any?
|
247
|
-
raise MissingOptionError,
|
248
|
-
"Missing required option(s): #{missing_options.map(&:key).join(', ')}"
|
249
|
-
end
|
250
|
-
|
251
|
-
if @unknown_options.any?
|
252
|
-
raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
|
253
|
-
end
|
254
|
-
|
255
|
-
if @triggered_options.empty? && @callbacks[:no_options]
|
256
|
-
@callbacks[:no_options].each { |cb| cb.call(self) }
|
257
|
-
end
|
258
|
-
|
259
|
-
if @runner.respond_to?(:call)
|
260
|
-
@runner.call(self, items) unless config[:help] and present?(:help)
|
261
|
-
end
|
262
|
-
|
263
|
-
items
|
264
|
-
end
|
265
|
-
|
266
|
-
# Add an Option.
|
267
|
-
#
|
268
|
-
# objects - An Array with an optional Hash as the last element.
|
269
|
-
#
|
270
|
-
# Examples:
|
271
|
-
#
|
272
|
-
# on '-u', '--username=', 'Your username'
|
273
|
-
# on :v, :verbose, 'Enable verbose mode'
|
274
|
-
#
|
275
|
-
# Returns the created instance of Slop::Option.
|
276
|
-
def on(*objects, &block)
|
277
|
-
option = build_option(objects, &block)
|
278
|
-
original = options.find do |o|
|
279
|
-
o.long and o.long == option.long or o.short and o.short == option.short
|
280
|
-
end
|
281
|
-
options.delete(original) if original
|
282
|
-
options << option
|
283
|
-
option
|
284
|
-
end
|
285
|
-
alias option on
|
286
|
-
alias opt on
|
287
|
-
|
288
|
-
# Fetch an options argument value.
|
289
|
-
#
|
290
|
-
# key - The Symbol or String option short or long flag.
|
291
|
-
#
|
292
|
-
# Returns the Object value for this option, or nil.
|
293
|
-
def [](key)
|
294
|
-
option = fetch_option(key)
|
295
|
-
option.value if option
|
296
|
-
end
|
297
|
-
alias get []
|
298
|
-
|
299
|
-
# Returns a new Hash with option flags as keys and option values as values.
|
300
|
-
#
|
301
|
-
# include_commands - If true, merge options from all sub-commands.
|
302
|
-
def to_hash(include_commands = false)
|
303
|
-
hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
|
304
|
-
if include_commands
|
305
|
-
@commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) }
|
306
|
-
end
|
307
|
-
hash
|
308
|
-
end
|
309
|
-
alias to_h to_hash
|
310
|
-
|
311
|
-
# Enumerable interface. Yields each Slop::Option.
|
312
|
-
def each(&block)
|
313
|
-
options.each(&block)
|
314
|
-
end
|
315
|
-
|
316
|
-
# Specify code to be executed when these options are parsed.
|
317
|
-
#
|
318
|
-
# callable - An object responding to a call method.
|
319
|
-
#
|
320
|
-
# yields - The instance of Slop parsing these options
|
321
|
-
# An Array of unparsed arguments
|
12
|
+
# Parse an array of options (defaults to ARGV). Accepts an
|
13
|
+
# optional hash of configuration options and block.
|
322
14
|
#
|
323
15
|
# Example:
|
324
16
|
#
|
325
|
-
# Slop.parse do
|
326
|
-
#
|
327
|
-
#
|
328
|
-
# run do |opts, args|
|
329
|
-
# puts "Arguments: #{args.inspect}" if opts.verbose?
|
330
|
-
# end
|
17
|
+
# opts = Slop.parse(["-host", "localhost"]) do |o|
|
18
|
+
# o.string '-host', 'a hostname', default: '0.0.0.0'
|
331
19
|
# end
|
332
|
-
|
333
|
-
@runner = callable || block
|
334
|
-
unless @runner.respond_to?(:call)
|
335
|
-
raise ArgumentError, "You must specify a callable object or a block to #run"
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
|
-
# Check for an options presence.
|
340
|
-
#
|
341
|
-
# Examples:
|
20
|
+
# opts.to_hash #=> { host: 'localhost' }
|
342
21
|
#
|
343
|
-
#
|
344
|
-
|
345
|
-
|
346
|
-
#
|
347
|
-
# Returns true if all of the keys are present in the parsed arguments.
|
348
|
-
def present?(*keys)
|
349
|
-
keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
|
350
|
-
end
|
351
|
-
|
352
|
-
# Override this method so we can check if an option? method exists.
|
353
|
-
#
|
354
|
-
# Returns true if this option key exists in our list of options.
|
355
|
-
def respond_to_missing?(method_name, include_private = false)
|
356
|
-
options.any? { |o| o.key == method_name.to_s.chop } || super
|
357
|
-
end
|
358
|
-
|
359
|
-
# Fetch a list of options which were missing from the parsed list.
|
360
|
-
#
|
361
|
-
# Examples:
|
362
|
-
#
|
363
|
-
# opts = Slop.new do
|
364
|
-
# on :n, :name=
|
365
|
-
# on :p, :password=
|
366
|
-
# end
|
367
|
-
#
|
368
|
-
# opts.parse %w[ --name Lee ]
|
369
|
-
# opts.missing #=> ['password']
|
370
|
-
#
|
371
|
-
# Returns an Array of Strings representing missing options.
|
372
|
-
def missing
|
373
|
-
(options - @triggered_options).map(&:key)
|
374
|
-
end
|
375
|
-
|
376
|
-
# Fetch a Slop::Option object.
|
377
|
-
#
|
378
|
-
# key - The Symbol or String option key.
|
379
|
-
#
|
380
|
-
# Examples:
|
381
|
-
#
|
382
|
-
# opts.on(:foo, 'Something fooey', :argument => :optional)
|
383
|
-
# opt = opts.fetch_option(:foo)
|
384
|
-
# opt.class #=> Slop::Option
|
385
|
-
# opt.accepts_optional_argument? #=> true
|
386
|
-
#
|
387
|
-
# Returns an Option or nil if none were found.
|
388
|
-
def fetch_option(key)
|
389
|
-
options.find { |option| [option.long, option.short].include?(clean(key)) }
|
390
|
-
end
|
391
|
-
|
392
|
-
# Fetch a Slop object associated with this command.
|
393
|
-
#
|
394
|
-
# command - The String or Symbol name of the command.
|
395
|
-
#
|
396
|
-
# Examples:
|
397
|
-
#
|
398
|
-
# opts.command :foo do
|
399
|
-
# on :v, :verbose, 'Enable verbose mode'
|
400
|
-
# end
|
401
|
-
#
|
402
|
-
# # ruby run.rb foo -v
|
403
|
-
# opts.fetch_command(:foo).verbose? #=> true
|
404
|
-
def fetch_command(command)
|
405
|
-
@commands[command.to_s]
|
406
|
-
end
|
407
|
-
|
408
|
-
# Add a callback.
|
409
|
-
#
|
410
|
-
# label - The Symbol identifier to attach this callback.
|
411
|
-
#
|
412
|
-
# Returns nothing.
|
413
|
-
def add_callback(label, &block)
|
414
|
-
(@callbacks[label] ||= []) << block
|
415
|
-
end
|
416
|
-
|
417
|
-
# Add string separators between options.
|
418
|
-
#
|
419
|
-
# text - The String text to print.
|
420
|
-
def separator(text)
|
421
|
-
if @separators[options.size]
|
422
|
-
@separators[options.size] << "\n#{text}"
|
423
|
-
else
|
424
|
-
@separators[options.size] = text
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
# Print a handy Slop help string.
|
429
|
-
#
|
430
|
-
# Returns the banner followed by available option help strings.
|
431
|
-
def to_s
|
432
|
-
heads = options.reject(&:tail?)
|
433
|
-
tails = (options - heads)
|
434
|
-
opts = (heads + tails).select(&:help).map(&:to_s)
|
435
|
-
optstr = opts.each_with_index.map { |o, i|
|
436
|
-
(str = @separators[i + 1]) ? [o, str].join("\n") : o
|
437
|
-
}.join("\n")
|
438
|
-
|
439
|
-
if @commands.any?
|
440
|
-
optstr << "\n" if !optstr.empty?
|
441
|
-
optstr << "\nAvailable commands:\n\n"
|
442
|
-
optstr << commands_to_help
|
443
|
-
optstr << "\n\nSee `<command> --help` for more information on a specific command."
|
444
|
-
end
|
445
|
-
|
446
|
-
banner = config[:banner]
|
447
|
-
if banner.nil?
|
448
|
-
banner = "Usage: #{File.basename($0, '.*')}"
|
449
|
-
banner << " #{@command}" if @command
|
450
|
-
banner << " [command]" if @commands.any?
|
451
|
-
banner << " [options]"
|
452
|
-
end
|
453
|
-
if banner
|
454
|
-
"#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}"
|
455
|
-
else
|
456
|
-
optstr
|
457
|
-
end
|
458
|
-
end
|
459
|
-
alias help to_s
|
460
|
-
|
461
|
-
private
|
462
|
-
|
463
|
-
# Convenience method for present?(:option).
|
464
|
-
#
|
465
|
-
# Examples:
|
466
|
-
#
|
467
|
-
# opts.parse %( --verbose )
|
468
|
-
# opts.verbose? #=> true
|
469
|
-
# opts.other? #=> false
|
470
|
-
#
|
471
|
-
# Returns true if this option is present. If this method does not end
|
472
|
-
# with a ? character it will instead call super().
|
473
|
-
def method_missing(method, *args, &block)
|
474
|
-
meth = method.to_s
|
475
|
-
if meth.end_with?('?')
|
476
|
-
meth.chop!
|
477
|
-
present?(meth) || present?(meth.gsub('_', '-'))
|
478
|
-
else
|
479
|
-
super
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
# Process a list item, figure out if it's an option, execute any
|
484
|
-
# callbacks, assign any option arguments, and do some sanity checks.
|
485
|
-
#
|
486
|
-
# items - The Array of items to process.
|
487
|
-
# index - The current Integer index of the item we want to process.
|
488
|
-
# block - An optional block which when passed will yield non options.
|
489
|
-
#
|
490
|
-
# Returns nothing.
|
491
|
-
def process_item(items, index, &block)
|
492
|
-
return unless item = items[index]
|
493
|
-
option, argument = extract_option(item) if item.start_with?('-')
|
494
|
-
|
495
|
-
if option
|
496
|
-
option.count += 1 unless item.start_with?('--no-')
|
497
|
-
option.count += 1 if option.key[0, 3] == "no-"
|
498
|
-
@trash << index
|
499
|
-
@triggered_options << option
|
500
|
-
|
501
|
-
if option.expects_argument?
|
502
|
-
argument ||= items.at(index + 1)
|
503
|
-
|
504
|
-
if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
|
505
|
-
raise MissingArgumentError, "#{option.key} expects an argument"
|
506
|
-
end
|
507
|
-
|
508
|
-
execute_option(option, argument, index, item)
|
509
|
-
elsif option.accepts_optional_argument?
|
510
|
-
argument ||= items.at(index + 1)
|
511
|
-
|
512
|
-
if argument && argument =~ /\A([^\-?]|-\d)+/
|
513
|
-
execute_option(option, argument, index, item)
|
514
|
-
else
|
515
|
-
option.call(nil)
|
516
|
-
end
|
517
|
-
elsif config[:multiple_switches] && argument
|
518
|
-
execute_multiple_switches(option, argument, items, index)
|
519
|
-
else
|
520
|
-
option.value = option.count > 0
|
521
|
-
option.call(nil)
|
522
|
-
end
|
523
|
-
else
|
524
|
-
@unknown_options << item if strict? && item =~ /\A--?/
|
525
|
-
block.call(item) if block && !@trash.include?(index)
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
|
-
# Execute an option, firing off callbacks and assigning arguments.
|
530
|
-
#
|
531
|
-
# option - The Slop::Option object found by #process_item.
|
532
|
-
# argument - The argument Object to assign to this option.
|
533
|
-
# index - The current Integer index of the object we're processing.
|
534
|
-
# item - The optional String item we're processing.
|
535
|
-
#
|
536
|
-
# Returns nothing.
|
537
|
-
def execute_option(option, argument, index, item = nil)
|
538
|
-
if !option
|
539
|
-
if config[:multiple_switches] && strict?
|
540
|
-
raise InvalidOptionError, "Unknown option -#{item}"
|
541
|
-
end
|
542
|
-
return
|
543
|
-
end
|
544
|
-
|
545
|
-
if argument
|
546
|
-
unless item && item.end_with?("=#{argument}")
|
547
|
-
@trash << index + 1 unless option.argument_in_value
|
548
|
-
end
|
549
|
-
option.value = argument
|
550
|
-
else
|
551
|
-
option.value = option.count > 0
|
552
|
-
end
|
553
|
-
|
554
|
-
if option.match? && !argument.match(option.config[:match])
|
555
|
-
raise InvalidArgumentError, "#{argument} is an invalid argument"
|
556
|
-
end
|
557
|
-
|
558
|
-
option.call(option.value)
|
559
|
-
end
|
560
|
-
|
561
|
-
# Execute a `-abc` type option where a, b and c are all options. This
|
562
|
-
# method is only executed if the multiple_switches argument is true.
|
563
|
-
#
|
564
|
-
# option - The first Option object.
|
565
|
-
# argument - The argument to this option. (Split into multiple Options).
|
566
|
-
# items - The Array of items currently being parsed.
|
567
|
-
# index - The index of the current item being processed.
|
568
|
-
#
|
569
|
-
# Returns nothing.
|
570
|
-
def execute_multiple_switches(option, argument, items, index)
|
571
|
-
execute_option(option, nil, index)
|
572
|
-
flags = argument.split('')
|
573
|
-
flags.each do |key|
|
574
|
-
if opt = fetch_option(key)
|
575
|
-
opt.count += 1
|
576
|
-
if (opt.expects_argument? || opt.accepts_optional_argument?) &&
|
577
|
-
(flags[-1] == opt.key) && (val = items[index+1])
|
578
|
-
execute_option(opt, val, index, key)
|
579
|
-
else
|
580
|
-
execute_option(opt, nil, index, key)
|
581
|
-
end
|
582
|
-
else
|
583
|
-
raise InvalidOptionError, "Unknown option -#{key}" if strict?
|
584
|
-
end
|
585
|
-
end
|
22
|
+
# Returns a Slop::Result.
|
23
|
+
def self.parse(items = ARGV, **config, &block)
|
24
|
+
Options.new(config, &block).parse(items)
|
586
25
|
end
|
587
26
|
|
588
|
-
#
|
589
|
-
#
|
590
|
-
# flag - The flag key used to extract an option.
|
591
|
-
#
|
592
|
-
# Returns an Array of [option, argument].
|
593
|
-
def extract_option(flag)
|
594
|
-
option = fetch_option(flag)
|
595
|
-
option ||= fetch_option(flag.downcase) if config[:ignore_case]
|
596
|
-
option ||= fetch_option(flag.gsub(/([^-])-/, '\1_'))
|
597
|
-
|
598
|
-
unless option
|
599
|
-
case flag
|
600
|
-
when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
|
601
|
-
option, argument = fetch_option($1), ($2 || false)
|
602
|
-
option.argument_in_value = true if option
|
603
|
-
end
|
604
|
-
end
|
605
|
-
|
606
|
-
[option, argument]
|
607
|
-
end
|
608
|
-
|
609
|
-
# Autocreate an option on the fly. See the :autocreate Slop config option.
|
27
|
+
# Example:
|
610
28
|
#
|
611
|
-
#
|
612
|
-
#
|
29
|
+
# Slop.option_defined?(:string) #=> true
|
30
|
+
# Slop.option_defined?(:omg) #=> false
|
613
31
|
#
|
614
|
-
# Returns
|
615
|
-
def
|
616
|
-
|
617
|
-
if !fetch_option(flag) && !@trash.include?(index)
|
618
|
-
option = build_option(Array(flag))
|
619
|
-
argument = items[index + 1]
|
620
|
-
option.config[:argument] = (argument && argument !~ /\A--?/)
|
621
|
-
option.config[:autocreated] = true
|
622
|
-
options << option
|
623
|
-
end
|
32
|
+
# Returns true if an option is defined.
|
33
|
+
def self.option_defined?(name)
|
34
|
+
const_defined?(string_to_option(name.to_s))
|
624
35
|
end
|
625
36
|
|
626
|
-
#
|
627
|
-
#
|
628
|
-
# objects - An Array of objects used to build this option.
|
37
|
+
# Example:
|
629
38
|
#
|
630
|
-
#
|
631
|
-
|
632
|
-
config = {}
|
633
|
-
config[:argument] = true if @config[:arguments]
|
634
|
-
config[:optional_argument] = true if @config[:optional_arguments]
|
635
|
-
|
636
|
-
if objects.last.is_a?(Hash)
|
637
|
-
config.merge!(objects.pop)
|
638
|
-
end
|
639
|
-
|
640
|
-
short = extract_short_flag(objects, config)
|
641
|
-
long = extract_long_flag(objects, config)
|
642
|
-
desc = objects.shift if objects[0].respond_to?(:to_str)
|
643
|
-
|
644
|
-
Option.new(self, short, long, desc, config, &block)
|
645
|
-
end
|
646
|
-
|
647
|
-
def extract_short_flag(objects, config)
|
648
|
-
flag = objects[0].to_s
|
649
|
-
if flag =~ /\A-?\w=?\z/
|
650
|
-
config[:argument] ||= flag.end_with?('=')
|
651
|
-
objects.shift
|
652
|
-
flag.delete('-=')
|
653
|
-
end
|
654
|
-
end
|
655
|
-
|
656
|
-
# Extract the long flag from an item.
|
39
|
+
# Slop.string_to_option("string") #=> "StringOption"
|
40
|
+
# Slop.string_to_option("some_thing") #=> "SomeThingOption"
|
657
41
|
#
|
658
|
-
#
|
659
|
-
|
660
|
-
|
661
|
-
flag = objects.first.to_s
|
662
|
-
if flag =~ /\A(?:--?)?[a-zA-Z0-9][a-zA-Z0-9_.-]+\=?\??\z/
|
663
|
-
config[:argument] ||= true if flag.end_with?('=')
|
664
|
-
config[:optional_argument] = true if flag.end_with?('=?')
|
665
|
-
objects.shift
|
666
|
-
clean(flag).sub(/\=\??\z/, '')
|
667
|
-
end
|
42
|
+
# Returns a camel-cased class looking string with Option suffix.
|
43
|
+
def self.string_to_option(s)
|
44
|
+
s.gsub(/(?:^|_)([a-z])/) { $1.capitalize } + "Option"
|
668
45
|
end
|
669
46
|
|
670
|
-
#
|
47
|
+
# Example:
|
671
48
|
#
|
672
|
-
#
|
49
|
+
# Slop.string_to_option_class("string") #=> Slop::StringOption
|
50
|
+
# Slop.string_to_option_class("foo") #=> uninitialized constant FooOption
|
673
51
|
#
|
674
|
-
# Returns the
|
675
|
-
def
|
676
|
-
|
52
|
+
# Returns the full qualified option class. Uses `#string_to_option`.
|
53
|
+
def self.string_to_option_class(s)
|
54
|
+
const_get(string_to_option(s))
|
677
55
|
end
|
678
|
-
|
679
|
-
def commands_to_help
|
680
|
-
padding = 0
|
681
|
-
@commands.each { |c, _| padding = c.size if c.size > padding }
|
682
|
-
@commands.map do |cmd, opts|
|
683
|
-
" #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}"
|
684
|
-
end.join("\n")
|
685
|
-
end
|
686
|
-
|
687
56
|
end
|