slop 1.0.0.rc1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +5 -1
- data/README.md +43 -8
- data/Rakefile +3 -1
- data/lib/slop.rb +96 -17
- data/lib/slop/option.rb +21 -26
- data/lib/slop/version.rb +1 -1
- data/test/option_test.rb +17 -4
- data/test/slop_test.rb +44 -1
- metadata +4 -4
data/.yardopts
CHANGED
data/README.md
CHANGED
@@ -3,16 +3,12 @@ Slop
|
|
3
3
|
|
4
4
|
Slop is a simple option collector with an easy to remember syntax and friendly API.
|
5
5
|
|
6
|
-
**NOTE** This branch refers to the prerelease 1.0.0.rc1 version, this version has
|
7
|
-
incompatible changes from version 0.0.3 (the latest stable version). You **should**
|
8
|
-
upgrade.
|
9
|
-
|
10
6
|
Installation
|
11
7
|
------------
|
12
8
|
|
13
9
|
### Rubygems
|
14
10
|
|
15
|
-
gem install slop
|
11
|
+
gem install slop
|
16
12
|
|
17
13
|
### GitHub
|
18
14
|
|
@@ -26,10 +22,11 @@ Usage
|
|
26
22
|
opts = Slop.parse do
|
27
23
|
on :v, :verbose, 'Enable verbose mode' # boolean value
|
28
24
|
on :n, :name, 'Your name', true # compulsory argument
|
25
|
+
on :s, :sex, 'Your sex', :optional => false # the same thing
|
29
26
|
on :a, :age, 'Your age', :optional => true # optional argument
|
30
27
|
end
|
31
28
|
|
32
|
-
# if ARGV is `-v --name 'lee jarvis'`
|
29
|
+
# if ARGV is `-v --name 'lee jarvis' -s male`
|
33
30
|
opts.verbose? #=> true
|
34
31
|
opts.name? #=> true
|
35
32
|
opts[:name] #=> 'lee jarvis'
|
@@ -38,7 +35,7 @@ Usage
|
|
38
35
|
|
39
36
|
You can also return your options as a Hash
|
40
37
|
|
41
|
-
opts.to_hash #=> {
|
38
|
+
opts.to_hash #=> {'name' => 'Lee Jarvis', 'verbose' => true, 'age' => nil, 'sex' => 'male'}
|
42
39
|
|
43
40
|
If you don't like the method `on` (because it sounds like the option **expects**
|
44
41
|
a callback), you can use the `opt` or `option` alternatives.
|
@@ -134,7 +131,7 @@ do this:
|
|
134
131
|
You can also try other variations:
|
135
132
|
|
136
133
|
on :name, "Your name"
|
137
|
-
on :n, :name
|
134
|
+
on :n, :name
|
138
135
|
on :name, true
|
139
136
|
|
140
137
|
Lists
|
@@ -156,6 +153,44 @@ You can also change both the split delimiter and limit
|
|
156
153
|
end
|
157
154
|
opts[:people] #=> ["lee", "injekt:bob"]
|
158
155
|
|
156
|
+
Woah woah, why you hating on OptionParser?
|
157
|
+
------------------------------------------
|
158
|
+
|
159
|
+
I'm not, honestly! I love OptionParser. I really do, it's a fantastic library.
|
160
|
+
So why did I build Slop? Well, I find myself using OptionParser to simple
|
161
|
+
gather a bunch of key/value options, usually you would do something like this:
|
162
|
+
|
163
|
+
require 'optparse'
|
164
|
+
|
165
|
+
things = {}
|
166
|
+
|
167
|
+
opt = OptionParser.new do |opt|
|
168
|
+
opt.on('-n', '--name NAME', 'Your name') do |name|
|
169
|
+
things[:name] = name
|
170
|
+
end
|
171
|
+
|
172
|
+
opt.on('-a', '--age AGE', 'Your age') do |age|
|
173
|
+
things[:age] = age
|
174
|
+
end
|
175
|
+
|
176
|
+
# you get the point
|
177
|
+
end
|
178
|
+
|
179
|
+
opt.parse
|
180
|
+
# do something with things
|
181
|
+
|
182
|
+
Which is all great and stuff, but it can lead to some repetition, the same
|
183
|
+
thing in Slop:
|
184
|
+
|
185
|
+
require 'slop'
|
186
|
+
|
187
|
+
opts = Slop.parse do
|
188
|
+
on :n, :name, 'Your name', true
|
189
|
+
on :a, :age, 'Your age', true
|
190
|
+
end
|
191
|
+
|
192
|
+
things = opts.to_hash
|
193
|
+
|
159
194
|
Contributing
|
160
195
|
------------
|
161
196
|
|
data/Rakefile
CHANGED
data/lib/slop.rb
CHANGED
@@ -6,6 +6,21 @@ class Slop
|
|
6
6
|
|
7
7
|
class MissingArgumentError < ArgumentError; end
|
8
8
|
|
9
|
+
# Parses the items from a CLI format into a friendly object.
|
10
|
+
#
|
11
|
+
# @param [Array] items Items to parse into options.
|
12
|
+
# @yield Specify available CLI arguments using Slop# methods such as Slop#banner and Slop#option
|
13
|
+
# @return [Slop] Returns an instance of Slop.
|
14
|
+
# @example Specifying three options to parse:
|
15
|
+
# opts = Slops.parse do
|
16
|
+
# on :v, :verbose, 'Enable verbose mode'
|
17
|
+
# on :n, :name, 'Your name'
|
18
|
+
# on :a, :age, 'Your age'
|
19
|
+
# end
|
20
|
+
# -------
|
21
|
+
# program.rb --verbose -n 'Emily' -a 25
|
22
|
+
# @see Slop#banner
|
23
|
+
# @see Slop#option
|
9
24
|
def self.parse(items=ARGV, &block)
|
10
25
|
slop = new(&block)
|
11
26
|
slop.parse(items)
|
@@ -14,24 +29,42 @@ class Slop
|
|
14
29
|
|
15
30
|
attr_reader :options
|
16
31
|
attr_writer :banner
|
32
|
+
attr_accessor :longest_flag
|
17
33
|
|
18
34
|
def initialize(&block)
|
19
35
|
@options = Options.new
|
20
36
|
@banner = nil
|
37
|
+
@longest_flag = 0
|
38
|
+
|
21
39
|
if block_given?
|
22
40
|
block.arity == 1 ? yield(self) : instance_eval(&block)
|
23
41
|
end
|
24
42
|
end
|
25
43
|
|
44
|
+
# Set or return banner text.
|
45
|
+
#
|
46
|
+
# @param [String] text Displayed banner text.
|
47
|
+
# @return [String] Returns current banner.
|
48
|
+
# @example
|
49
|
+
# opts = Slop.parse do
|
50
|
+
# banner "Usage - ruby foo.rb [arguments]"
|
51
|
+
# end
|
52
|
+
# @see Slop#to_s
|
26
53
|
def banner(text=nil)
|
27
54
|
@banner = text if text
|
28
55
|
@banner
|
29
56
|
end
|
30
57
|
|
58
|
+
# Parse a list of options, leaving the original Array unchanged.
|
59
|
+
#
|
60
|
+
# @param items
|
31
61
|
def parse(items=ARGV)
|
32
62
|
parse_items(items)
|
33
63
|
end
|
34
64
|
|
65
|
+
# Parse a list of options, removing parsed options from the original Array.
|
66
|
+
#
|
67
|
+
# @parse items
|
35
68
|
def parse!(items=ARGV)
|
36
69
|
parse_items(items, true)
|
37
70
|
end
|
@@ -42,20 +75,43 @@ class Slop
|
|
42
75
|
@options.each { |option| yield option }
|
43
76
|
end
|
44
77
|
|
78
|
+
# Return the value of an option via the subscript operator.
|
79
|
+
#
|
80
|
+
# @param [Symbol] key Option symbol.
|
81
|
+
# @return [Object] Returns the object associated with that option.
|
82
|
+
# @example
|
83
|
+
# opts[:name]
|
84
|
+
# #=> "Emily"
|
85
|
+
# @see Slop#method_missing
|
45
86
|
def [](key)
|
46
87
|
option = @options[key]
|
47
88
|
option ? option.argument_value : nil
|
48
89
|
end
|
49
90
|
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
# :
|
91
|
+
# Specify an option with a short or long version, description and type.
|
92
|
+
#
|
93
|
+
# @param [*] args Option configuration.
|
94
|
+
# @option args [Symbol, String] :short_flag Short option name.
|
95
|
+
# @option args [Symbol, String] :long_flag Full option name.
|
96
|
+
# @option args [String] :description Option description for use in Slop#help
|
97
|
+
# @option args [Boolean] :argument Specifies whether a required option or not.
|
98
|
+
# @option args [Hash] :options Optional option configurations.
|
99
|
+
# @example
|
100
|
+
# opts = Slop.parse do
|
101
|
+
# on :n, :name, 'Your username', true # Required argument
|
102
|
+
# on :a, :age, 'Your age (optional)', :optional => true # Optional argument
|
103
|
+
# on :g, :gender, 'Your gender', :optional => false # Required argument
|
104
|
+
# on :V, :verbose, 'Run in verbose mode', :default => true # Runs verbose mode by default
|
105
|
+
# on :P, :people, 'Your friends', true, :as => Array # Required, list of people.
|
106
|
+
# on :h, :help, 'Print this help screen' do
|
107
|
+
# puts help
|
108
|
+
# end # Runs a block
|
109
|
+
# end
|
54
110
|
def option(*args, &block)
|
55
111
|
options = args.pop if args.last.is_a?(Hash)
|
56
112
|
options ||= {}
|
57
113
|
|
58
|
-
option = Option.new(*clean_options(args), options, &block)
|
114
|
+
option = Option.new(self, *clean_options(args), options, &block)
|
59
115
|
@options << option
|
60
116
|
|
61
117
|
option
|
@@ -63,10 +119,26 @@ class Slop
|
|
63
119
|
alias :opt :option
|
64
120
|
alias :on :option
|
65
121
|
|
122
|
+
# Returns the parsed list into a option/value hash.
|
123
|
+
#
|
124
|
+
# @return [Hash] Returns a hash with each specified option as a symbolic key with an associated value.
|
125
|
+
# @example
|
126
|
+
# opts.to_hash
|
127
|
+
# #=> { :name => 'Emily' }
|
66
128
|
def to_hash
|
67
129
|
@options.to_hash
|
68
130
|
end
|
69
131
|
|
132
|
+
# Allows you to check whether an option was specified in the parsed list.
|
133
|
+
#
|
134
|
+
# @return [Boolean] Whether the desired option was specified.
|
135
|
+
# @example
|
136
|
+
# #== ruby foo.rb -v
|
137
|
+
# opts.verbose?
|
138
|
+
# #=> true
|
139
|
+
# opts.name?
|
140
|
+
# #=> false
|
141
|
+
# @see Slop#[]
|
70
142
|
def method_missing(meth, *args, &block)
|
71
143
|
if meth.to_s =~ /\?$/
|
72
144
|
!!self[meth.to_s.chomp('?')]
|
@@ -75,6 +147,17 @@ class Slop
|
|
75
147
|
end
|
76
148
|
end
|
77
149
|
|
150
|
+
# Returns the banner followed by available options listed on the next line.
|
151
|
+
#
|
152
|
+
# @return [String] Help text.
|
153
|
+
# @example
|
154
|
+
# opts = Slop.parse do
|
155
|
+
# banner "Usage - ruby foo.rb [arguments]"
|
156
|
+
# on :v, :verbose, "Enable verbose mode"
|
157
|
+
# end
|
158
|
+
# opts.to_s
|
159
|
+
# #=> "Usage - ruby foo.rb [options]\n -v, --verbose Enable verbose mode"
|
160
|
+
# @see Slop#banner
|
78
161
|
def to_s
|
79
162
|
banner = "#{@banner}\n" if @banner
|
80
163
|
(banner || '') + options.map(&:to_s).join("\n")
|
@@ -87,38 +170,34 @@ private
|
|
87
170
|
trash = []
|
88
171
|
|
89
172
|
items.each do |item|
|
90
|
-
|
91
173
|
flag = item.to_s.sub(/^--?/, '')
|
92
|
-
if flag.length == 1
|
93
|
-
option = find { |option| option.short_flag == flag }
|
94
|
-
else
|
95
|
-
option = find { |option| option.long_flag == flag }
|
96
|
-
end
|
97
174
|
|
98
|
-
if option
|
175
|
+
if option = @options[flag]
|
176
|
+
trash << item if delete
|
99
177
|
option.argument_value = true
|
100
178
|
|
101
179
|
if option.expects_argument? || option.accepts_optional_argument?
|
102
180
|
argument = items.at(items.index(item) + 1)
|
103
|
-
trash << argument if delete
|
181
|
+
trash << argument if delete
|
104
182
|
|
105
183
|
if argument
|
106
184
|
option.argument_value = argument
|
107
|
-
option.callback.call(option.argument_value) if option.
|
185
|
+
option.callback.call(option.argument_value) if option.callback
|
108
186
|
else
|
187
|
+
option.argument_value = nil
|
109
188
|
if option.accepts_optional_argument?
|
110
|
-
option.callback.call(nil) if option.
|
189
|
+
option.callback.call(nil) if option.callback
|
111
190
|
else
|
112
191
|
raise MissingArgumentError,
|
113
192
|
"'#{flag}' expects an argument, none given"
|
114
193
|
end
|
115
194
|
end
|
116
|
-
elsif option.
|
195
|
+
elsif option.callback
|
117
196
|
option.callback.call(nil)
|
118
197
|
end
|
119
|
-
trash << item if delete
|
120
198
|
end
|
121
199
|
end
|
200
|
+
|
122
201
|
items.delete_if { |item| trash.include? item }
|
123
202
|
end
|
124
203
|
|
data/lib/slop/option.rb
CHANGED
@@ -8,8 +8,8 @@ class Slop
|
|
8
8
|
|
9
9
|
def [](item)
|
10
10
|
item = item.to_s
|
11
|
-
if item =~ /^\d
|
12
|
-
|
11
|
+
if item =~ /^\d+$/
|
12
|
+
slice(item.to_i)
|
13
13
|
else
|
14
14
|
find do |option|
|
15
15
|
option.short_flag == item || option.long_flag == item
|
@@ -26,18 +26,20 @@ class Slop
|
|
26
26
|
attr_reader :callback
|
27
27
|
attr_writer :argument_value
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@
|
33
|
-
@description
|
29
|
+
def initialize(slop, short, long, description, argument, options={}, &blk)
|
30
|
+
@slop = slop
|
31
|
+
@short_flag = short
|
32
|
+
@long_flag = long
|
33
|
+
@description = description
|
34
34
|
@options = options
|
35
|
+
@expects_argument = argument
|
36
|
+
@expects_argument = true if options[:optional] == false
|
35
37
|
|
36
|
-
if @long_flag && @long_flag.size >
|
37
|
-
|
38
|
+
if @long_flag && @long_flag.size > @slop.longest_flag
|
39
|
+
@slop.longest_flag = @long_flag.size
|
38
40
|
end
|
39
41
|
|
40
|
-
@callback =
|
42
|
+
@callback = blk if block_given?
|
41
43
|
@callback ||= options[:callback]
|
42
44
|
@argument_value = nil
|
43
45
|
end
|
@@ -50,10 +52,6 @@ class Slop
|
|
50
52
|
@options[:optional]
|
51
53
|
end
|
52
54
|
|
53
|
-
def has_callback?
|
54
|
-
!!@callback && @callback.respond_to?(:call)
|
55
|
-
end
|
56
|
-
|
57
55
|
def key
|
58
56
|
@long_flag || @short_flag
|
59
57
|
end
|
@@ -64,15 +62,15 @@ class Slop
|
|
64
62
|
|
65
63
|
def argument_value
|
66
64
|
value = @argument_value || default
|
67
|
-
return
|
65
|
+
return if value.nil?
|
68
66
|
|
69
67
|
case @options[:as].to_s
|
70
68
|
when 'Array'
|
71
69
|
value.split(@options[:delimiter] || ',', @options[:limit] || 0)
|
72
70
|
when 'String'; value.to_s
|
73
71
|
when 'Symbol'; value.to_s.to_sym
|
74
|
-
when 'Integer'; value.to_i
|
75
|
-
when 'Float'; value.to_f
|
72
|
+
when 'Integer'; value.to_s.to_i
|
73
|
+
when 'Float'; value.to_s.to_f
|
76
74
|
else
|
77
75
|
value
|
78
76
|
end
|
@@ -80,28 +78,25 @@ class Slop
|
|
80
78
|
|
81
79
|
def to_s
|
82
80
|
out = " "
|
83
|
-
out += @short_flag ?
|
84
|
-
|
85
|
-
out += "--#{@long_flag}" if @long_flag
|
81
|
+
out += @short_flag ? "-#{@short_flag}, " : ' ' * 4
|
86
82
|
|
87
83
|
if @long_flag
|
88
|
-
|
84
|
+
out += "--#{@long_flag}"
|
85
|
+
diff = @slop.longest_flag - @long_flag.size
|
89
86
|
spaces = " " * (diff + 6)
|
90
87
|
out += spaces
|
91
88
|
else
|
92
|
-
spaces = " " * (
|
89
|
+
spaces = " " * (@slop.longest_flag + 8)
|
93
90
|
out += spaces
|
94
91
|
end
|
95
92
|
|
96
|
-
out
|
97
|
-
|
98
|
-
out
|
93
|
+
"#{out}#{@description}"
|
99
94
|
end
|
100
95
|
|
101
96
|
def inspect
|
102
97
|
"#<Slop::Option short_flag=#{@short_flag.inspect} " +
|
103
98
|
"long_flag=#{@long_flag.inspect} " +
|
104
|
-
"description=#{@description.inspect}
|
99
|
+
"description=#{@description.inspect}>"
|
105
100
|
end
|
106
101
|
end
|
107
102
|
|
data/lib/slop/version.rb
CHANGED
data/test/option_test.rb
CHANGED
@@ -17,9 +17,10 @@ class OptionTest < TestCase
|
|
17
17
|
option_with_argument(*args, &block).argument_value
|
18
18
|
end
|
19
19
|
|
20
|
-
test 'expects an argument if argument is true' do
|
20
|
+
test 'expects an argument if argument is true or optional is false' do
|
21
21
|
assert option(:f, :foo, 'foo', true).expects_argument?
|
22
22
|
assert option(:f, :argument => true).expects_argument?
|
23
|
+
assert option(:f, :optional => false).expects_argument?
|
23
24
|
|
24
25
|
refute option(:f, :foo).expects_argument?
|
25
26
|
end
|
@@ -32,10 +33,10 @@ class OptionTest < TestCase
|
|
32
33
|
end
|
33
34
|
|
34
35
|
test 'has a callback when passed a block or callback option' do
|
35
|
-
assert option(:f){}.
|
36
|
-
assert option(:callback => proc {}).
|
36
|
+
assert option(:f){}.callback
|
37
|
+
assert option(:callback => proc {}).callback
|
37
38
|
|
38
|
-
refute option(:f).
|
39
|
+
refute option(:f).callback
|
39
40
|
end
|
40
41
|
|
41
42
|
test 'splits argument_value with :as => array' do
|
@@ -77,4 +78,16 @@ class OptionTest < TestCase
|
|
77
78
|
assert_equal " --age Your age", slop.options[:age].to_s
|
78
79
|
assert_equal " -V, Display the version", slop.options[:V].to_s
|
79
80
|
end
|
81
|
+
|
82
|
+
test 'falls back to default option' do
|
83
|
+
slop = Slop.new
|
84
|
+
slop.opt :foo, :optional => true, :default => 'lee'
|
85
|
+
slop.parse %w/--foo/
|
86
|
+
assert_equal 'lee', slop[:foo]
|
87
|
+
end
|
88
|
+
|
89
|
+
test 'key should default to long flag otherwise use short flag' do
|
90
|
+
assert_equal 'foo', option(:f, :foo).key
|
91
|
+
assert_equal 'b', option(:b).key
|
92
|
+
end
|
80
93
|
end
|
data/test/slop_test.rb
CHANGED
@@ -26,8 +26,22 @@ class SlopTest < TestCase
|
|
26
26
|
slop.each { |option| assert option }
|
27
27
|
end
|
28
28
|
|
29
|
-
test '
|
29
|
+
test 'passing a block' do
|
30
|
+
assert Slop.new {}
|
31
|
+
slop = nil
|
32
|
+
assert Slop.new {|s| slop = s }
|
33
|
+
assert_kind_of Slop, slop
|
34
|
+
end
|
35
|
+
|
36
|
+
test 'setting the banner' do
|
37
|
+
slop = Slop.new
|
38
|
+
slop.banner = "foo bar"
|
39
|
+
|
40
|
+
assert_equal "foo bar", slop.banner
|
41
|
+
assert slop.to_s =~ /^foo bar/
|
30
42
|
|
43
|
+
slop.banner = nil
|
44
|
+
assert_equal "", slop.to_s
|
31
45
|
end
|
32
46
|
|
33
47
|
test '#parse does not remove parsed items' do
|
@@ -103,6 +117,35 @@ class SlopTest < TestCase
|
|
103
117
|
assert slop.parse %w/--name 'foo'/
|
104
118
|
end
|
105
119
|
|
120
|
+
test 'returning a hash of options' do
|
121
|
+
slop = Slop.new
|
122
|
+
slop.opt :name, true
|
123
|
+
slop.opt :version
|
124
|
+
slop.opt :V, :verbose, :default => false
|
125
|
+
slop.parse %w/--name lee --version/
|
126
|
+
|
127
|
+
assert_equal({'name' => 'lee', 'version' => true, 'verbose' => false}, slop.to_hash)
|
128
|
+
end
|
129
|
+
|
130
|
+
test 'iterating options' do
|
131
|
+
slop = Slop.new
|
132
|
+
slop.opt :a, :abc
|
133
|
+
slop.opt :f, :foo
|
134
|
+
|
135
|
+
assert_equal 2, slop.count
|
136
|
+
slop.each {|opt| assert_kind_of Slop::Option, opt }
|
137
|
+
end
|
138
|
+
|
139
|
+
test 'fetching options and option values' do
|
140
|
+
slop = Slop.new
|
141
|
+
slop.opt :foo, true
|
142
|
+
slop.parse %w/--foo bar/
|
143
|
+
|
144
|
+
assert_kind_of Slop::Option, slop.options[:foo]
|
145
|
+
assert_equal "bar", slop[:foo]
|
146
|
+
assert_equal "bar", slop['foo']
|
147
|
+
end
|
148
|
+
|
106
149
|
test 'printing help' do
|
107
150
|
slop = Slop.new
|
108
151
|
slop.banner = 'Usage: foo [options]'
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
version: 1.0.0
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Lee Jarvis
|
@@ -54,9 +54,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
54
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
55
|
none: false
|
56
56
|
requirements:
|
57
|
-
- - "
|
57
|
+
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version:
|
59
|
+
version: "0"
|
60
60
|
requirements: []
|
61
61
|
|
62
62
|
rubyforge_project:
|