slop 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.md +10 -1
- data/lib/slop.rb +130 -43
- data/slop.gemspec +1 -1
- data/test/slop_test.rb +35 -0
- metadata +2 -2
data/CHANGES.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
|
+
2.1.0 (2011-08-03)
|
2
|
+
------------------
|
3
|
+
|
4
|
+
* Added `Slop#missing` for returning a list of missing options parsed
|
5
|
+
* Allow `Slop#present?` to accept multiple arguments
|
6
|
+
* Added `:all_accept_arguments` to Slop configuration options, this saves
|
7
|
+
having to specify that every option takes an argument
|
8
|
+
* Added `Slop#to_struct` for building new classes from options
|
9
|
+
|
1
10
|
2.0.0 (2011-07-07)
|
2
|
-
|
11
|
+
------------------
|
3
12
|
|
4
13
|
* Deprecations:
|
5
14
|
* Removed `Slop::Options#to_hash` continue using `Slop#to_hash` directly.
|
data/lib/slop.rb
CHANGED
@@ -23,23 +23,56 @@ class Slop
|
|
23
23
|
class Option < Struct.new(:short_flag, :long_flag, :description,
|
24
24
|
:tail, :match, :help, :required, :forced, :count)
|
25
25
|
|
26
|
-
# @param [Slop] slop
|
27
|
-
#
|
28
|
-
# @param [String, #to_s]
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# @param [
|
26
|
+
# @param [Slop] slop The Slop object this Option belongs to
|
27
|
+
#
|
28
|
+
# @param [String, #to_s] short The short flag representing this Option
|
29
|
+
# without prefix (ie: `a`)
|
30
|
+
#
|
31
|
+
# @param [String, #to_s] long The long flag representing this Option
|
32
|
+
# without the prefix (ie: `foo`)
|
33
|
+
#
|
34
|
+
# @param [String] description This options description
|
35
|
+
#
|
36
|
+
# @param [Boolean] argument True if this option takes an argument
|
37
|
+
#
|
32
38
|
# @option options [Boolean] :optional
|
39
|
+
# * When true, this option takes an optional argument, ie an argument
|
40
|
+
# does not **have** to be supplied.
|
41
|
+
#
|
33
42
|
# @option options [Boolean] :argument
|
43
|
+
# * True if this option takes an argument.
|
44
|
+
#
|
34
45
|
# @option options [Object] :default
|
46
|
+
# * The default value for this option when no argument is given
|
47
|
+
#
|
35
48
|
# @option options [Proc, #call] :callback
|
49
|
+
# * The callback object, used instead of passing a block to this option
|
50
|
+
#
|
36
51
|
# @option options [String, #to_s] :delimiter (',')
|
52
|
+
# * A delimiter string when processing this option as a list
|
53
|
+
#
|
37
54
|
# @option options [Integer] :limit (0)
|
55
|
+
# * A limit, used when processing this option as a list
|
56
|
+
#
|
38
57
|
# @option options [Boolean] :tail (false)
|
58
|
+
# * When true, this option will be grouped at the bottom of the help
|
59
|
+
# text instead of in order of processing
|
60
|
+
#
|
39
61
|
# @option options [Regexp] :match
|
62
|
+
# * A regular expression this option should match
|
63
|
+
#
|
40
64
|
# @option options [String, #to_s] :unless
|
65
|
+
# * Used by `omit_exec` for omitting execution of this options callback
|
66
|
+
# if another option exists
|
67
|
+
#
|
41
68
|
# @option options [Boolean, String] :help (true)
|
69
|
+
# * If this option is a string, it'll be appended to the long flag
|
70
|
+
# help text (before the description). When false, no help information
|
71
|
+
# will be displayed for this option
|
72
|
+
#
|
42
73
|
# @option options [Boolean] :required (false)
|
74
|
+
# * When true, this option is considered mandatory. That is, when not
|
75
|
+
# supplied, Slop will raise a `MissingOptionError`
|
43
76
|
def initialize(slop, short, long, description, argument, options, &blk)
|
44
77
|
@slop = slop
|
45
78
|
|
@@ -66,7 +99,10 @@ class Slop
|
|
66
99
|
@callback = blk if block_given?
|
67
100
|
@callback ||= @options[:callback]
|
68
101
|
|
69
|
-
|
102
|
+
if long_flag && long_flag.size > @slop.longest_flag
|
103
|
+
@slop.longest_flag = long_flag.size
|
104
|
+
@slop.longest_flag += help.size if help.respond_to?(:to_str)
|
105
|
+
end
|
70
106
|
end
|
71
107
|
|
72
108
|
# @return [Boolean] true if this option expects an argument
|
@@ -194,13 +230,6 @@ class Slop
|
|
194
230
|
end
|
195
231
|
end
|
196
232
|
|
197
|
-
def build_longest_flag
|
198
|
-
if long_flag && long_flag.size > @slop.longest_flag
|
199
|
-
@slop.longest_flag = long_flag.size
|
200
|
-
@slop.longest_flag += help.size if help.respond_to? :to_str
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
233
|
end
|
205
234
|
|
206
235
|
# Used to hold a list of Option objects. This class inherits from Array
|
@@ -234,7 +263,7 @@ class Slop
|
|
234
263
|
end
|
235
264
|
|
236
265
|
# @return [String] The current version string
|
237
|
-
VERSION = '2.
|
266
|
+
VERSION = '2.1.0'
|
238
267
|
|
239
268
|
# Parses the items from a CLI format into a friendly object
|
240
269
|
#
|
@@ -329,9 +358,13 @@ class Slop
|
|
329
358
|
# @option opts [Boolean] :completion (true)
|
330
359
|
# * When true, commands will be auto completed. Ie `foobar` will be
|
331
360
|
# executed simply when `foo` `fo` or `foob` are used
|
361
|
+
#
|
362
|
+
# @option options [Boolean] :all_accept_arguments (false)
|
363
|
+
# * When true, every option added will take an argument, this saves
|
364
|
+
# having to enable it for every option
|
332
365
|
def initialize(*opts, &block)
|
333
366
|
sloptions = opts.last.is_a?(Hash) ? opts.pop : {}
|
334
|
-
sloptions[:banner] = opts.shift if opts[0].respond_to?
|
367
|
+
sloptions[:banner] = opts.shift if opts[0].respond_to?(:to_str)
|
335
368
|
opts.each { |o| sloptions[o] = true }
|
336
369
|
|
337
370
|
@options = Options.new
|
@@ -433,7 +466,7 @@ class Slop
|
|
433
466
|
option = @options[key]
|
434
467
|
option ? option.argument_value : @commands[key]
|
435
468
|
end
|
436
|
-
alias
|
469
|
+
alias get []
|
437
470
|
|
438
471
|
# Specify an option with a short or long version, description and type
|
439
472
|
#
|
@@ -459,15 +492,18 @@ class Slop
|
|
459
492
|
def option(*args, &block)
|
460
493
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
461
494
|
|
462
|
-
short, long, desc, arg, extras = clean_options
|
495
|
+
short, long, desc, arg, extras = clean_options(args)
|
496
|
+
|
463
497
|
options.merge!(extras)
|
464
|
-
|
498
|
+
options[:argument] = true if @sloptions[:all_accept_arguments]
|
499
|
+
|
500
|
+
option = Option.new(self, short, long, desc, arg, options, &block)
|
465
501
|
@options << option
|
466
502
|
|
467
503
|
option
|
468
504
|
end
|
469
|
-
alias
|
470
|
-
alias
|
505
|
+
alias opt option
|
506
|
+
alias on option
|
471
507
|
|
472
508
|
# Namespace options depending on what command is executed
|
473
509
|
#
|
@@ -515,7 +551,7 @@ class Slop
|
|
515
551
|
def on_empty(obj=nil, &block)
|
516
552
|
@on_empty ||= (obj || block)
|
517
553
|
end
|
518
|
-
alias
|
554
|
+
alias on_empty= on_empty
|
519
555
|
|
520
556
|
# Trigger an event when the arguments contain no options
|
521
557
|
#
|
@@ -529,7 +565,7 @@ class Slop
|
|
529
565
|
def on_noopts(obj=nil, &block)
|
530
566
|
@on_noopts ||= (obj || block)
|
531
567
|
end
|
532
|
-
alias
|
568
|
+
alias on_optionless on_noopts
|
533
569
|
|
534
570
|
# Add an execution block (for commands)
|
535
571
|
#
|
@@ -546,7 +582,7 @@ class Slop
|
|
546
582
|
# @param [Array] args The list of arguments to send to this command
|
547
583
|
# is invoked
|
548
584
|
# @since 1.8.0
|
549
|
-
# @
|
585
|
+
# @yield [Slop] an instance of Slop for this command
|
550
586
|
def execute(args=[], &block)
|
551
587
|
if block_given?
|
552
588
|
@execution_block = block
|
@@ -571,7 +607,52 @@ class Slop
|
|
571
607
|
hsh
|
572
608
|
end
|
573
609
|
end
|
574
|
-
alias
|
610
|
+
alias to_h to_hash
|
611
|
+
|
612
|
+
# Return parsed items as a new Class
|
613
|
+
#
|
614
|
+
# @example
|
615
|
+
# opts = Slop.new do
|
616
|
+
# on :n, :name, 'Persons name', true
|
617
|
+
# on :a, :age, 'Persons age', true, :as => :int
|
618
|
+
# on :s, :sex, 'Persons sex m/f', true, :match => /^[mf]$/
|
619
|
+
# on :A, :admin, 'Enable admin mode'
|
620
|
+
# end
|
621
|
+
#
|
622
|
+
# opts.parse %w[ --name Lee --age 22 -s m --admin ]
|
623
|
+
#
|
624
|
+
# person = opts.to_struct("Person")
|
625
|
+
# person.class #=> Struct::Person
|
626
|
+
# person.name #=> 'Lee'
|
627
|
+
# person.age #=> 22
|
628
|
+
# person.sex #=> m
|
629
|
+
# person.admin #=> true
|
630
|
+
#
|
631
|
+
# @param [String] name The name of this class
|
632
|
+
# @return [Class] The new class, or nil if there are no options
|
633
|
+
# @since 2.0.0
|
634
|
+
def to_struct(name=nil)
|
635
|
+
hash = to_hash
|
636
|
+
Struct.new(name, *hash.keys).new(*hash.values) unless hash.empty?
|
637
|
+
end
|
638
|
+
|
639
|
+
# Fetch a list of options which were missing from the parsed list
|
640
|
+
#
|
641
|
+
# @example
|
642
|
+
# opts = Slop.new do
|
643
|
+
# on :n, :name, 'Your name', true
|
644
|
+
# on :p, :password, 'Your password', true
|
645
|
+
# on :A, 'Use auth?'
|
646
|
+
# end
|
647
|
+
#
|
648
|
+
# opts.parse %w[ --name Lee ]
|
649
|
+
# opts.missing #=> ['password', 'a']
|
650
|
+
#
|
651
|
+
# @return [Array] A list of options missing from the parsed string
|
652
|
+
# @since 2.1.0
|
653
|
+
def missing
|
654
|
+
@options.select { |opt| not present?(opt.key) }.map { |opt| opt.key }
|
655
|
+
end
|
575
656
|
|
576
657
|
# Allows you to check whether an option was specified in the parsed list
|
577
658
|
#
|
@@ -599,11 +680,11 @@ class Slop
|
|
599
680
|
# Does the same as Slop#option? but a convenience method for unacceptable
|
600
681
|
# method names
|
601
682
|
#
|
602
|
-
# @param [Object] The object name to check
|
683
|
+
# @param [Object] The object name(s) to check
|
603
684
|
# @since 1.5.0
|
604
|
-
# @return [Boolean] true if
|
605
|
-
def present?(
|
606
|
-
@options[
|
685
|
+
# @return [Boolean] true if these options are present, false otherwise
|
686
|
+
def present?(*option_names)
|
687
|
+
option_names.all? { |opt| @options[opt] && @options[opt].count > 0 }
|
607
688
|
end
|
608
689
|
|
609
690
|
# Returns the banner followed by available options listed on the next line
|
@@ -634,7 +715,7 @@ class Slop
|
|
634
715
|
|
635
716
|
parts.join("\n\n")
|
636
717
|
end
|
637
|
-
alias
|
718
|
+
alias help to_s
|
638
719
|
|
639
720
|
# @return [String] This Slop object will options and configuration
|
640
721
|
# settings revealed
|
@@ -669,10 +750,10 @@ class Slop
|
|
669
750
|
# * Remove non-parsed items if `delete` is true
|
670
751
|
# * Yield any non-options to the block (if one is given)
|
671
752
|
def parse_items(items, delete=false, &block)
|
672
|
-
if items.empty?
|
753
|
+
if items.empty? and @on_empty.respond_to?(:call)
|
673
754
|
@on_empty.call self
|
674
755
|
return items
|
675
|
-
elsif
|
756
|
+
elsif not items.any? {|i| i.to_s[/\A--?/] } and @on_noopts.respond_to?(:call)
|
676
757
|
@on_noopts.call self
|
677
758
|
return items
|
678
759
|
elsif execute_command(items, delete)
|
@@ -695,7 +776,7 @@ class Slop
|
|
695
776
|
autocreate(flag, index, items) if @autocreate
|
696
777
|
option, argument = extract_option(item, flag)
|
697
778
|
|
698
|
-
if @multiple_switches
|
779
|
+
if @multiple_switches and item[/\A-[^-]/] and not option
|
699
780
|
trash << index
|
700
781
|
next
|
701
782
|
end
|
@@ -706,16 +787,16 @@ class Slop
|
|
706
787
|
next if option.forced
|
707
788
|
option.argument_value = true
|
708
789
|
|
709
|
-
if option.expects_argument?
|
790
|
+
if option.expects_argument? or option.accepts_optional_argument?
|
710
791
|
argument ||= items.at(index + 1)
|
711
792
|
trash << index + 1
|
712
793
|
|
713
|
-
if
|
794
|
+
if not option.accepts_optional_argument? and argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
|
714
795
|
raise MissingArgumentError, "'#{option.key}' expects an argument, none given"
|
715
796
|
end
|
716
797
|
|
717
798
|
if argument
|
718
|
-
if option.match
|
799
|
+
if option.match and not argument.match(option.match)
|
719
800
|
raise InvalidArgumentError, "'#{argument}' does not match #{option.match.inspect}"
|
720
801
|
end
|
721
802
|
|
@@ -729,8 +810,8 @@ class Slop
|
|
729
810
|
option.call unless option.omit_exec?(items)
|
730
811
|
end
|
731
812
|
else
|
732
|
-
@invalid_options << flag if item[/\A--?/]
|
733
|
-
block.call(item) if block_given?
|
813
|
+
@invalid_options << flag if item[/\A--?/] and @strict
|
814
|
+
block.call(item) if block_given? and not trash.include?(index)
|
734
815
|
end
|
735
816
|
end
|
736
817
|
|
@@ -749,7 +830,7 @@ class Slop
|
|
749
830
|
end
|
750
831
|
|
751
832
|
def raise_if_invalid_options!
|
752
|
-
return if
|
833
|
+
return if not @strict or @invalid_options.empty?
|
753
834
|
message = "Unknown option#{'s' if @invalid_options.size > 1}"
|
754
835
|
message << ' -- ' << @invalid_options.map { |o| "'#{o}'" }.join(', ')
|
755
836
|
raise InvalidOptionError, message
|
@@ -770,10 +851,13 @@ class Slop
|
|
770
851
|
# flag was not found
|
771
852
|
def enable_multiple_switches(item)
|
772
853
|
item[1..-1].each_char do |switch|
|
773
|
-
|
854
|
+
option = @options[switch]
|
855
|
+
|
856
|
+
if option
|
774
857
|
if option.expects_argument?
|
775
858
|
raise MissingArgumentError, "'-#{switch}' expects an argument, used in multiple_switch context"
|
776
859
|
end
|
860
|
+
|
777
861
|
option.argument_value = true
|
778
862
|
option.count += 1
|
779
863
|
else
|
@@ -812,6 +896,7 @@ class Slop
|
|
812
896
|
option = @options[flag]
|
813
897
|
option ||= @options[flag.downcase] if @ignore_case
|
814
898
|
end
|
899
|
+
|
815
900
|
unless option
|
816
901
|
case item
|
817
902
|
when /\A-[^-]/
|
@@ -828,6 +913,7 @@ class Slop
|
|
828
913
|
option.force_argument_value(false) if option
|
829
914
|
end
|
830
915
|
end
|
916
|
+
|
831
917
|
[option, argument]
|
832
918
|
end
|
833
919
|
|
@@ -881,7 +967,7 @@ class Slop
|
|
881
967
|
def clean_options(args)
|
882
968
|
options = []
|
883
969
|
extras = {}
|
884
|
-
extras[:as] =args.find {|c| c.is_a? Class }
|
970
|
+
extras[:as] = args.find { |c| c.is_a? Class }
|
885
971
|
args.delete(extras[:as])
|
886
972
|
extras.delete(:as) if extras[:as].nil?
|
887
973
|
|
@@ -895,12 +981,13 @@ class Slop
|
|
895
981
|
|
896
982
|
long = args.first
|
897
983
|
boolean = [true, false].include? long
|
898
|
-
|
984
|
+
|
985
|
+
if not boolean and long.to_s =~ /\A(?:--?)?[a-z_-]+\s[A-Z\s\[\]]+\z/
|
899
986
|
arg, help = args.shift.split(/ /, 2)
|
900
987
|
options.push arg.sub(/\A--?/, '')
|
901
988
|
extras[:optional] = help[0, 1] == '[' && help[-1, 1] == ']'
|
902
989
|
extras[:help] = help
|
903
|
-
elsif
|
990
|
+
elsif not boolean and long.to_s =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\z/
|
904
991
|
options.push args.shift.to_s.sub(/\A--?/, '')
|
905
992
|
else
|
906
993
|
options.push nil
|
data/slop.gemspec
CHANGED
data/test/slop_test.rb
CHANGED
@@ -112,6 +112,18 @@ class SlopTest < TestCase
|
|
112
112
|
assert_equal 'Print this help message', slop.options[:help].description
|
113
113
|
end
|
114
114
|
|
115
|
+
test ':all_accept_arguments' do
|
116
|
+
opts = Slop.new(:all_accept_arguments) do
|
117
|
+
on :foo
|
118
|
+
on :bar, :optional => true
|
119
|
+
end
|
120
|
+
opts.parse %w[ --foo hello --bar ]
|
121
|
+
|
122
|
+
assert_equal 'hello', opts[:foo]
|
123
|
+
assert_nil opts[:bar]
|
124
|
+
assert_raises(Slop::MissingArgumentError) { opts.parse %w[ --foo --bar ] }
|
125
|
+
end
|
126
|
+
|
115
127
|
test 'yielding non-options when a block is passed to "parse"' do
|
116
128
|
opts = Slop.new do
|
117
129
|
on :name, true
|
@@ -300,6 +312,7 @@ class SlopTest < TestCase
|
|
300
312
|
|
301
313
|
assert opts.present?('foo-bar')
|
302
314
|
refute opts.present?('bar-baz')
|
315
|
+
refute opts.present?('foo-bar', 'bar-baz')
|
303
316
|
assert opts.present?(:h)
|
304
317
|
end
|
305
318
|
|
@@ -517,4 +530,26 @@ class SlopTest < TestCase
|
|
517
530
|
" labore et dolore magna aliqua.",
|
518
531
|
slop.send(:wrap_and_indent, "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 36, 4))
|
519
532
|
end
|
533
|
+
|
534
|
+
test 'to_struct' do
|
535
|
+
assert_nil Slop.new.to_struct
|
536
|
+
|
537
|
+
slop = Slop.new { on :a, true }
|
538
|
+
slop.parse %w[ -a foo -b ]
|
539
|
+
struct = slop.to_struct
|
540
|
+
|
541
|
+
assert_equal 'foo', struct.a
|
542
|
+
assert_kind_of Struct, struct
|
543
|
+
assert_raises(NoMethodError) { struct.b }
|
544
|
+
|
545
|
+
pstruct = slop.to_struct('Foo')
|
546
|
+
assert_kind_of Struct::Foo, pstruct
|
547
|
+
end
|
548
|
+
|
549
|
+
test 'returning missing options' do
|
550
|
+
slop = Slop.new { on :a; on :b, :bar; on :c; }
|
551
|
+
slop.parse %w[ -a ]
|
552
|
+
|
553
|
+
assert_equal %w[ bar c ], slop.missing
|
554
|
+
end
|
520
555
|
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: 2.
|
4
|
+
version: 2.1.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: 2011-
|
12
|
+
date: 2011-08-03 00:00:00.000000000 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
description: A simple DSL for gathering options and parsing the command line
|