slop 2.0.0 → 2.1.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.
- 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
|