trollop 1.4 → 1.5

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.
Files changed (5) hide show
  1. data/FAQ.txt +6 -4
  2. data/History.txt +7 -0
  3. data/lib/trollop.rb +59 -26
  4. data/test/test_trollop.rb +73 -7
  5. metadata +2 -2
data/FAQ.txt CHANGED
@@ -20,12 +20,14 @@ A: Because it's ambiguous whether these are arguments or negative
20
20
 
21
21
  I could be very clever and detect when there are no arguments
22
22
  that require floating-point parameters, and allow such short option
23
- names in those cases, but I'd rather be simple and consistent.
23
+ names in those cases, but opted for simplicity and consistency.
24
24
 
25
25
  Q: Why does Trollop disallow options appearing multiple times, despite
26
26
  the POSIX standard allowing it?
27
27
  A: Because basically I think it's confusing, and more often than
28
28
  not, a symptom of you making a mistake (e.g. getting lost in a long
29
- command line and accidentally setting the same thing twice.) Given
30
- that, I really don't see that much advantage to "-vvvvv" over "-v
31
- 5", so Trollop will produce an error if it happens.
29
+ command line and accidentally setting the same thing twice.)
30
+ I also don't see that much advantage to "-vvvvv" over "-v 5", so
31
+ Trollop will produce an error if you try to use the same argument
32
+ multiple times.
33
+
@@ -1,3 +1,10 @@
1
+ == 1.5 / 2007-03-31
2
+ * --help and --version do the right thing even if the rest of the
3
+ command line is incorrect.
4
+ * Added #conflicts and #depends to model dependencies and exclusivity
5
+ between arguments.
6
+ * Minor bugfixes.
7
+
1
8
  == 1.4 / 2007-03-26
2
9
  * Disable short options with :short => :none.
3
10
  * Minor bugfixes and error message improvements.
@@ -5,7 +5,7 @@
5
5
 
6
6
  module Trollop
7
7
 
8
- VERSION = "1.4"
8
+ VERSION = "1.5"
9
9
 
10
10
  ## Thrown by Parser in the event of a commandline error. Not needed if
11
11
  ## you're using the Trollop::options entry.
@@ -47,6 +47,7 @@ class Parser
47
47
  @long = {}
48
48
  @short = {}
49
49
  @order = []
50
+ @constraints = []
50
51
 
51
52
  #instance_eval(&b) if b # can't take arguments
52
53
  cloaker(&b).bind(self).call(*a) if b
@@ -170,6 +171,20 @@ class Parser
170
171
  def banner s; @order << [:text, s] end
171
172
  alias :text :banner
172
173
 
174
+ ## Marks two (or more!) options as requiring each other. Only
175
+ ## handles undirected dependcies. Directed dependencies are better
176
+ ## modeled with #die.
177
+ def depends *syms
178
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
179
+ @constraints << [:depends, syms]
180
+ end
181
+
182
+ ## Marks two (or more!) options as conflicting.
183
+ def conflicts *syms
184
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
185
+ @constraints << [:conflicts, syms]
186
+ end
187
+
173
188
  ## yield successive arg, parameter pairs
174
189
  def each_arg args # :nodoc:
175
190
  remains = []
@@ -210,7 +225,7 @@ class Parser
210
225
  remains
211
226
  end
212
227
 
213
- def parse args #:nodoc:
228
+ def parse cmdline #:nodoc:
214
229
  vals = {}
215
230
  required = {}
216
231
  found = {}
@@ -218,13 +233,15 @@ class Parser
218
233
  opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
219
234
  opt :help, "Show this message" unless @specs[:help] || @long["help"]
220
235
 
221
- @specs.each do |name, opts|
222
- required[name] = true if opts[:required]
223
- vals[name] = opts[:default]
236
+ @specs.each do |sym, opts|
237
+ required[sym] = true if opts[:required]
238
+ vals[sym] = opts[:default]
224
239
  end
225
240
 
226
- @leftovers = each_arg args do |arg, param|
227
- name =
241
+ ## resolve symbols
242
+ args = []
243
+ @leftovers = each_arg cmdline do |arg, param|
244
+ sym =
228
245
  case arg
229
246
  when /^-([^-])$/
230
247
  @short[$1]
@@ -233,37 +250,53 @@ class Parser
233
250
  else
234
251
  raise CommandlineError, "invalid argument syntax: '#{arg}'"
235
252
  end
