slop 1.5.5 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.md +41 -0
- data/lib/slop.rb +70 -33
- data/lib/slop/option.rb +30 -11
- data/lib/slop/options.rb +6 -0
- data/test/option_test.rb +26 -3
- data/test/slop_test.rb +24 -0
- metadata +3 -2
data/CHANGES.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
1.6.0 (2011-05-18)
|
2
|
+
------------------
|
3
|
+
|
4
|
+
* Add `:ignore_case` to Slop options for case insensitive option matching
|
5
|
+
* Add `:on_noopts` for triggering an event when the arguments contain no
|
6
|
+
options
|
7
|
+
* Add `:unless` to Slop::Option for omitting execution of the Options block
|
8
|
+
when this object exists in the Array of items passed to Slop.new
|
9
|
+
* Bugfix: Do not parse negative integers as options. A valid option must
|
10
|
+
start with an alphabet character
|
11
|
+
* Bugfix: Allow a Range to accept a negative Integer at either end
|
12
|
+
|
13
|
+
1.5.5 (2011-05-03)
|
14
|
+
------------------
|
15
|
+
|
16
|
+
* Bugfix: only attempt to extract options prefixed with `-`
|
17
|
+
|
18
|
+
1.5.4 (2011-05-01)
|
19
|
+
------------------
|
20
|
+
|
21
|
+
* Bugfix: `parse!` should not remove items with the same value as items used
|
22
|
+
in option arguments. Fixes #22 (Utkarsh Kukreti)
|
23
|
+
|
24
|
+
1.5.3 (2011-04-22)
|
25
|
+
------------------
|
26
|
+
|
27
|
+
* Bugfix: Use integers when fetching array indexes, not strings
|
28
|
+
|
29
|
+
1.5.2 (2011-04-17)
|
30
|
+
------------------
|
31
|
+
|
32
|
+
* Bugfix: Ensure `ARGV` is empty when using the `on_empty` event
|
33
|
+
|
34
|
+
1.5.0 (2011-04-15)
|
35
|
+
------------------
|
36
|
+
|
37
|
+
* Add `Slop#get` as alias to `Slop#[]`
|
38
|
+
* Add `Slop#present?` as alias for `Slop#<option>?`
|
39
|
+
* Add `Option#count` for monitoring how many times an option is called
|
40
|
+
* Add `:io` for using a custom IO object when using the `:help` option
|
41
|
+
* Numerous performance tweaks
|
data/lib/slop.rb
CHANGED
@@ -16,7 +16,7 @@ class Slop
|
|
16
16
|
class InvalidOptionError < RuntimeError; end
|
17
17
|
|
18
18
|
# @return [String] The current version string
|
19
|
-
VERSION = '1.
|
19
|
+
VERSION = '1.6.0'
|
20
20
|
|
21
21
|
# Parses the items from a CLI format into a friendly object.
|
22
22
|
#
|
@@ -69,6 +69,9 @@ class Slop
|
|
69
69
|
# :help => true is used
|
70
70
|
# @option opts [Boolean] :exit_on_help (true) When false and coupled with
|
71
71
|
# the :help option, Slop will not exit inside of the `help` option
|
72
|
+
# @option opts [Boolean] :ignore_case (false) Ignore options case
|
73
|
+
# @option opts [Proc, #call] :on_noopts Trigger an event when no options
|
74
|
+
# are found
|
72
75
|
def initialize(*opts, &block)
|
73
76
|
sloptions = {}
|
74
77
|
sloptions.merge! opts.pop if opts.last.is_a? Hash
|
@@ -83,8 +86,10 @@ class Slop
|
|
83
86
|
|
84
87
|
@banner = sloptions[:banner]
|
85
88
|
@strict = sloptions[:strict]
|
89
|
+
@ignore_case = sloptions[:ignore_case]
|
86
90
|
@multiple_switches = sloptions[:multiple_switches]
|
87
91
|
@on_empty = sloptions[:on_empty]
|
92
|
+
@on_noopts = sloptions[:on_noopts] || sloptions[:on_optionless]
|
88
93
|
@sloptions = sloptions
|
89
94
|
|
90
95
|
io = sloptions[:io] || $stderr
|
@@ -117,14 +122,14 @@ class Slop
|
|
117
122
|
|
118
123
|
# Parse a list of options, leaving the original Array unchanged.
|
119
124
|
#
|
120
|
-
# @param items
|
125
|
+
# @param [Array] items A list of items to parse
|
121
126
|
def parse(items=ARGV, &block)
|
122
127
|
parse_items items, &block
|
123
128
|
end
|
124
129
|
|
125
130
|
# Parse a list of options, removing parsed options from the original Array.
|
126
131
|
#
|
127
|
-
# @param items
|
132
|
+
# @param [Array] items A list of items to parse
|
128
133
|
def parse!(items=ARGV, &block)
|
129
134
|
parse_items items, true, &block
|
130
135
|
end
|
@@ -152,7 +157,8 @@ class Slop
|
|
152
157
|
# @option args [Symbol, String] :short_flag Short option name.
|
153
158
|
# @option args [Symbol, String] :long_flag Full option name.
|
154
159
|
# @option args [String] :description Option description for use in Slop#help
|
155
|
-
# @option args [Boolean] :argument Specifies whether
|
160
|
+
# @option args [Boolean] :argument Specifies whether this option requires
|
161
|
+
# an argument
|
156
162
|
# @option args [Hash] :options Optional option configurations.
|
157
163
|
# @example
|
158
164
|
# opts = Slop.parse do
|
@@ -210,10 +216,10 @@ class Slop
|
|
210
216
|
slop
|
211
217
|
end
|
212
218
|
|
213
|
-
#
|
219
|
+
# Trigger an event when Slop has no values to parse
|
214
220
|
#
|
215
221
|
# @param [Object, nil] proc The object (which can be anything
|
216
|
-
# responding to
|
222
|
+
# responding to `call`)
|
217
223
|
# @example
|
218
224
|
# Slop.parse do
|
219
225
|
# on_empty { puts 'No argument given!' }
|
@@ -224,6 +230,20 @@ class Slop
|
|
224
230
|
end
|
225
231
|
alias :on_empty= :on_empty
|
226
232
|
|
233
|
+
# Trigger an event when the arguments contain no options
|
234
|
+
#
|
235
|
+
# @param [Object, nil] obj The object to be triggered (anything
|
236
|
+
# responding to `call`)
|
237
|
+
# @example
|
238
|
+
# Slop.parse do
|
239
|
+
# on_noopts { puts 'No options here!' }
|
240
|
+
# end
|
241
|
+
# @since 1.6.0
|
242
|
+
def on_noopts(obj=nil, &block)
|
243
|
+
@on_noopts ||= (obj || block)
|
244
|
+
end
|
245
|
+
alias :on_optionless :on_noopts
|
246
|
+
|
227
247
|
# Returns the parsed list into a option/value hash.
|
228
248
|
#
|
229
249
|
# @example
|
@@ -253,6 +273,7 @@ class Slop
|
|
253
273
|
# Slop#option? but a convenience method for unacceptable method names.
|
254
274
|
#
|
255
275
|
# @param [Object] The object name to check
|
276
|
+
# @since 1.5.0
|
256
277
|
# @return [Boolean] true if this option is present
|
257
278
|
def present?(option_name)
|
258
279
|
!!get(option_name)
|
@@ -298,7 +319,10 @@ class Slop
|
|
298
319
|
def parse_items(items, delete=false, &block)
|
299
320
|
if items.empty? && @on_empty.respond_to?(:call)
|
300
321
|
@on_empty.call self
|
301
|
-
return
|
322
|
+
return items
|
323
|
+
elsif !items.any? {|i| i.to_s[/\A--?/] } && @on_noopts.respond_to?(:call)
|
324
|
+
@on_noopts.call self
|
325
|
+
return items
|
302
326
|
end
|
303
327
|
|
304
328
|
return if execute_command(items, delete)
|
@@ -308,26 +332,8 @@ class Slop
|
|
308
332
|
items.each_with_index do |item, index|
|
309
333
|
item = item.to_s
|
310
334
|
flag = item.sub(/\A--?/, '')
|
311
|
-
option =
|
312
|
-
|
313
|
-
unless option
|
314
|
-
case item
|
315
|
-
when /\A-[^-]/
|
316
|
-
if @multiple_switches
|
317
|
-
enable_multiple_switches(item)
|
318
|
-
next
|
319
|
-
else
|
320
|
-
flag, argument = flag.split('', 2)
|
321
|
-
option = @options[flag]
|
322
|
-
end
|
323
|
-
when /\A--([^=]+)=(.+)\z/
|
324
|
-
option = @options[$1]
|
325
|
-
argument = $2
|
326
|
-
when /\A--no-(.+)\z/
|
327
|
-
option = @options[$1]
|
328
|
-
option.force_argument_value(false) if option
|
329
|
-
end
|
330
|
-
end
|
335
|
+
option, argument = extract_option(item, flag)
|
336
|
+
next if @multiple_switches
|
331
337
|
|
332
338
|
if option
|
333
339
|
option.count += 1
|
@@ -343,13 +349,13 @@ class Slop
|
|
343
349
|
if argument
|
344
350
|
check_matching_argument!(option, argument)
|
345
351
|
option.argument_value = argument
|
346
|
-
option.
|
352
|
+
option.call option.argument_value unless option.omit_exec?(items)
|
347
353
|
else
|
348
354
|
option.argument_value = nil
|
349
355
|
check_optional_argument!(option, flag)
|
350
356
|
end
|
351
|
-
|
352
|
-
option.
|
357
|
+
else
|
358
|
+
option.call unless option.omit_exec?(items)
|
353
359
|
end
|
354
360
|
else
|
355
361
|
check_invalid_option!(item, flag)
|
@@ -363,7 +369,7 @@ class Slop
|
|
363
369
|
end
|
364
370
|
|
365
371
|
def check_valid_argument!(option, argument)
|
366
|
-
if !option.accepts_optional_argument? && argument
|
372
|
+
if !option.accepts_optional_argument? && flag?(argument)
|
367
373
|
raise MissingArgumentError,
|
368
374
|
"'#{option.key}' expects an argument, none given"
|
369
375
|
end
|
@@ -378,7 +384,7 @@ class Slop
|
|
378
384
|
|
379
385
|
def check_optional_argument!(option, flag)
|
380
386
|
if option.accepts_optional_argument?
|
381
|
-
option.
|
387
|
+
option.call
|
382
388
|
else
|
383
389
|
raise MissingArgumentError,
|
384
390
|
"'#{flag}' expects an argument, none given"
|
@@ -414,6 +420,33 @@ class Slop
|
|
414
420
|
end
|
415
421
|
end
|
416
422
|
|
423
|
+
def extract_option(item, flag)
|
424
|
+
if item[0, 1] == '-'
|
425
|
+
option = @options[flag]
|
426
|
+
if !option && @ignore_case
|
427
|
+
option = @options[flag.downcase]
|
428
|
+
end
|
429
|
+
end
|
430
|
+
unless option
|
431
|
+
case item
|
432
|
+
when /\A-[^-]/
|
433
|
+
if @multiple_switches
|
434
|
+
enable_multiple_switches(item)
|
435
|
+
else
|
436
|
+
flag, argument = flag.split('', 2)
|
437
|
+
option = @options[flag]
|
438
|
+
end
|
439
|
+
when /\A--([^=]+)=(.+)\z/
|
440
|
+
option = @options[$1]
|
441
|
+
argument = $2
|
442
|
+
when /\A--no-(.+)\z/
|
443
|
+
option = @options[$1]
|
444
|
+
option.force_argument_value(false) if option
|
445
|
+
end
|
446
|
+
end
|
447
|
+
[option, argument]
|
448
|
+
end
|
449
|
+
|
417
450
|
def execute_command(items, delete)
|
418
451
|
command = items[0]
|
419
452
|
command = @commands.keys.find { |cmd| cmd.to_s == command.to_s }
|
@@ -438,7 +471,7 @@ class Slop
|
|
438
471
|
|
439
472
|
long = args.first
|
440
473
|
boolean = [true, false].include?(long)
|
441
|
-
if !boolean && long.to_s =~ /\A(?:--?)?[a-zA-Z0-9_-]+\z/
|
474
|
+
if !boolean && long.to_s =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\z/
|
442
475
|
options.push args.shift.to_s.sub(/\A--?/, '')
|
443
476
|
else
|
444
477
|
options.push nil
|
@@ -447,4 +480,8 @@ class Slop
|
|
447
480
|
options.push args.first.respond_to?(:to_sym) ? args.shift : nil
|
448
481
|
options.push args.shift ? true : false # force true/false
|
449
482
|
end
|
483
|
+
|
484
|
+
def flag?(str)
|
485
|
+
str =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]+\z/
|
486
|
+
end
|
450
487
|
end
|
data/lib/slop/option.rb
CHANGED
@@ -10,9 +10,6 @@ class Slop
|
|
10
10
|
# @return [String] This options description
|
11
11
|
attr_reader :description
|
12
12
|
|
13
|
-
# @return [Proc, #call] The object to execute when this option is used
|
14
|
-
attr_reader :callback
|
15
|
-
|
16
13
|
# @return [Boolean] True if the option should be grouped at the
|
17
14
|
# tail of the help list
|
18
15
|
attr_reader :tail
|
@@ -35,6 +32,10 @@ class Slop
|
|
35
32
|
# @return [Integer] The amount of times this option has been invoked
|
36
33
|
attr_accessor :count
|
37
34
|
|
35
|
+
# @return [Object] Omit execution of this Options block or callback if
|
36
|
+
# this object exists in the Array of items passed to `Slop.new`
|
37
|
+
attr_accessor :unless
|
38
|
+
|
38
39
|
# @param [Slop] slop
|
39
40
|
# @param [String, #to_s] short
|
40
41
|
# @param [String, #to_s] long
|
@@ -49,6 +50,7 @@ class Slop
|
|
49
50
|
# @option options [Integer] :limit (0)
|
50
51
|
# @option options [Boolean] :tail (false)
|
51
52
|
# @option options [Regexp] :match
|
53
|
+
# @option options [String, #to_s] :unless
|
52
54
|
# @option options [Boolean, String] :help
|
53
55
|
def initialize(slop, short, long, description, argument, options={}, &blk)
|
54
56
|
@slop = slop
|
@@ -64,6 +66,7 @@ class Slop
|
|
64
66
|
@match = options[:match]
|
65
67
|
@delimiter = options[:delimiter] || ','
|
66
68
|
@limit = options[:limit] || 0
|
69
|
+
@unless = options[:unless]
|
67
70
|
@help = options[:help]
|
68
71
|
@help = true if @help.nil?
|
69
72
|
|
@@ -92,7 +95,7 @@ class Slop
|
|
92
95
|
@long_flag || @short_flag
|
93
96
|
end
|
94
97
|
|
95
|
-
# @return [Object] the argument value after it's been
|
98
|
+
# @return [Object] the argument value after it's been cast
|
96
99
|
# according to the `:as` option
|
97
100
|
def argument_value
|
98
101
|
return @argument_value if @forced
|
@@ -122,8 +125,24 @@ class Slop
|
|
122
125
|
@forced = true
|
123
126
|
end
|
124
127
|
|
128
|
+
# Execute the block or callback object associated with this Option
|
129
|
+
#
|
130
|
+
# @param [Object] The object to be sent to `:call`
|
131
|
+
def call(obj=nil)
|
132
|
+
@callback.call(obj) if @callback.respond_to?(:call)
|
133
|
+
end
|
134
|
+
|
135
|
+
# @param [Array] items The original array of objects passed to `Slop.new`
|
136
|
+
# @return [Boolean] true if this options `:unless` argument exists
|
137
|
+
# inside *items*
|
138
|
+
def omit_exec?(items)
|
139
|
+
string = @unless.to_s.sub(/\A--?/, '')
|
140
|
+
items.any? { |i| i.to_s.sub(/\A--?/, '') == string }
|
141
|
+
end
|
142
|
+
|
125
143
|
# This option in a nice pretty string, including a short flag, long
|
126
|
-
#
|
144
|
+
# flag, and description (if they exist).
|
145
|
+
#
|
127
146
|
# @see Slop#help
|
128
147
|
# @return [String]
|
129
148
|
def to_s
|
@@ -160,12 +179,12 @@ class Slop
|
|
160
179
|
|
161
180
|
def value_to_range(value)
|
162
181
|
case value.to_s
|
163
|
-
when /\A(
|
164
|
-
|
165
|
-
when /\A(
|
166
|
-
|
167
|
-
when /\A
|
168
|
-
|
182
|
+
when /\A(-?\d+?)(?:\.\.|-|,)(-?\d+)\z/
|
183
|
+
$1.to_i .. $2.to_i
|
184
|
+
when /\A(-?\d+?)\.\.\.(-?\d+)\z/
|
185
|
+
$1.to_i ... $2.to_i
|
186
|
+
when /\A-?\d+\z/
|
187
|
+
value.to_i
|
169
188
|
else
|
170
189
|
value
|
171
190
|
end
|
data/lib/slop/options.rb
CHANGED
@@ -12,7 +12,13 @@ class Slop
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
# Fetch an Option object
|
16
|
+
#
|
15
17
|
# @param [Object] flag The short/long flag representing the option
|
18
|
+
# @example
|
19
|
+
# opts = Slop.parse { on :v, "Verbose mode" }
|
20
|
+
# opts.options[:v] #=> Option
|
21
|
+
# opts.options[:v].description #=> "Verbose mode"
|
16
22
|
# @return [Option] the option assoiated with this flag
|
17
23
|
def [](flag)
|
18
24
|
if flag.is_a?(Integer)
|
data/test/option_test.rb
CHANGED
@@ -33,10 +33,14 @@ class OptionTest < TestCase
|
|
33
33
|
end
|
34
34
|
|
35
35
|
test 'has a callback when passed a block or callback option' do
|
36
|
-
|
37
|
-
|
36
|
+
item = nil
|
37
|
+
option(:f){ item = "foo" }.call
|
38
|
+
assert_equal "foo", item
|
38
39
|
|
39
|
-
|
40
|
+
assert option(:callback => proc { item = "bar" }).call
|
41
|
+
assert_equal "bar", item
|
42
|
+
|
43
|
+
refute option(:f).call
|
40
44
|
end
|
41
45
|
|
42
46
|
test 'splits argument_value with :as => array' do
|
@@ -66,6 +70,11 @@ class OptionTest < TestCase
|
|
66
70
|
assert_equal :foo, option_value(%w/--name foo/, :name, true, :as => :sym)
|
67
71
|
assert_equal 30, option_value(%w/--age 30/, :age, true, :as => Integer)
|
68
72
|
assert_equal "1.0", option_value(%w/--id 1/, :id, true, :as => Float).to_s
|
73
|
+
|
74
|
+
assert_equal -1, option_value(%w/-i -1/, :i, true, :as => Integer)
|
75
|
+
assert_equal -1, option_value(%w/-i -1.1/, :i, true, :as => Integer)
|
76
|
+
assert_equal "-1.1", option_value(%w/-i -1.1/, :i, true, :as => Float).to_s
|
77
|
+
assert_equal "foo", option_value(%w/--foo1 foo/, :foo1, true)
|
69
78
|
end
|
70
79
|
|
71
80
|
test 'ranges' do
|
@@ -73,6 +82,8 @@ class OptionTest < TestCase
|
|
73
82
|
assert_equal (1..10), option_value(%w/-r 1-10/, :r, true, :as => Range)
|
74
83
|
assert_equal (1..10), option_value(%w/-r 1,10/, :r, true, :as => Range)
|
75
84
|
assert_equal (1...10), option_value(%w/-r 1...10/, :r, true, :as => Range)
|
85
|
+
assert_equal (-1..10), option_value(%w/-r -1..10/, :r, true, :as => Range)
|
86
|
+
assert_equal (1..-10), option_value(%w/-r 1..-10/, :r, true, :as => Range)
|
76
87
|
|
77
88
|
# default back to the string unless a regex is successful
|
78
89
|
# return value.to_i if the value is /\A\d+\z/
|
@@ -145,4 +156,16 @@ class OptionTest < TestCase
|
|
145
156
|
assert_equal 1, slop.options[:x].count
|
146
157
|
assert_equal 3, slop.options[:v].count
|
147
158
|
end
|
159
|
+
|
160
|
+
test 'omit block execution with :unless option' do
|
161
|
+
item = nil
|
162
|
+
opts = Slop.new do
|
163
|
+
on :foo
|
164
|
+
on :bar, true, :unless => 'foo' do; item = "foo"; end
|
165
|
+
end
|
166
|
+
opts.parse %w/--foo --bar 1/
|
167
|
+
|
168
|
+
assert_equal "1", opts[:bar]
|
169
|
+
refute item
|
170
|
+
end
|
148
171
|
end
|
data/test/slop_test.rb
CHANGED
@@ -29,6 +29,15 @@ class SlopTest < TestCase
|
|
29
29
|
assert_kind_of Slop, slop
|
30
30
|
end
|
31
31
|
|
32
|
+
test 'parsing calls to_s on all of the items in the array' do
|
33
|
+
opts = Slop.parse([:'--foo']) { on :foo }
|
34
|
+
assert opts.foo?
|
35
|
+
end
|
36
|
+
|
37
|
+
test '#opt returns an Slop::Option' do
|
38
|
+
assert_kind_of Slop::Option, Slop.new.option(:n)
|
39
|
+
end
|
40
|
+
|
32
41
|
test 'enumerating options' do
|
33
42
|
slop = Slop.new
|
34
43
|
slop.opt(:f, :foo, 'foo')
|
@@ -50,6 +59,13 @@ class SlopTest < TestCase
|
|
50
59
|
end
|
51
60
|
|
52
61
|
assert_equal 'foo', item1
|
62
|
+
assert_equal [], Slop.new { on_empty {} }.parse
|
63
|
+
end
|
64
|
+
|
65
|
+
test 'callback when arguments contain no options' do
|
66
|
+
item = nil
|
67
|
+
Slop.new { on_optionless { item = 'foo' } }.parse %w/a b c/
|
68
|
+
assert_equal 'foo', item
|
53
69
|
end
|
54
70
|
|
55
71
|
test 'multiple switches with the :multiple_switches flag' do
|
@@ -136,6 +152,7 @@ class SlopTest < TestCase
|
|
136
152
|
end
|
137
153
|
assert_equal %w/a/, opts.parse!(%w/--name lee a/)
|
138
154
|
assert_equal %w/--name lee a/, opts.parse(%w/--name lee a/)
|
155
|
+
assert_equal ['foo', :bar, 1], opts.parse(['foo', :bar, 1])
|
139
156
|
end
|
140
157
|
|
141
158
|
test '#parse does not remove parsed items' do
|
@@ -359,4 +376,11 @@ class SlopTest < TestCase
|
|
359
376
|
opts = Slop.new(:help => true, :io => io, :exit_on_help => false)
|
360
377
|
assert opts.parse %w/--help/
|
361
378
|
end
|
379
|
+
|
380
|
+
test 'ignoring case' do
|
381
|
+
opts = Slop.new(:ignore_case => true)
|
382
|
+
opts.on :n, :name, true
|
383
|
+
opts.parse %w/--NAME lee/
|
384
|
+
assert_equal 'lee', opts[:name]
|
385
|
+
end
|
362
386
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: slop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.
|
5
|
+
version: 1.6.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Lee Jarvis
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-05-
|
13
|
+
date: 2011-05-18 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -26,6 +26,7 @@ files:
|
|
26
26
|
- .gemtest
|
27
27
|
- .gitignore
|
28
28
|
- .yardopts
|
29
|
+
- CHANGES.md
|
29
30
|
- LICENSE
|
30
31
|
- README.md
|
31
32
|
- Rakefile
|