slop 0.2.0 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/.gitignore +4 -0
- data/.yardopts +1 -0
- data/README.md +87 -67
- data/Rakefile +7 -0
- data/lib/slop.rb +105 -162
- data/lib/slop/option.rb +70 -152
- data/lib/slop/version.rb +3 -0
- data/slop.gemspec +17 -0
- data/test/helper.rb +14 -0
- data/test/option_test.rb +80 -0
- data/test/slop_test.rb +113 -0
- metadata +23 -38
- data/spec/option_spec.rb +0 -186
- data/spec/slop_spec.rb +0 -204
data/lib/slop/option.rb
CHANGED
@@ -1,190 +1,108 @@
|
|
1
1
|
class Slop
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# @return [Symbol,#to_s]
|
8
|
-
attr_reader :option
|
9
|
-
|
10
|
-
# @return [String]
|
11
|
-
attr_reader :description
|
12
|
-
|
13
|
-
# @return [Object]
|
14
|
-
attr_reader :default
|
15
|
-
|
16
|
-
# @return [Proc]
|
17
|
-
attr_reader :callback
|
18
|
-
|
19
|
-
# The maximum size of any --option, recorded for
|
20
|
-
# padding out any output string with smaller option lengths
|
21
|
-
# with the correct amount of whitespace
|
22
|
-
#
|
23
|
-
# @return [Fixnum]
|
24
|
-
@@max_option_size = 0
|
25
|
-
|
26
|
-
# @param [Hash] options Option attributes
|
27
|
-
# @option options [Symbol,#to_s] :flag
|
28
|
-
# @option options [Symbol,#to_s] :option
|
29
|
-
# @option options [Symbol,#to_s] :description
|
30
|
-
# @option options [Boolean] :argument
|
31
|
-
# @option options [Boolean] :optional
|
32
|
-
# @option options [Object] :default
|
33
|
-
# @option options [Object] :as
|
34
|
-
# @option options [Proc] :callback
|
35
|
-
# @option options [String,#to_s] :delimiter
|
36
|
-
# @option options [Integer] :limit
|
37
|
-
def initialize(options={}, &blk)
|
38
|
-
@options = options
|
39
|
-
@flag = options[:flag]
|
40
|
-
@option = options[:option] || options[:opt]
|
41
|
-
@description = options[:description] || options[:desc]
|
42
|
-
@argument = options[:argument] || false
|
43
|
-
@optional = options[:optional] || options[:optional_argument]
|
44
|
-
@argument ||= @optional
|
45
|
-
@default = options[:default]
|
46
|
-
@as = options[:as]
|
47
|
-
@callback = options[:callback]
|
48
|
-
|
49
|
-
# Array properties
|
50
|
-
@delimiter = options[:delimiter] || ','
|
51
|
-
@limit = options[:limit] || 0
|
52
|
-
|
53
|
-
@argument_value = nil
|
54
|
-
|
55
|
-
if @option
|
56
|
-
if requires_argument?
|
57
|
-
size = (@option.size * 2) + 4
|
58
|
-
else
|
59
|
-
size = @option.size + 2
|
60
|
-
end
|
61
|
-
|
62
|
-
@@max_option_size = size if @@max_option_size < size
|
2
|
+
class Options < Array
|
3
|
+
def to_hash
|
4
|
+
each_with_object({}) do |option, out|
|
5
|
+
out[option.key] = option.argument_value
|
63
6
|
end
|
64
7
|
end
|
65
8
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
# @return [Object] the argument value after it's been cast
|
73
|
-
# according to the `:as` option
|
74
|
-
def argument_value
|
75
|
-
@argument_value ||= @default
|
76
|
-
return unless @argument_value
|
77
|
-
|
78
|
-
case @as.to_s.downcase[0, 3]
|
79
|
-
when 'arr'; @argument_value.split(@delimiter, @limit)
|
80
|
-
when 'int'; @argument_value.to_i
|
81
|
-
when 'sym'; @argument_value.to_sym
|
9
|
+
def [](item)
|
10
|
+
item = item.to_s
|
11
|
+
if item =~ /^\d+/
|
12
|
+
super
|
82
13
|
else
|
83
|
-
|
14
|
+
find do |option|
|
15
|
+
option.short_flag == item || option.long_flag == item
|
16
|
+
end
|
84
17
|
end
|
85
18
|
end
|
19
|
+
end
|
86
20
|
|
87
|
-
|
88
|
-
# @return [Boolean] true if this option contains a flag
|
89
|
-
def has_flag?(flag)
|
90
|
-
@flag.to_s == flag.to_s
|
91
|
-
end
|
21
|
+
class Option
|
92
22
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
23
|
+
attr_reader :short_flag
|
24
|
+
attr_reader :long_flag
|
25
|
+
attr_reader :description
|
26
|
+
attr_reader :callback
|
27
|
+
attr_writer :argument_value
|
98
28
|
|
99
|
-
|
100
|
-
def has_default?
|
101
|
-
!@default.nil?
|
102
|
-
end
|
29
|
+
@@longest_flag = 0
|
103
30
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
31
|
+
def initialize(short, long, description, argument, options={}, &block)
|
32
|
+
@short_flag, @long_flag = short, long
|
33
|
+
@description, @expects_argument = description, argument
|
34
|
+
@options = options
|
108
35
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
end
|
36
|
+
if @long_flag && @long_flag.size > @@longest_flag
|
37
|
+
@@longest_flag = @long_flag.size
|
38
|
+
end
|
113
39
|
|
114
|
-
|
115
|
-
|
116
|
-
@
|
40
|
+
@callback = block if block_given?
|
41
|
+
@callback ||= options[:callback]
|
42
|
+
@argument_value = nil
|
117
43
|
end
|
118
44
|
|
119
|
-
|
120
|
-
|
121
|
-
def requires_argument?
|
122
|
-
!!@argument
|
45
|
+
def expects_argument?
|
46
|
+
@expects_argument || @options[:argument]
|
123
47
|
end
|
124
48
|
|
125
|
-
|
126
|
-
# @return [Boolean]
|
127
|
-
def optional_argument?
|
49
|
+
def accepts_optional_argument?
|
128
50
|
@options[:optional]
|
129
51
|
end
|
130
52
|
|
131
|
-
|
132
|
-
|
133
|
-
@options[key]
|
134
|
-
end
|
135
|
-
|
136
|
-
# Replace options argument value with the switch value supplied, used
|
137
|
-
# when supplying the `switch` option making switch flags easy to alter
|
138
|
-
#
|
139
|
-
# @example
|
140
|
-
# option :v, :verbose, :default => false, :switch => true
|
141
|
-
#
|
142
|
-
# Now when the `-v` or `--verbose` option is supplied, verbose will
|
143
|
-
# be set to `true`, rather than the default `false` option
|
144
|
-
def switch_argument_value
|
145
|
-
@argument_value = @options[:switch]
|
53
|
+
def has_callback?
|
54
|
+
!!@callback && @callback.respond_to?(:call)
|
146
55
|
end
|
147
56
|
|
148
|
-
# return a key for an option, prioritize
|
149
|
-
# option before flag as it's more descriptive
|
150
57
|
def key
|
151
|
-
@
|
58
|
+
@long_flag || @short_flag
|
152
59
|
end
|
153
60
|
|
154
|
-
def
|
155
|
-
|
61
|
+
def default
|
62
|
+
@options[:default]
|
63
|
+
end
|
156
64
|
|
157
|
-
|
158
|
-
|
65
|
+
def argument_value
|
66
|
+
value = @argument_value || default
|
67
|
+
return unless value
|
68
|
+
|
69
|
+
case @options[:as].to_s
|
70
|
+
when 'Array'
|
71
|
+
value.split(@options[:delimiter] || ',', @options[:limit] || 0)
|
72
|
+
when 'String'; value.to_s
|
73
|
+
when 'Symbol'; value.to_s.to_sym
|
74
|
+
when 'Integer'; value.to_i
|
75
|
+
when 'Float'; value.to_f
|
159
76
|
else
|
160
|
-
|
77
|
+
value
|
161
78
|
end
|
79
|
+
end
|
162
80
|
|
163
|
-
|
164
|
-
|
165
|
-
|
81
|
+
def to_s
|
82
|
+
out = " "
|
83
|
+
out += @short_flag ? "-#{@short_flag}, " : ' ' * 4
|
166
84
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
end
|
174
|
-
size_diff = @@max_option_size - ((@option.size * 2) + 4)
|
175
|
-
optionstr << " " * size_diff if size_diff > 0
|
176
|
-
str << optionstr
|
85
|
+
out += "--#{@long_flag}" if @long_flag
|
86
|
+
|
87
|
+
if @long_flag
|
88
|
+
diff = @@longest_flag - @long_flag.size
|
89
|
+
spaces = " " * (diff + 6)
|
90
|
+
out += spaces
|
177
91
|
else
|
178
|
-
|
92
|
+
spaces = " " * (@@longest_flag + 8)
|
93
|
+
out += spaces
|
179
94
|
end
|
180
95
|
|
181
|
-
|
182
|
-
|
96
|
+
out += @description if @description
|
97
|
+
|
98
|
+
out
|
183
99
|
end
|
184
100
|
|
185
101
|
def inspect
|
186
|
-
"
|
102
|
+
"#<Slop::Option short_flag=#{@short_flag.inspect} " +
|
103
|
+
"long_flag=#{@long_flag.inspect} " +
|
104
|
+
"description=#{@description.inspect} "
|
187
105
|
end
|
188
|
-
|
189
106
|
end
|
190
|
-
|
107
|
+
|
108
|
+
end
|
data/lib/slop/version.rb
ADDED
data/slop.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'slop/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'slop'
|
6
|
+
s.version = Slop::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.summary = 'Option gathering made easy'
|
9
|
+
s.description = 'A simple DSL for gathering options and parsing the command line'
|
10
|
+
s.author = 'Lee Jarvis'
|
11
|
+
s.email = 'lee@jarvis.co'
|
12
|
+
s.homepage = 'http://github.com/injekt/slop'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class TestCase < MiniTest::Unit::TestCase
|
2
|
+
def self.test(name, &block)
|
3
|
+
test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
|
4
|
+
defined = instance_method(test_name) rescue false
|
5
|
+
raise "#{test_name} is already defined in #{self}" if defined
|
6
|
+
if block_given?
|
7
|
+
define_method(test_name, &block)
|
8
|
+
else
|
9
|
+
define_method(test_name) do
|
10
|
+
flunk "No implementation provided for #{name}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/test/option_test.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class OptionTest < TestCase
|
4
|
+
def option(*args, &block)
|
5
|
+
Slop.new.option(*args, &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def option_with_argument(*args, &block)
|
9
|
+
options = args.shift
|
10
|
+
slop = Slop.new
|
11
|
+
option = slop.opt(*args)
|
12
|
+
slop.parse(options)
|
13
|
+
slop.find {|opt| opt.key == option.key }
|
14
|
+
end
|
15
|
+
|
16
|
+
def option_value(*args, &block)
|
17
|
+
option_with_argument(*args, &block).argument_value
|
18
|
+
end
|
19
|
+
|
20
|
+
test 'expects an argument if argument is true' do
|
21
|
+
assert option(:f, :foo, 'foo', true).expects_argument?
|
22
|
+
assert option(:f, :argument => true).expects_argument?
|
23
|
+
|
24
|
+
refute option(:f, :foo).expects_argument?
|
25
|
+
end
|
26
|
+
|
27
|
+
test 'accepts an optional argument if optional is true' do
|
28
|
+
assert option(:f, :optional => true).accepts_optional_argument?
|
29
|
+
assert option(:f, false, :optional => true).accepts_optional_argument?
|
30
|
+
|
31
|
+
refute option(:f, true).accepts_optional_argument?
|
32
|
+
end
|
33
|
+
|
34
|
+
test 'has a callback when passed a block or callback option' do
|
35
|
+
assert option(:f){}.has_callback?
|
36
|
+
assert option(:callback => proc {}).has_callback?
|
37
|
+
|
38
|
+
refute option(:f).has_callback?
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'splits argument_value with :as => array' do
|
42
|
+
assert_equal %w/lee john bill/, option_value(
|
43
|
+
%w/--people lee,john,bill/, :people, true, :as => Array
|
44
|
+
)
|
45
|
+
|
46
|
+
assert_equal %w/lee john bill/, option_value(
|
47
|
+
%w/--people lee:john:bill/,
|
48
|
+
:people, true, :as => Array, :delimiter => ':'
|
49
|
+
)
|
50
|
+
|
51
|
+
assert_equal ['lee', 'john,bill'], option_value(
|
52
|
+
%w/--people lee,john,bill/,
|
53
|
+
:people, true, :as => Array, :limit => 2
|
54
|
+
)
|
55
|
+
|
56
|
+
assert_equal ['lee', 'john:bill'], option_value(
|
57
|
+
%w/--people lee:john:bill/,
|
58
|
+
:people, true, :as => Array, :limit => 2, :delimiter => ':'
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
test 'returns a symbol with :as => Symbol' do
|
63
|
+
assert_equal :foo, option_value(%w/--name foo/, :name, true, :as => Symbol)
|
64
|
+
end
|
65
|
+
|
66
|
+
test 'returns an integer with :as => Integer' do
|
67
|
+
assert_equal 30, option_value(%w/--age 30/, :age, true, :as => Integer)
|
68
|
+
end
|
69
|
+
|
70
|
+
test 'printing options' do
|
71
|
+
slop = Slop.new
|
72
|
+
slop.opt :n, :name, 'Your name', true
|
73
|
+
slop.opt :age, 'Your age', true
|
74
|
+
slop.opt :V, 'Display the version'
|
75
|
+
|
76
|
+
assert_equal " -n, --name Your name", slop.options[:name].to_s
|
77
|
+
assert_equal " --age Your age", slop.options[:age].to_s
|
78
|
+
assert_equal " -V, Display the version", slop.options[:V].to_s
|
79
|
+
end
|
80
|
+
end
|
data/test/slop_test.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class SlopTest < TestCase
|
4
|
+
def clean_options(*args)
|
5
|
+
Slop.new.send(:clean_options, args)
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse(items, &block)
|
9
|
+
Slop.parse(items, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
test 'includes Enumerable' do
|
13
|
+
assert Slop.included_modules.include?(Enumerable)
|
14
|
+
end
|
15
|
+
|
16
|
+
test 'parse returns a Slop object' do
|
17
|
+
slop = Slop.parse([])
|
18
|
+
assert_kind_of Slop, slop
|
19
|
+
end
|
20
|
+
|
21
|
+
test 'enumerating options' do
|
22
|
+
slop = Slop.new
|
23
|
+
slop.opt(:f, :foo, 'foo')
|
24
|
+
slop.opt(:b, :bar, 'bar')
|
25
|
+
|
26
|
+
slop.each { |option| assert option }
|
27
|
+
end
|
28
|
+
|
29
|
+
test 'parsing options' do
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
test '#parse does not remove parsed items' do
|
34
|
+
items = %w/--foo/
|
35
|
+
Slop.new { |opt| opt.on :foo }.parse(items)
|
36
|
+
assert_equal %w/--foo/, items
|
37
|
+
end
|
38
|
+
|
39
|
+
test '#parse! removes parsed items' do
|
40
|
+
items = %w/--foo/
|
41
|
+
Slop.new { |opt| opt.on :foo }.parse!(items)
|
42
|
+
assert_empty items
|
43
|
+
end
|
44
|
+
|
45
|
+
test 'the shit out of clean_options' do
|
46
|
+
assert_equal(
|
47
|
+
['s', 'short', 'short option', false],
|
48
|
+
clean_options('-s', '--short', 'short option')
|
49
|
+
)
|
50
|
+
|
51
|
+
assert_equal(
|
52
|
+
[nil, 'long', 'long option only', true],
|
53
|
+
clean_options('--long', 'long option only', true)
|
54
|
+
)
|
55
|
+
|
56
|
+
assert_equal(
|
57
|
+
['S', 'symbol', 'symbolize', false],
|
58
|
+
clean_options(:S, :symbol, 'symbolize')
|
59
|
+
)
|
60
|
+
|
61
|
+
assert_equal(
|
62
|
+
['a', nil, 'alphabetical only', true],
|
63
|
+
clean_options('a', 'alphabetical only', true)
|
64
|
+
)
|
65
|
+
|
66
|
+
assert_equal( # for description-less options
|
67
|
+
[nil, 'optiononly', nil, false],
|
68
|
+
clean_options('--optiononly')
|
69
|
+
)
|
70
|
+
|
71
|
+
assert_equal(['c', nil, nil, true], clean_options(:c, true))
|
72
|
+
assert_equal(['c', nil, nil, false], clean_options(:c, false))
|
73
|
+
end
|
74
|
+
|
75
|
+
test '[] returns an options argument value or nil' do
|
76
|
+
slop = Slop.new
|
77
|
+
slop.opt :n, :name, true
|
78
|
+
slop.parse %w/--name lee/
|
79
|
+
|
80
|
+
assert_equal 'lee', slop[:name]
|
81
|
+
assert_equal 'lee', slop[:n]
|
82
|
+
end
|
83
|
+
|
84
|
+
test 'arguments ending ? test for option existance' do
|
85
|
+
slop = Slop.new
|
86
|
+
slop.opt :v, :verbose
|
87
|
+
slop.opt :d, :debug
|
88
|
+
slop.parse %w/--verbose/
|
89
|
+
|
90
|
+
assert slop[:verbose]
|
91
|
+
assert slop.verbose?
|
92
|
+
|
93
|
+
refute slop[:debug]
|
94
|
+
refute slop.debug?
|
95
|
+
end
|
96
|
+
|
97
|
+
test 'raises if an option expects an argument and none is given' do
|
98
|
+
slop = Slop.new
|
99
|
+
slop.opt :name, true
|
100
|
+
slop.opt :age, :optional => true
|
101
|
+
|
102
|
+
assert_raises(Slop::MissingArgumentError, /name/) { slop.parse %w/--name/ }
|
103
|
+
assert slop.parse %w/--name 'foo'/
|
104
|
+
end
|
105
|
+
|
106
|
+
test 'printing help' do
|
107
|
+
slop = Slop.new
|
108
|
+
slop.banner = 'Usage: foo [options]'
|
109
|
+
slop.parse
|
110
|
+
|
111
|
+
assert slop.to_s =~ /^Usage: foo/
|
112
|
+
end
|
113
|
+
end
|