236
- raise CommandlineError, "unknown argument '#{arg}'" unless name
237
- raise CommandlineError, "option '#{arg}' specified multiple times" if found[name]
253
+ raise CommandlineError, "unknown argument '#{arg}'" unless sym
254
+ raise CommandlineError, "option '#{arg}' specified multiple times" if found[sym]
255
+ args << [sym, arg, param]
256
+ found[sym] = true
257
+
258
+ @specs[sym][:type] != :flag # take params on all except flags
259
+ end
260
+
261
+ ## check for version and help args
262
+ raise VersionNeeded if args.any? { |sym, *a| sym == :version }
263
+ raise HelpNeeded if args.any? { |sym, *a| sym == :help }
264
+
265
+ ## check constraint satisfaction
266
+ @constraints.each do |type, syms|
267
+ constraint_sym = syms.find { |sym| found[sym] }
268
+ next unless constraint_sym
269
+
270
+ case type
271
+ when :depends
272
+ syms.each { |sym| raise CommandlineError, "--#{@long[constraint_sym]} requires --#{@long[sym]}" unless found[sym] }
273
+ when :conflicts
274
+ syms.each { |sym| raise CommandlineError, "--#{@long[constraint_sym]} conflicts with --#{@long[sym]}" if found[sym] && sym != constraint_sym }
275
+ end
276
+ end
277
+
278
+ raise CommandlineError, "option '#{required.keys.first}' must be specified" if required.any? { |sym, x| !found[sym] }
238
279
 
239
- raise VersionNeeded if name == :version
240
- raise HelpNeeded if name == :help
280
+ ## parse parameters
281
+ args.each do |sym, arg, param|
282
+ opts = @specs[sym]
241
283
 
242
- found[name] = true
243
- opts = @specs[name]
284
+ raise CommandlineError, "option '#{arg}' needs a parameter" unless param || opts[:type] == :flag
244
285
 
245
286
  case opts[:type]
246
287
  when :flag
247
- vals[name] = !opts[:default]
248
- false
288
+ vals[sym] = !opts[:default]
249
289
  when :int
250
- raise CommandlineError, "option '#{arg}' needs a parameter" unless param
251
290
  raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
252
- vals[name] = param.to_i
253
- true
291
+ vals[sym] = param.to_i
254
292
  when :float
255
- raise CommandlineError, "option '#{arg}' needs a parameter" unless param
256
293
  raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
257
- vals[name] = param.to_f
258
- true
294
+ vals[sym] = param.to_f
259
295
  when :string
260
- raise CommandlineError, "option '#{arg}' needs a parameter" unless param
261
- vals[name] = param
262
- true
296
+ vals[sym] = param.to_s
263
297
  end
264
298
  end
265
299
 
266
- raise CommandlineError, "option '#{required.keys.first}' must be specified" if required.any? { |name, x| !found[name] }
267
300
  vals
268
301
  end
269
302
 
@@ -422,7 +455,7 @@ end
422
455
  ## Trollop::die "need at least one filename" if ARGV.empty?
423
456
  def die arg, msg=nil
424
457
  if msg
425
- $stderr.puts "Error: parameter for option '--#{@p.specs[arg][:long]}' or '-#{@p.specs[arg][:short]}' #{msg}."
458
+ $stderr.puts "Error: argument --#{@p.specs[arg][:long]} #{msg}."
426
459
  else
427
460
  $stderr.puts "Error: #{arg}."
428
461
  end
@@ -17,13 +17,13 @@ class Trollop < ::Test::Unit::TestCase
17
17
 
18
18
  def test_unknown_arguments
19
19
  assert_raise(CommandlineError) { @p.parse(%w(--arg)) }
20
- @p.opt "arg", "desc"
20
+ @p.opt "arg"
21
21
  assert_nothing_raised { @p.parse(%w(--arg)) }
22
22
  assert_raise(CommandlineError) { @p.parse(%w(--arg2)) }
23
23
  end
24
24
 
25
25
  def test_syntax_check
26
- @p.opt "arg", "desc"
26
+ @p.opt "arg"
27
27
 
28
28
  assert_nothing_raised { @p.parse(%w(--arg)) }
29
29
  assert_nothing_raised { @p.parse(%w(arg)) }
@@ -54,8 +54,8 @@ class Trollop < ::Test::Unit::TestCase
54
54
 
55
55
  ## flags that don't take arguments ignore them
56
56
  def test_arglessflags_refuse_args
57
- @p.opt "goodarg", "desc"
58
- @p.opt "goodarg2", "desc"
57
+ @p.opt "goodarg"
58
+ @p.opt "goodarg2"
59
59
  assert_nothing_raised { @p.parse(%w(--goodarg)) }
60
60
  assert_nothing_raised { @p.parse(%w(--goodarg --goodarg2)) }
61
61
  opts = @p.parse %w(--goodarg a)
@@ -135,8 +135,8 @@ class Trollop < ::Test::Unit::TestCase
135
135
 
136
136
  ## two args can't have the same name
137
137
  def test_conflicting_names_are_detected
138
- assert_nothing_raised { @p.opt "goodarg", "desc" }
139
- assert_raise(ArgumentError) { @p.opt "goodarg", "desc" }
138
+ assert_nothing_raised { @p.opt "goodarg" }
139
+ assert_raise(ArgumentError) { @p.opt "goodarg" }
140
140
  end
141
141
 
142
142
  ## two args can't have the same :long
@@ -195,7 +195,7 @@ class Trollop < ::Test::Unit::TestCase
195
195
  end
