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