slop 3.3.3 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
  [![Build Status](https://secure.travis-ci.org/injekt/slop.png)](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 "ruby foo.rb [options]\n"
30
- on :name=, 'Your name'
31
- on :p, :password, 'Your password', :argument => :optional
32
- on :v, :verbose, 'Enable verbose mode'
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
- Slop supports several methods of writing options:
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
- ```ruby
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
- Features
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
- Check out the following wiki pages for more features:
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
- * [Ranges](https://github.com/injekt/slop/wiki/Ranges)
93
- * [Auto Create](https://github.com/injekt/slop/wiki/Auto-Create)
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
@@ -4,7 +4,7 @@ require 'slop/commands'
4
4
  class Slop
5
5
  include Enumerable
6
6
 
7
- VERSION = '3.3.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
- init_and_parse(items, false, config, &block)
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
- init_and_parse(items, true, config, &block)
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
- parse_items(items, false, &block)
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
- parse_items(items, true, &block)
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
- def to_hash
236
- Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
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 respond_to?(method)
281
- method = method.to_s
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 config[:banner]
354
- config[:banner] << "\n"
355
- config[:banner] << "#{@separators[0]}\n" if @separators[0]
356
- config[:banner] + optstr
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
- # Parse a list of items and process their values.
440
+ # Convenience method for present?(:option).
371
441
  #
372
- # items - The Array of items to process.
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
- # Returns the original Array of items.
378
- def parse_items(items, delete, &block)
379
- if items.empty? && @callbacks[:empty]
380
- @callbacks[:empty].each { |cb| cb.call(self) }
381
- return items
382
- end
383
-
384
- items.each_with_index do |item, index|
385
- @trash << index && break if item == '--'
386
- autocreate(items, index) if config[:autocreate]
387
- process_item(items, index, &block) unless @trash.include?(index)
388
- end
389
- items.reject!.with_index { |item, index| @trash.include?(index) } if delete
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 = config.merge!(objects.last)
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
@@ -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
- parse_items(items)
118
- end
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
- parse_items(items, true)
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
- helps.merge!('Global options' => globals.to_s) if globals
145
- helps.merge!('Other options' => defaults.to_s) if defaults
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, bang)
183
+ def execute_arguments!(items)
185
184
  @arguments = items.take_while { |arg| !arg.start_with?('-') }
186
- items.shift(@arguments.size) if bang
185
+ items.shift @arguments.size
187
186
  end
188
187
 
189
188
  # Returns nothing.
190
- def execute_global_opts(items, bang)
189
+ def execute_global_opts!(items)
191
190
  if global_opts = commands['global']
192
- bang ? global_opts.parse!(items) : global_opts.parse(items)
191
+ global_opts.parse! items
193
192
  end
194
193
  end
195
194
 
196
195
  end
197
- end
196
+ end
@@ -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.send(:define_method, predicate) { !!@config[key] }
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 += "--#{long}"
132
+ out << "--#{long}"
134
133
  size = long.size
135
134
  diff = @slop.config[:longest_flag] - size
136
- out += " " * (diff + 6)
135
+ out << (' ' * (diff + 6))
137
136
  else
138
- out += " " * (@slop.config[:longest_flag] + 8)
137
+ out << (' ' * (@slop.config[:longest_flag] + 8))
139
138
  end
140
139
 
141
140
  "#{out}#{description}"
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'slop'
3
- s.version = '3.3.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
@@ -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
@@ -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.3.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: 2012-08-29 00:00:00.000000000 Z
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: '0'
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: