slop 3.3.3 → 3.4.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.
- data/CHANGES.md +8 -0
- data/README.md +90 -43
- data/Rakefile +16 -0
- data/lib/slop.rb +155 -94
- data/lib/slop/commands.rb +38 -39
- data/lib/slop/option.rb +5 -6
- data/slop.gemspec +3 -1
- data/test/commands_test.rb +36 -0
- data/test/slop_test.rb +106 -2
- metadata +4 -9
data/CHANGES.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
3.4.0 (2013-01-12)
|
2
|
+
------------------
|
3
|
+
|
4
|
+
* Implement new command system (#95)
|
5
|
+
* Deprecate Slop::Commands
|
6
|
+
* Ensure 'no-foo' options are not inverted when parsing '--no-foo' (#86)
|
7
|
+
* Code refactoring and simplification (Kenichi Kamiya, #84, #85)
|
8
|
+
|
1
9
|
3.3.3 (2012-08-29)
|
2
10
|
------------------
|
3
11
|
|
data/README.md
CHANGED
@@ -2,61 +2,33 @@ Slop
|
|
2
2
|
====
|
3
3
|
|
4
4
|
Slop is a simple option parser with an easy to remember syntax and friendly API.
|
5
|
-
|
6
|
-
This README is targeted at Slop v3.
|
5
|
+
API Documentation is available [here](http://injekt.github.com/rdoc/slop/).
|
7
6
|
|
8
7
|
[](http://travis-ci.org/injekt/slop)
|
9
8
|
|
10
|
-
Installation
|
11
|
-
------------
|
12
|
-
|
13
|
-
### Rubygems
|
14
|
-
|
15
|
-
gem install slop
|
16
|
-
|
17
|
-
### GitHub
|
18
|
-
|
19
|
-
git clone git://github.com/injekt/slop.git
|
20
|
-
gem build slop.gemspec
|
21
|
-
gem install slop-<version>.gem
|
22
|
-
|
23
9
|
Usage
|
24
10
|
-----
|
25
11
|
|
26
12
|
```ruby
|
27
|
-
# parse assumes ARGV, otherwise you can pass it your own Array
|
28
13
|
opts = Slop.parse do
|
29
|
-
banner
|
30
|
-
|
31
|
-
on
|
32
|
-
on
|
14
|
+
banner 'Usage: foo.rb [options]'
|
15
|
+
|
16
|
+
on 'name=', 'Your name'
|
17
|
+
on 'p', 'password', 'An optional password', argument: :optional
|
18
|
+
on 'v', 'verbose', 'Enable verbose mode'
|
33
19
|
end
|
34
20
|
|
35
21
|
# if ARGV is `--name Lee -v`
|
36
22
|
opts.verbose? #=> true
|
37
23
|
opts.password? #=> false
|
38
24
|
opts[:name] #=> 'lee'
|
25
|
+
opts.to_hash #=> {:name=>"Lee", :password=>nil, :verbose=>true}
|
39
26
|
```
|
40
27
|
|
41
|
-
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
# These options all do the same thing
|
45
|
-
on '-n', '--name', 'Your name', :argument => true
|
46
|
-
on 'n', :name=, 'Your name'
|
47
|
-
on :n, '--name=', 'Your name'
|
48
|
-
|
49
|
-
# As do these
|
50
|
-
on 'p', '--password', 'Your password', :argument => :optional
|
51
|
-
on :p, :password, 'Your password', :optional_argument => true
|
52
|
-
on '-p', 'password=?', 'Your password'
|
53
|
-
```
|
54
|
-
|
55
|
-
You can also return your options as a Hash:
|
28
|
+
Installation
|
29
|
+
------------
|
56
30
|
|
57
|
-
|
58
|
-
opts.to_hash #=> { :name => 'lee', :verbose => true, :password => nil }
|
59
|
-
```
|
31
|
+
gem install slop
|
60
32
|
|
61
33
|
Printing Help
|
62
34
|
-------------
|
@@ -84,13 +56,88 @@ All of these options can be sent to `Slop.new` or `Slop.parse` in Hash form.
|
|
84
56
|
* `longest_flag` - The longest string flag, used to aid configuring help
|
85
57
|
text. **default:** *0*.
|
86
58
|
|
87
|
-
|
59
|
+
Lists
|
60
|
+
-----
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
opts = Slop.parse do
|
64
|
+
on :list=, as: Array
|
65
|
+
end
|
66
|
+
# ruby run.rb --list one,two
|
67
|
+
opts[:list] #=> ["one", "two"]
|
68
|
+
# ruby run.rb --list one,two --list three
|
69
|
+
opts[:list] #=> ["one", "two", "three"]
|
70
|
+
```
|
71
|
+
|
72
|
+
You can also specify a delimiter and limit.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
opts = Slop.parse do
|
76
|
+
on :list=, as: Array, delimiter: ':', limit: 2
|
77
|
+
end
|
78
|
+
# ruby run.rb --list one:two:three
|
79
|
+
opts[:list] #=> ["one", "two:three"]
|
80
|
+
```
|
81
|
+
|
82
|
+
Ranges
|
83
|
+
------
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
opts = Slop.parse do
|
87
|
+
on :range=, as: Range
|
88
|
+
end
|
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
|
+
# ruby run.rb --range 1-10
|
94
|
+
opts[:range] #=> 1..10
|
95
|
+
# ruby run.rb --range 1,10
|
96
|
+
opts[:range] #=> 1..10
|
97
|
+
```
|
98
|
+
|
99
|
+
Autocreate
|
100
|
+
----------
|
101
|
+
|
102
|
+
Slop has an 'autocreate' feature. This feature is intended to create
|
103
|
+
options on the fly, without having to specify them yourself. In some case,
|
104
|
+
uses this code could be all you need in your application:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
# ruby run.rb --foo bar --baz --name lee
|
108
|
+
opts = Slop.parse(autocreate: true)
|
109
|
+
opts.to_hash #=> {:foo=>"bar", :baz=>true, :name=>"lee"}
|
110
|
+
opts.fetch_option(:name).expects_argument? #=> true
|
111
|
+
```
|
112
|
+
|
113
|
+
Commands
|
88
114
|
--------
|
89
115
|
|
90
|
-
|
116
|
+
Slop supports git style sub-commands, like so:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
opts = Slop.parse do
|
120
|
+
on '-v', 'Print the version' do
|
121
|
+
puts "Version 1.0"
|
122
|
+
end
|
91
123
|
|
92
|
-
|
93
|
-
|
124
|
+
command 'add' do
|
125
|
+
on :v, :verbose, 'Enable verbose mode'
|
126
|
+
on :name, 'Your name'
|
127
|
+
|
128
|
+
run do |opts, args|
|
129
|
+
puts "You ran 'add' with options #{opts.to_hash} and args: #{args.inspect}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# ruby run.rb -v
|
135
|
+
#=> Version 1.0
|
136
|
+
# ruby run.rb add -v foo --name Lee
|
137
|
+
#=> You ran 'add' with options {:verbose=>true,:name=>"Lee"} and args ["foo"]
|
138
|
+
opts.to_hash(true) # Pass true to tell Slop to merge sub-command option values.
|
139
|
+
# => { :v => nil, :add => { :v => true, :name => "Lee" } }
|
140
|
+
```
|
94
141
|
|
95
142
|
Woah woah, why you hating on OptionParser?
|
96
143
|
------------------------------------------
|
@@ -132,4 +179,4 @@ opts = Slop.parse do
|
|
132
179
|
end
|
133
180
|
|
134
181
|
opts.to_hash #=> { :name => 'lee', :age => 105 }
|
135
|
-
```
|
182
|
+
```
|
data/Rakefile
CHANGED
@@ -10,4 +10,20 @@ Rake::TestTask.new do |t|
|
|
10
10
|
t.test_files = Dir['test/*_test.rb']
|
11
11
|
end
|
12
12
|
|
13
|
+
begin
|
14
|
+
require 'rdoc/task'
|
15
|
+
|
16
|
+
# requires sdoc and horo gems
|
17
|
+
RDoc::Task.new do |rdoc|
|
18
|
+
rdoc.title = 'Slop API Documentation'
|
19
|
+
rdoc.rdoc_dir = 'doc'
|
20
|
+
rdoc.options << '-f' << 'sdoc'
|
21
|
+
rdoc.options << '-T' << 'rails'
|
22
|
+
rdoc.options << '-e' << 'UTF-8'
|
23
|
+
rdoc.options << '-g'
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
25
|
+
end
|
26
|
+
rescue LoadError
|
27
|
+
end
|
28
|
+
|
13
29
|
task :default => :test
|
data/lib/slop.rb
CHANGED
@@ -4,7 +4,7 @@ require 'slop/commands'
|
|
4
4
|
class Slop
|
5
5
|
include Enumerable
|
6
6
|
|
7
|
-
VERSION = '3.
|
7
|
+
VERSION = '3.4.0'
|
8
8
|
|
9
9
|
# The main Error class, all Exception classes inherit from this class.
|
10
10
|
class Error < StandardError; end
|
@@ -51,7 +51,7 @@ class Slop
|
|
51
51
|
#
|
52
52
|
# Returns a new instance of Slop.
|
53
53
|
def parse(items = ARGV, config = {}, &block)
|
54
|
-
|
54
|
+
parse! items.dup, config, &block
|
55
55
|
end
|
56
56
|
|
57
57
|
# items - The Array of items to extract options from (default: ARGV).
|
@@ -60,7 +60,10 @@ class Slop
|
|
60
60
|
#
|
61
61
|
# Returns a new instance of Slop.
|
62
62
|
def parse!(items = ARGV, config = {}, &block)
|
63
|
-
|
63
|
+
config, items = items, ARGV if items.is_a?(Hash) && config.empty?
|
64
|
+
slop = Slop.new config, &block
|
65
|
+
slop.parse! items
|
66
|
+
slop
|
64
67
|
end
|
65
68
|
|
66
69
|
# Build a Slop object from a option specification.
|
@@ -106,22 +109,6 @@ class Slop
|
|
106
109
|
opts
|
107
110
|
end
|
108
111
|
|
109
|
-
private
|
110
|
-
|
111
|
-
# Convenience method used by ::parse and ::parse!.
|
112
|
-
#
|
113
|
-
# items - The Array of items to parse.
|
114
|
-
# delete - When true, executes #parse! over #parse.
|
115
|
-
# config - The Hash of configuration options to pass to Slop.new.
|
116
|
-
# block - The optional block to pass to Slop.new
|
117
|
-
#
|
118
|
-
# Returns a newly created instance of Slop.
|
119
|
-
def init_and_parse(items, delete, config, &block)
|
120
|
-
config, items = items, ARGV if items.is_a?(Hash) && config.empty?
|
121
|
-
slop = Slop.new(config, &block)
|
122
|
-
delete ? slop.parse!(items) : slop.parse(items)
|
123
|
-
slop
|
124
|
-
end
|
125
112
|
end
|
126
113
|
|
127
114
|
# The Hash of configuration options for this Slop instance.
|
@@ -137,11 +124,13 @@ class Slop
|
|
137
124
|
def initialize(config = {}, &block)
|
138
125
|
@config = DEFAULT_OPTIONS.merge(config)
|
139
126
|
@options = []
|
127
|
+
@commands = {}
|
140
128
|
@trash = []
|
141
129
|
@triggered_options = []
|
142
130
|
@unknown_options = []
|
143
131
|
@callbacks = {}
|
144
132
|
@separators = {}
|
133
|
+
@runner = nil
|
145
134
|
|
146
135
|
if block_given?
|
147
136
|
block.arity == 1 ? yield(self) : instance_eval(&block)
|
@@ -164,8 +153,6 @@ class Slop
|
|
164
153
|
# Set the banner.
|
165
154
|
#
|
166
155
|
# banner - The String to set the banner.
|
167
|
-
#
|
168
|
-
# Returns nothing.
|
169
156
|
def banner=(banner)
|
170
157
|
config[:banner] = banner
|
171
158
|
end
|
@@ -180,6 +167,33 @@ class Slop
|
|
180
167
|
config[:banner]
|
181
168
|
end
|
182
169
|
|
170
|
+
# Set the description (used for commands).
|
171
|
+
#
|
172
|
+
# desc - The String to set the description.
|
173
|
+
def description=(desc)
|
174
|
+
config[:description] = desc
|
175
|
+
end
|
176
|
+
|
177
|
+
# Get or set the description (used for commands).
|
178
|
+
#
|
179
|
+
# desc - The String to set the description.
|
180
|
+
#
|
181
|
+
# Returns the description String.
|
182
|
+
def description(desc = nil)
|
183
|
+
config[:description] = desc if desc
|
184
|
+
config[:description]
|
185
|
+
end
|
186
|
+
|
187
|
+
# Add a new command.
|
188
|
+
#
|
189
|
+
# command - The Symbol or String used to identify this command.
|
190
|
+
# options - A Hash of configuration options (see Slop::new)
|
191
|
+
#
|
192
|
+
# Returns a new instance of Slop mapped to this command.
|
193
|
+
def command(command, options = {}, &block)
|
194
|
+
@commands[command.to_s] = Slop.new(options, &block)
|
195
|
+
end
|
196
|
+
|
183
197
|
# Parse a list of items, executing and gathering options along the way.
|
184
198
|
#
|
185
199
|
# items - The Array of items to extract options from (default: ARGV).
|
@@ -187,7 +201,8 @@ class Slop
|
|
187
201
|
#
|
188
202
|
# Returns an Array of original items.
|
189
203
|
def parse(items = ARGV, &block)
|
190
|
-
|
204
|
+
parse! items.dup, &block
|
205
|
+
items
|
191
206
|
end
|
192
207
|
|
193
208
|
# Parse a list of items, executing and gathering options along the way.
|
@@ -199,7 +214,39 @@ class Slop
|
|
199
214
|
#
|
200
215
|
# Returns an Array of original items with options removed.
|
201
216
|
def parse!(items = ARGV, &block)
|
202
|
-
|
217
|
+
if items.empty? && @callbacks[:empty]
|
218
|
+
@callbacks[:empty].each { |cb| cb.call(self) }
|
219
|
+
return items
|
220
|
+
end
|
221
|
+
|
222
|
+
if cmd = @commands[items[0]]
|
223
|
+
return cmd.parse! items[1..-1]
|
224
|
+
end
|
225
|
+
|
226
|
+
items.each_with_index do |item, index|
|
227
|
+
@trash << index && break if item == '--'
|
228
|
+
autocreate(items, index) if config[:autocreate]
|
229
|
+
process_item(items, index, &block) unless @trash.include?(index)
|
230
|
+
end
|
231
|
+
items.reject!.with_index { |item, index| @trash.include?(index) }
|
232
|
+
|
233
|
+
missing_options = options.select { |opt| opt.required? && opt.count < 1 }
|
234
|
+
if missing_options.any?
|
235
|
+
raise MissingOptionError,
|
236
|
+
"Missing required option(s): #{missing_options.map(&:key).join(', ')}"
|
237
|
+
end
|
238
|
+
|
239
|
+
if @unknown_options.any?
|
240
|
+
raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
|
241
|
+
end
|
242
|
+
|
243
|
+
if @triggered_options.empty? && @callbacks[:no_options]
|
244
|
+
@callbacks[:no_options].each { |cb| cb.call(self) }
|
245
|
+
end
|
246
|
+
|
247
|
+
@runner.call(self, items) if @runner.respond_to?(:call)
|
248
|
+
|
249
|
+
items
|
203
250
|
end
|
204
251
|
|
205
252
|
# Add an Option.
|
@@ -232,8 +279,14 @@ class Slop
|
|
232
279
|
alias get []
|
233
280
|
|
234
281
|
# Returns a new Hash with option flags as keys and option values as values.
|
235
|
-
|
236
|
-
|
282
|
+
#
|
283
|
+
# include_commands - If true, merge options from all sub-commands.
|
284
|
+
def to_hash(include_commands = false)
|
285
|
+
hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
|
286
|
+
if include_commands
|
287
|
+
@commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) }
|
288
|
+
end
|
289
|
+
hash
|
237
290
|
end
|
238
291
|
alias to_h to_hash
|
239
292
|
|
@@ -242,6 +295,29 @@ class Slop
|
|
242
295
|
options.each(&block)
|
243
296
|
end
|
244
297
|
|
298
|
+
# Specify code to be executed when these options are parsed.
|
299
|
+
#
|
300
|
+
# callable - An object responding to a call method.
|
301
|
+
#
|
302
|
+
# yields - The instance of Slop parsing these options
|
303
|
+
# An Array of unparsed arguments
|
304
|
+
#
|
305
|
+
# Example:
|
306
|
+
#
|
307
|
+
# Slop.parse do
|
308
|
+
# on :v, :verbose
|
309
|
+
#
|
310
|
+
# run do |opts, args|
|
311
|
+
# puts "Arguments: #{args.inspect}" if opts.verbose?
|
312
|
+
# end
|
313
|
+
# end
|
314
|
+
def run(callable = nil, &block)
|
315
|
+
@runner = callable || block
|
316
|
+
unless @runner.respond_to?(:call)
|
317
|
+
raise ArgumentError, "You must specify a callable object or a block to #run"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
245
321
|
# Check for an options presence.
|
246
322
|
#
|
247
323
|
# Examples:
|
@@ -255,35 +331,11 @@ class Slop
|
|
255
331
|
keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
|
256
332
|
end
|
257
333
|
|
258
|
-
# Convenience method for present?(:option).
|
259
|
-
#
|
260
|
-
# Examples:
|
261
|
-
#
|
262
|
-
# opts.parse %( --verbose )
|
263
|
-
# opts.verbose? #=> true
|
264
|
-
# opts.other? #=> false
|
265
|
-
#
|
266
|
-
# Returns true if this option is present. If this method does not end
|
267
|
-
# with a ? character it will instead call super().
|
268
|
-
def method_missing(method, *args, &block)
|
269
|
-
meth = method.to_s
|
270
|
-
if meth.end_with?('?')
|
271
|
-
present?(meth.chop)
|
272
|
-
else
|
273
|
-
super
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
334
|
# Override this method so we can check if an option? method exists.
|
278
335
|
#
|
279
336
|
# Returns true if this option key exists in our list of options.
|
280
|
-
def
|
281
|
-
|
282
|
-
if method.end_with?('?') && options.any? { |o| o.key == method.chop }
|
283
|
-
true
|
284
|
-
else
|
285
|
-
super
|
286
|
-
end
|
337
|
+
def respond_to_missing?(method_name, include_private = false)
|
338
|
+
options.any? { |o| o.key == method_name.to_s.chop } || super
|
287
339
|
end
|
288
340
|
|
289
341
|
# Fetch a list of options which were missing from the parsed list.
|
@@ -319,6 +371,22 @@ class Slop
|
|
319
371
|
options.find { |option| [option.long, option.short].include?(clean(key)) }
|
320
372
|
end
|
321
373
|
|
374
|
+
# Fetch a Slop object associated with this command.
|
375
|
+
#
|
376
|
+
# command - The String or Symbol name of the command.
|
377
|
+
#
|
378
|
+
# Examples:
|
379
|
+
#
|
380
|
+
# opts.command :foo do
|
381
|
+
# on :v, :verbose, 'Enable verbose mode'
|
382
|
+
# end
|
383
|
+
#
|
384
|
+
# # ruby run.rb foo -v
|
385
|
+
# opts.fetch_command(:foo).verbose? #=> true
|
386
|
+
def fetch_command(command)
|
387
|
+
@commands[command.to_s]
|
388
|
+
end
|
389
|
+
|
322
390
|
# Add a callback.
|
323
391
|
#
|
324
392
|
# label - The Symbol identifier to attach this callback.
|
@@ -350,59 +418,43 @@ class Slop
|
|
350
418
|
(str = @separators[i + 1]) ? [o, str].join("\n") : o
|
351
419
|
}.join("\n")
|
352
420
|
|
353
|
-
if
|
354
|
-
|
355
|
-
|
356
|
-
|
421
|
+
if @commands.any?
|
422
|
+
optstr << "\n" if !optstr.empty?
|
423
|
+
optstr << "\nAvailable commands:\n\n"
|
424
|
+
optstr << commands_to_help
|
425
|
+
optstr << "\n\nSee `<command> --help` for more information on a specific command."
|
426
|
+
end
|
427
|
+
|
428
|
+
banner = config[:banner]
|
429
|
+
banner = "Usage: #{File.basename($0, '.*')}#{' [command]' if @commands.any?} [options]" if banner.nil?
|
430
|
+
if banner
|
431
|
+
"#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}"
|
357
432
|
else
|
358
433
|
optstr
|
359
434
|
end
|
360
435
|
end
|
361
436
|
alias help to_s
|
362
437
|
|
363
|
-
# Returns the String inspection text.
|
364
|
-
def inspect
|
365
|
-
"#<Slop #{config.inspect} #{options.map(&:inspect)}>"
|
366
|
-
end
|
367
|
-
|
368
438
|
private
|
369
439
|
|
370
|
-
#
|
440
|
+
# Convenience method for present?(:option).
|
371
441
|
#
|
372
|
-
#
|
373
|
-
# delete - True to remove any triggered options and arguments from the
|
374
|
-
# original list of items.
|
375
|
-
# block - An optional block which when passed will yields non-options.
|
442
|
+
# Examples:
|
376
443
|
#
|
377
|
-
#
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
missing_options = options.select { |opt| opt.required? && opt.count < 1 }
|
392
|
-
if missing_options.any?
|
393
|
-
raise MissingOptionError,
|
394
|
-
"Missing required option(s): #{missing_options.map(&:key).join(', ')}"
|
395
|
-
end
|
396
|
-
|
397
|
-
if @unknown_options.any?
|
398
|
-
raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
|
399
|
-
end
|
400
|
-
|
401
|
-
if @triggered_options.empty? && @callbacks[:no_options]
|
402
|
-
@callbacks[:no_options].each { |cb| cb.call(self) }
|
444
|
+
# opts.parse %( --verbose )
|
445
|
+
# opts.verbose? #=> true
|
446
|
+
# opts.other? #=> false
|
447
|
+
#
|
448
|
+
# Returns true if this option is present. If this method does not end
|
449
|
+
# with a ? character it will instead call super().
|
450
|
+
def method_missing(method, *args, &block)
|
451
|
+
meth = method.to_s
|
452
|
+
if meth.end_with?('?')
|
453
|
+
meth.chop!
|
454
|
+
present?(meth) || present?(meth.gsub('_', '-'))
|
455
|
+
else
|
456
|
+
super
|
403
457
|
end
|
404
|
-
|
405
|
-
items
|
406
458
|
end
|
407
459
|
|
408
460
|
# Process a list item, figure out if it's an option, execute any
|
@@ -419,6 +471,7 @@ class Slop
|
|
419
471
|
|
420
472
|
if option
|
421
473
|
option.count += 1 unless item.start_with?('--no-')
|
474
|
+
option.count += 1 if option.key[0, 3] == "no-"
|
422
475
|
@trash << index
|
423
476
|
@triggered_options << option
|
424
477
|
|
@@ -548,7 +601,7 @@ class Slop
|
|
548
601
|
config[:optional_argument] = true if @config[:optional_arguments]
|
549
602
|
|
550
603
|
if objects.last.is_a?(Hash)
|
551
|
-
config
|
604
|
+
config.merge!(objects.last)
|
552
605
|
objects.pop
|
553
606
|
end
|
554
607
|
short = extract_short_flag(objects, config)
|
@@ -599,4 +652,12 @@ class Slop
|
|
599
652
|
object.to_s.sub(/\A--?/, '')
|
600
653
|
end
|
601
654
|
|
655
|
+
def commands_to_help
|
656
|
+
padding = 0
|
657
|
+
@commands.each { |c, _| padding = c.size if c.size > padding }
|
658
|
+
@commands.map do |cmd, opts|
|
659
|
+
" #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}"
|
660
|
+
end.join("\n")
|
661
|
+
end
|
662
|
+
|
602
663
|
end
|
data/lib/slop/commands.rb
CHANGED
@@ -39,6 +39,10 @@ class Slop
|
|
39
39
|
@banner = nil
|
40
40
|
@triggered_command = nil
|
41
41
|
|
42
|
+
warn "[DEPRECATED] Slop::Commands is deprecated and will be removed in "\
|
43
|
+
"Slop version 4. Check out http://injekt.github.com/slop/#commands for "\
|
44
|
+
"a new implementation of commands."
|
45
|
+
|
42
46
|
if block_given?
|
43
47
|
block.arity == 1 ? yield(self) : instance_eval(&block)
|
44
48
|
end
|
@@ -108,18 +112,19 @@ class Slop
|
|
108
112
|
key.to_s == @triggered_command
|
109
113
|
end
|
110
114
|
|
115
|
+
# Enumerable interface.
|
116
|
+
def each(&block)
|
117
|
+
@commands.each(&block)
|
118
|
+
end
|
119
|
+
|
111
120
|
# Parse a list of items.
|
112
121
|
#
|
113
122
|
# items - The Array of items to parse.
|
114
123
|
#
|
115
124
|
# Returns the original Array of items.
|
116
125
|
def parse(items = ARGV)
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
# Enumerable interface.
|
121
|
-
def each(&block)
|
122
|
-
@commands.each(&block)
|
126
|
+
parse! items.dup
|
127
|
+
items
|
123
128
|
end
|
124
129
|
|
125
130
|
# Parse a list of items, removing any options or option arguments found.
|
@@ -128,7 +133,22 @@ class Slop
|
|
128
133
|
#
|
129
134
|
# Returns the original Array of items with options removed.
|
130
135
|
def parse!(items = ARGV)
|
131
|
-
|
136
|
+
if opts = commands[items[0].to_s]
|
137
|
+
@triggered_command = items.shift
|
138
|
+
execute_arguments! items
|
139
|
+
opts.parse! items
|
140
|
+
execute_global_opts! items
|
141
|
+
else
|
142
|
+
if opts = commands['default']
|
143
|
+
opts.parse! items
|
144
|
+
else
|
145
|
+
if config[:strict] && items[0]
|
146
|
+
raise InvalidCommandError, "Unknown command `#{items[0]}`"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
execute_global_opts! items
|
150
|
+
end
|
151
|
+
items
|
132
152
|
end
|
133
153
|
|
134
154
|
# Returns a nested Hash with Slop options and values. See Slop#to_hash.
|
@@ -141,8 +161,12 @@ class Slop
|
|
141
161
|
defaults = commands.delete('default')
|
142
162
|
globals = commands.delete('global')
|
143
163
|
helps = commands.reject { |_, v| v.options.none? }
|
144
|
-
|
145
|
-
|
164
|
+
if globals && globals.options.any?
|
165
|
+
helps.merge!('Global options' => globals.to_s)
|
166
|
+
end
|
167
|
+
if defaults && defaults.options.any?
|
168
|
+
helps.merge!('Other options' => defaults.to_s)
|
169
|
+
end
|
146
170
|
banner = @banner ? "#{@banner}\n" : ""
|
147
171
|
banner + helps.map { |key, opts| " #{key}\n#{opts}" }.join("\n\n")
|
148
172
|
end
|
@@ -155,43 +179,18 @@ class Slop
|
|
155
179
|
|
156
180
|
private
|
157
181
|
|
158
|
-
# Parse a list of items.
|
159
|
-
#
|
160
|
-
# items - The Array of items to parse.
|
161
|
-
# bang - When true, #parse! will be called instead of #parse.
|
162
|
-
#
|
163
|
-
# Returns the Array of items (with options removed if bang == true).
|
164
|
-
def parse_items(items, bang = false)
|
165
|
-
if opts = commands[items[0].to_s]
|
166
|
-
@triggered_command = items.shift
|
167
|
-
execute_arguments(items, bang)
|
168
|
-
bang ? opts.parse!(items) : opts.parse(items)
|
169
|
-
execute_global_opts(items, bang)
|
170
|
-
else
|
171
|
-
if opts = commands['default']
|
172
|
-
bang ? opts.parse!(items) : opts.parse(items)
|
173
|
-
else
|
174
|
-
if config[:strict] && items[0]
|
175
|
-
raise InvalidCommandError, "Unknown command `#{items[0]}`"
|
176
|
-
end
|
177
|
-
end
|
178
|
-
execute_global_opts(items, bang)
|
179
|
-
end
|
180
|
-
items
|
181
|
-
end
|
182
|
-
|
183
182
|
# Returns nothing.
|
184
|
-
def execute_arguments(items
|
183
|
+
def execute_arguments!(items)
|
185
184
|
@arguments = items.take_while { |arg| !arg.start_with?('-') }
|
186
|
-
items.shift
|
185
|
+
items.shift @arguments.size
|
187
186
|
end
|
188
187
|
|
189
188
|
# Returns nothing.
|
190
|
-
def execute_global_opts(items
|
189
|
+
def execute_global_opts!(items)
|
191
190
|
if global_opts = commands['global']
|
192
|
-
|
191
|
+
global_opts.parse! items
|
193
192
|
end
|
194
193
|
end
|
195
194
|
|
196
195
|
end
|
197
|
-
end
|
196
|
+
end
|
data/lib/slop/option.rb
CHANGED
@@ -56,7 +56,7 @@ class Slop
|
|
56
56
|
@config.each_key do |key|
|
57
57
|
predicate = :"#{key}?"
|
58
58
|
unless self.class.method_defined? predicate
|
59
|
-
self.class.
|
59
|
+
self.class.__send__(:define_method, predicate) { !!@config[key] }
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -126,16 +126,15 @@ class Slop
|
|
126
126
|
def to_s
|
127
127
|
return config[:help] if config[:help].respond_to?(:to_str)
|
128
128
|
|
129
|
-
out = " "
|
130
|
-
out += short ? "-#{short}, " : ' ' * 4
|
129
|
+
out = " #{short ? "-#{short}, " : ' ' * 4}"
|
131
130
|
|
132
131
|
if long
|
133
|
-
out
|
132
|
+
out << "--#{long}"
|
134
133
|
size = long.size
|
135
134
|
diff = @slop.config[:longest_flag] - size
|
136
|
-
out
|
135
|
+
out << (' ' * (diff + 6))
|
137
136
|
else
|
138
|
-
out
|
137
|
+
out << (' ' * (@slop.config[:longest_flag] + 8))
|
139
138
|
end
|
140
139
|
|
141
140
|
"#{out}#{description}"
|
data/slop.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'slop'
|
3
|
-
s.version = '3.
|
3
|
+
s.version = '3.4.0'
|
4
4
|
s.summary = 'Option gathering made easy'
|
5
5
|
s.description = 'A simple DSL for gathering options and parsing the command line'
|
6
6
|
s.author = 'Lee Jarvis'
|
@@ -9,6 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.files = `git ls-files`.split("\n")
|
10
10
|
s.test_files = `git ls-files -- test/*`.split("\n")
|
11
11
|
|
12
|
+
s.required_ruby_version = '>= 1.8.7'
|
13
|
+
|
12
14
|
s.add_development_dependency 'rake'
|
13
15
|
s.add_development_dependency 'minitest'
|
14
16
|
end
|
data/test/commands_test.rb
CHANGED
@@ -13,6 +13,16 @@ class CommandsTest < TestCase
|
|
13
13
|
add_callback(:empty) { 'version 1' }
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
@empty_commands = Slop::Commands.new do
|
18
|
+
default do
|
19
|
+
end
|
20
|
+
|
21
|
+
global do
|
22
|
+
end
|
23
|
+
|
24
|
+
on 'verbose'
|
25
|
+
end
|
16
26
|
end
|
17
27
|
|
18
28
|
test "it nests instances of Slop" do
|
@@ -75,6 +85,10 @@ class CommandsTest < TestCase
|
|
75
85
|
assert_kind_of Slop, @commands.global
|
76
86
|
end
|
77
87
|
|
88
|
+
test "empty default/global blocks don't add their titles in the help output" do
|
89
|
+
assert_empty @empty_commands.to_s
|
90
|
+
end
|
91
|
+
|
78
92
|
test "parse does nothing when there's nothing to parse" do
|
79
93
|
assert @commands.parse []
|
80
94
|
end
|
@@ -99,4 +113,26 @@ class CommandsTest < TestCase
|
|
99
113
|
assert_equal %w( file1 file2 ), @commands.arguments
|
100
114
|
end
|
101
115
|
|
116
|
+
test "context and return value of constructor block" do
|
117
|
+
peep = nil
|
118
|
+
ret = Slop::Commands.new { peep = self }
|
119
|
+
assert_same ret, peep
|
120
|
+
assert !equal?(peep)
|
121
|
+
|
122
|
+
peep = nil
|
123
|
+
ret = Slop::Commands.new { |a| peep = self }
|
124
|
+
assert !peep.equal?(ret)
|
125
|
+
assert_same peep, self
|
126
|
+
|
127
|
+
peep = nil
|
128
|
+
ret = Slop::Commands.new { |a, b| peep = self }
|
129
|
+
assert_same ret, peep
|
130
|
+
assert !equal?(peep)
|
131
|
+
|
132
|
+
peep = nil
|
133
|
+
ret = Slop::Commands.new { |a, *rest| peep = self }
|
134
|
+
assert_same ret, peep
|
135
|
+
assert !equal?(peep)
|
136
|
+
end
|
137
|
+
|
102
138
|
end
|
data/test/slop_test.rb
CHANGED
@@ -314,7 +314,7 @@ class SlopTest < TestCase
|
|
314
314
|
end
|
315
315
|
|
316
316
|
test "separators" do
|
317
|
-
opts = Slop.new do
|
317
|
+
opts = Slop.new(:banner => false) do
|
318
318
|
on :foo
|
319
319
|
separator "hello"
|
320
320
|
separator "world"
|
@@ -331,10 +331,24 @@ class SlopTest < TestCase
|
|
331
331
|
|
332
332
|
test "printing help with :help => true" do
|
333
333
|
temp_stderr do |string|
|
334
|
-
opts = Slop.new(:help => true)
|
334
|
+
opts = Slop.new(:help => true, :banner => false)
|
335
335
|
opts.parse %w( --help )
|
336
336
|
assert_equal " -h, --help Display this help message.\n", string
|
337
337
|
end
|
338
|
+
|
339
|
+
temp_stderr do |string|
|
340
|
+
opts = Slop.new(:help => true)
|
341
|
+
opts.parse %w( --help )
|
342
|
+
assert_equal "Usage: rake_test_loader [options]\n -h, --help Display this help message.\n", string
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
test "fallback to substituting - for _ when using <option>?" do
|
347
|
+
opts = Slop.new do
|
348
|
+
on 'foo-bar'
|
349
|
+
end
|
350
|
+
opts.parse %w( --foo-bar )
|
351
|
+
assert opts.foo_bar?
|
338
352
|
end
|
339
353
|
|
340
354
|
test "option=value syntax does NOT consume following argument" do
|
@@ -344,4 +358,94 @@ class SlopTest < TestCase
|
|
344
358
|
assert_equal %w' baz hello ', args
|
345
359
|
end
|
346
360
|
|
361
|
+
test "context and return value of constructor block" do
|
362
|
+
peep = nil
|
363
|
+
ret = Slop.new { peep = self }
|
364
|
+
assert_same ret, peep
|
365
|
+
assert !equal?(peep)
|
366
|
+
|
367
|
+
peep = nil
|
368
|
+
ret = Slop.new { |a| peep = self }
|
369
|
+
assert !peep.equal?(ret)
|
370
|
+
assert_same peep, self
|
371
|
+
|
372
|
+
peep = nil
|
373
|
+
ret = Slop.new { |a, b| peep = self }
|
374
|
+
assert_same ret, peep
|
375
|
+
assert !equal?(peep)
|
376
|
+
|
377
|
+
peep = nil
|
378
|
+
ret = Slop.new { |a, *rest| peep = self }
|
379
|
+
assert_same ret, peep
|
380
|
+
assert !equal?(peep)
|
381
|
+
|
382
|
+
peep = nil
|
383
|
+
ret = Slop.parse([]) { peep = self }
|
384
|
+
assert_same ret, peep
|
385
|
+
assert !equal?(peep)
|
386
|
+
|
387
|
+
peep = nil
|
388
|
+
ret = Slop.parse([]) { |a| peep = self }
|
389
|
+
assert !peep.equal?(ret)
|
390
|
+
assert_same peep, self
|
391
|
+
|
392
|
+
peep = nil
|
393
|
+
ret = Slop.parse([]) { |a, b| peep = self }
|
394
|
+
assert_same ret, peep
|
395
|
+
assert !equal?(peep)
|
396
|
+
|
397
|
+
peep = nil
|
398
|
+
ret = Slop.parse([]) { |a, *rest| peep = self }
|
399
|
+
assert_same ret, peep
|
400
|
+
assert !equal?(peep)
|
401
|
+
end
|
402
|
+
|
403
|
+
test "to_s do not break self" do
|
404
|
+
slop = Slop.new do
|
405
|
+
banner "foo"
|
406
|
+
end
|
407
|
+
|
408
|
+
assert_equal "foo", slop.banner
|
409
|
+
slop.to_s
|
410
|
+
assert_equal "foo", slop.banner
|
411
|
+
end
|
412
|
+
|
413
|
+
test "options with prefixed --no should not default to inverted behaviour unless intended" do
|
414
|
+
opts = Slop.new { on :bar }
|
415
|
+
opts.parse %w'--no-bar'
|
416
|
+
assert_equal false, opts[:bar]
|
417
|
+
|
418
|
+
opts = Slop.new { on 'no-bar' }
|
419
|
+
opts.parse %w'--no-bar'
|
420
|
+
assert_equal true, opts['no-bar']
|
421
|
+
end
|
422
|
+
|
423
|
+
test "method missing() is a private method" do
|
424
|
+
assert Slop.new.private_methods.map(&:to_sym).include?(:method_missing)
|
425
|
+
end
|
426
|
+
|
427
|
+
test "respond_to?() arity checker is similar of other objects" do
|
428
|
+
slop = Slop.new
|
429
|
+
obj = Object.new
|
430
|
+
|
431
|
+
assert_same obj.respond_to?(:__id__), slop.respond_to?(:__id__)
|
432
|
+
assert_same obj.respond_to?(:__id__, false), slop.respond_to?(:__id__, false)
|
433
|
+
assert_same obj.respond_to?(:__id__, true), slop.respond_to?(:__id__, true)
|
434
|
+
|
435
|
+
assert_raises ArgumentError do
|
436
|
+
slop.respond_to? :__id__, false, :INVALID_ARGUMENT
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
test "adding a runner" do
|
441
|
+
orun = proc { |r| assert_instance_of Slop, r }
|
442
|
+
arun = proc { |r| assert_equal ['foo', 'bar'], r }
|
443
|
+
|
444
|
+
Slop.parse(%w'foo --foo bar -v bar') do
|
445
|
+
on :v
|
446
|
+
on :foo=
|
447
|
+
run { |o, a| orun[o]; arun[a] }
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
347
451
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -75,19 +75,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
75
|
requirements:
|
76
76
|
- - ! '>='
|
77
77
|
- !ruby/object:Gem::Version
|
78
|
-
version:
|
79
|
-
segments:
|
80
|
-
- 0
|
81
|
-
hash: 3713237527878732625
|
78
|
+
version: 1.8.7
|
82
79
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
80
|
none: false
|
84
81
|
requirements:
|
85
82
|
- - ! '>='
|
86
83
|
- !ruby/object:Gem::Version
|
87
84
|
version: '0'
|
88
|
-
segments:
|
89
|
-
- 0
|
90
|
-
hash: 3713237527878732625
|
91
85
|
requirements: []
|
92
86
|
rubyforge_project:
|
93
87
|
rubygems_version: 1.8.23
|
@@ -99,3 +93,4 @@ test_files:
|
|
99
93
|
- test/helper.rb
|
100
94
|
- test/option_test.rb
|
101
95
|
- test/slop_test.rb
|
96
|
+
has_rdoc:
|