slop 1.0.0.rc1 → 1.0.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/.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:
|