196
196
 
197
197
  def test_version_only_appears_if_set
198
- @p.opt "arg", "desc"
198
+ @p.opt "arg"
199
199
  assert_raise(CommandlineError) { @p.parse %w(-v) }
200
200
  @p.version "trollop 1.2.3.4"
201
201
  assert_raise(VersionNeeded) { @p.parse %w(-v) }
@@ -376,6 +376,72 @@ EOM
376
376
  assert_nothing_raised { @p.parse %w(-v) }
377
377
  end
378
378
 
379
+ def test_version_and_help_override_errors
380
+ @p.opt :asdf, "desc", :type => String
381
+ @p.version "version"
382
+ assert_nothing_raised { @p.parse %w(--asdf goat) }
383
+ assert_raises(CommandlineError) { @p.parse %w(--asdf) }
384
+ assert_raises(HelpNeeded) { @p.parse %w(--asdf --help) }
385
+ assert_raises(VersionNeeded) { @p.parse %w(--asdf --version) }
386
+ end
387
+
388
+ def test_conflicts
389
+ @p.opt :one
390
+ assert_raises(ArgumentError) { @p.conflicts :one, :two }
391
+ @p.opt :two
392
+ assert_nothing_raised { @p.conflicts :one, :two }
393
+ assert_nothing_raised { @p.parse %w(--one) }
394
+ assert_nothing_raised { @p.parse %w(--two) }
395
+ assert_raises(CommandlineError) { opts = @p.parse %w(--one --two) }
396
+
397
+ @p.opt :hello
398
+ @p.opt :yellow
399
+ @p.opt :mellow
400
+ @p.opt :jello
401
+ @p.conflicts :hello, :yellow, :mellow, :jello
402
+ assert_raises(CommandlineError) { opts = @p.parse %w(--hello --yellow --mellow --jello) }
403
+ assert_raises(CommandlineError) { opts = @p.parse %w(--hello --mellow --jello) }
404
+ assert_raises(CommandlineError) { opts = @p.parse %w(--hello --jello) }
405
+
406
+ assert_nothing_raised { opts = @p.parse %w(--hello) }
407
+ assert_nothing_raised { opts = @p.parse %w(--jello) }
408
+ assert_nothing_raised { opts = @p.parse %w(--yellow) }
409
+ assert_nothing_raised { opts = @p.parse %w(--mellow) }
410
+
411
+ assert_nothing_raised { opts = @p.parse %w(--mellow --one) }
412
+ assert_nothing_raised { opts = @p.parse %w(--mellow --two) }
413
+
414
+ assert_raises(CommandlineError) { opts = @p.parse %w(--mellow --two --jello) }
415
+ assert_raises(CommandlineError) { opts = @p.parse %w(--one --mellow --two --jello) }
416
+ end
417
+
418
+ def test_depends
419
+ @p.opt :one
420
+ assert_raises(ArgumentError) { @p.depends :one, :two }
421
+ @p.opt :two
422
+ assert_nothing_raised { @p.depends :one, :two }
423
+ assert_nothing_raised { opts = @p.parse %w(--one --two) }
424
+ assert_raises(CommandlineError) { @p.parse %w(--one) }
425
+ assert_raises(CommandlineError) { @p.parse %w(--two) }
426
+
427
+ @p.opt :hello
428
+ @p.opt :yellow
429
+ @p.opt :mellow
430
+ @p.opt :jello
431
+ @p.depends :hello, :yellow, :mellow, :jello
432
+ assert_nothing_raised { opts = @p.parse %w(--hello --yellow --mellow --jello) }
433
+ assert_raises(CommandlineError) { opts = @p.parse %w(--hello --mellow --jello) }
434
+ assert_raises(CommandlineError) { opts = @p.parse %w(--hello --jello) }
435
+
436
+ assert_raises(CommandlineError) { opts = @p.parse %w(--hello) }
437
+ assert_raises(CommandlineError) { opts = @p.parse %w(--mellow) }
438
+
439
+ assert_nothing_raised { opts = @p.parse %w(--hello --yellow --mellow --jello --one --two) }
440
+ assert_nothing_raised { opts = @p.parse %w(--hello --yellow --mellow --jello --one --two a b c) }
441
+
442
+ assert_raises(CommandlineError) { opts = @p.parse %w(--mellow --two --jello --one) }
443
+ end
444
+
379
445
  end
380
446
 
381
447
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: trollop
5
5
  version: !ruby/object:Gem::Version
6
- version: "1.4"
7
- date: 2007-03-26 00:00:00 -07:00
6
+ version: "1.5"
7
+ date: 2007-03-31 00:00:00 -07:00
8
8
  summary: Trollop is YAFCLAP --- yet another fine commandline argument processing library for Ruby. Trollop is designed to provide the maximal amount of GNU-style argument processing in the minimum number of lines of code (for you, the programmer).
9
9
  require_paths:
10
10
  - lib