slop 1.5.5 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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.5.5'
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 a required option or not.
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
- # Add an object to be called when Slop has no values to parse
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 :call)
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 = @options[flag] if item[/\A-/]
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.callback.call option.argument_value if option.callback
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
- elsif option.callback
352
- option.callback.call nil
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 =~ /\A--?.+\z/
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.callback.call nil if option.callback
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
@@ -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 case
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
- # flag, and description (if they exist).
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(\d+?)(?:\.\.|-|,)(\d+)\z/
164
- Integer($1) .. Integer($2)
165
- when /\A(\d+?)\.\.\.(\d+)\z/
166
- Integer($1) ... Integer($2)
167
- when /\A\d+\z/
168
- Integer(value)
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
@@ -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)
@@ -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
- assert option(:f){}.callback
37
- assert option(:callback => proc {}).callback
36
+ item = nil
37
+ option(:f){ item = "foo" }.call
38
+ assert_equal "foo", item
38
39
 
39
- refute option(:f).callback
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
@@ -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.5
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-03 00:00:00 +01:00
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