slop 1.5.5 → 1.6.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.
@@ -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