clive 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +20 -0
- data/lib/clive/argument.rb +9 -2
- data/lib/clive/arguments.rb +43 -14
- data/lib/clive/arguments/parser.rb +6 -2
- data/lib/clive/option.rb +3 -3
- data/lib/clive/parser.rb +11 -5
- data/lib/clive/version.rb +1 -1
- data/spec/clive/a_cli_spec.rb +14 -12
- data/spec/clive/argument_spec.rb +44 -30
- data/spec/clive/arguments_spec.rb +115 -38
- data/spec/clive/parser_spec.rb +32 -0
- metadata +6 -6
data/README.md
CHANGED
@@ -189,6 +189,26 @@ opt :size, args: '[<h> <w>]'
|
|
189
189
|
# --size 10 #=> [10, nil]
|
190
190
|
```
|
191
191
|
|
192
|
+
You can make an argument 'infinite' by appending the name with `...`, like
|
193
|
+
`<arg>...`. Optional-ness is respected so `<arg>...` expects at least one argument
|
194
|
+
whereas `[<arg>...]` takes 0 or more arguments. Infinite arguments also play well
|
195
|
+
with standard arguments, and will return arrays of items even if only one argument
|
196
|
+
was passed.
|
197
|
+
|
198
|
+
``` ruby
|
199
|
+
opt :items, :arg => '<thing>...'
|
200
|
+
# --items #=> Error
|
201
|
+
# --items iPad #=> ['iPad']
|
202
|
+
# --items iPad iPod #=> ['iPad', 'iPod']
|
203
|
+
|
204
|
+
opt :items, :arg => '[<thing>...]'
|
205
|
+
# --items #=> []
|
206
|
+
# and same as previously in other cases
|
207
|
+
|
208
|
+
opt :items, :arg => '<amount> <thing>...', :as => [Integer, nil]
|
209
|
+
# --items 5 iPad iMac #=> [5, 'iPad', 'iMac']
|
210
|
+
```
|
211
|
+
|
192
212
|
There are also various options that can be passed to constrain or change the
|
193
213
|
arguments. If one of the below is passed to an option with `:arg` or `:args`
|
194
214
|
a generic argument called `<arg>` will be added.
|
data/lib/clive/argument.rb
CHANGED
@@ -34,7 +34,8 @@ class Clive
|
|
34
34
|
:match => AlwaysTrue.for(:match),
|
35
35
|
:within => AlwaysTrue.for(:include?),
|
36
36
|
:default => nil,
|
37
|
-
:constraint => AlwaysTrue.for(:call)
|
37
|
+
:constraint => AlwaysTrue.for(:call),
|
38
|
+
:infinite => false
|
38
39
|
}
|
39
40
|
|
40
41
|
attr_reader :name, :default, :type
|
@@ -91,6 +92,7 @@ class Clive
|
|
91
92
|
@within = opts[:within]
|
92
93
|
@default = opts[:default]
|
93
94
|
@constraint = opts[:constraint]
|
95
|
+
@infinite = opts[:infinite]
|
94
96
|
end
|
95
97
|
|
96
98
|
# @return Whether the argument is optional.
|
@@ -98,9 +100,14 @@ class Clive
|
|
98
100
|
@optional
|
99
101
|
end
|
100
102
|
|
103
|
+
# @return Whether the argument is infinite.
|
104
|
+
def infinite?
|
105
|
+
@infinite
|
106
|
+
end
|
107
|
+
|
101
108
|
# @return [String] String representation for the argument.
|
102
109
|
def to_s
|
103
|
-
optional? ? "[<#@name>]" : "<#@name>"
|
110
|
+
(optional? ? "[<#@name>]" : "<#@name>") + (infinite? ? '...' : '')
|
104
111
|
end
|
105
112
|
|
106
113
|
# @return [String]
|
data/lib/clive/arguments.rb
CHANGED
@@ -36,9 +36,8 @@ class Clive
|
|
36
36
|
other = other.dup.compact
|
37
37
|
# Find the number of 'spares'
|
38
38
|
diff = other.size - find_all {|i| !i.optional? }.size
|
39
|
-
r = []
|
40
39
|
|
41
|
-
map do |arg|
|
40
|
+
r = map do |arg|
|
42
41
|
if arg.possible?(other.first)
|
43
42
|
if arg.optional?
|
44
43
|
if diff > 0
|
@@ -53,6 +52,19 @@ class Clive
|
|
53
52
|
[arg, nil]
|
54
53
|
end
|
55
54
|
end
|
55
|
+
|
56
|
+
# If last arg is infinite may still have some left over so add now
|
57
|
+
if other != [] && last.respond_to?(:infinite?) && last.infinite?
|
58
|
+
r += other.map {|i| [last, i] }
|
59
|
+
end
|
60
|
+
|
61
|
+
if last.respond_to?(:infinite?) && last.infinite?
|
62
|
+
infinites = []
|
63
|
+
r = r.reject! {|i| i.first.infinite? ? infinites << i : false }
|
64
|
+
r << [infinites[0].first, infinites.map {|i| i.last }]
|
65
|
+
end
|
66
|
+
|
67
|
+
r
|
56
68
|
end
|
57
69
|
|
58
70
|
# @return [String]
|
@@ -67,7 +79,23 @@ class Clive
|
|
67
79
|
|
68
80
|
# @return [Integer] The maximum number of arguments that *can* be given.
|
69
81
|
def max
|
70
|
-
|
82
|
+
if last && last.infinite?
|
83
|
+
1.0/0.0
|
84
|
+
else
|
85
|
+
size
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# If the last item is infinite returns that item for any indexes greater
|
90
|
+
# than the actual length of the Arguments list.
|
91
|
+
#
|
92
|
+
# @param idx [Integer] Index of item to return
|
93
|
+
def [](idx)
|
94
|
+
if size <= idx && idx < max
|
95
|
+
last
|
96
|
+
else
|
97
|
+
super
|
98
|
+
end
|
71
99
|
end
|
72
100
|
|
73
101
|
# Whether the +list+ of found arguments could possibly be the arguments for
|
@@ -81,7 +109,7 @@ class Clive
|
|
81
109
|
optionals = []
|
82
110
|
|
83
111
|
list.each do |item|
|
84
|
-
break if i >=
|
112
|
+
break if i >= max
|
85
113
|
|
86
114
|
# Either, +item+ is self[i]
|
87
115
|
if self[i].possible?(item)
|
@@ -89,7 +117,7 @@ class Clive
|
|
89
117
|
|
90
118
|
# Or, the argument is optional and there is another argument to move to
|
91
119
|
# meaning it can be skipped
|
92
|
-
elsif self[i].optional? && (i <
|
120
|
+
elsif self[i].optional? && (i < max - 1)
|
93
121
|
i += 1
|
94
122
|
optionals << item
|
95
123
|
|
@@ -118,21 +146,22 @@ class Clive
|
|
118
146
|
#
|
119
147
|
# @param list [Array<Object>]
|
120
148
|
def valid?(list)
|
121
|
-
zip(list).map
|
122
|
-
|
123
|
-
nil
|
124
|
-
else
|
125
|
-
i
|
126
|
-
end
|
127
|
-
end.compact.size >= min && possible?(list)
|
149
|
+
l = zip(list).map {|a,i| a.optional? ? nil : i }
|
150
|
+
l.flatten.compact.size >= min && possible?(list)
|
128
151
|
end
|
129
152
|
|
130
153
|
# Will fill spaces in +list+ with default values, then coerces all arguments
|
131
|
-
# to the
|
154
|
+
# to the correct types.
|
132
155
|
#
|
133
156
|
# @return [Array]
|
134
157
|
def create_valid(list)
|
135
|
-
zip(list).map
|
158
|
+
zip(list).map do |a,r|
|
159
|
+
if a.infinite?
|
160
|
+
r.map {|i| r ? a.coerce(i) : a.coerce(i.default) }
|
161
|
+
else
|
162
|
+
r ? a.coerce(r) : a.coerce(a.default)
|
163
|
+
end
|
164
|
+
end
|
136
165
|
end
|
137
166
|
|
138
167
|
end
|
@@ -104,7 +104,7 @@ class Clive
|
|
104
104
|
# @param name [String]
|
105
105
|
# @return [String] Argument name without sqaure or angle brackets
|
106
106
|
def clean(name)
|
107
|
-
name.tr '[<>]', ''
|
107
|
+
name.tr '[<>].', ''
|
108
108
|
end
|
109
109
|
|
110
110
|
# @param hash [Hash<Symbol=>Object, Symbol=>Array>]
|
@@ -188,6 +188,8 @@ class Clive
|
|
188
188
|
cancelled_optional = false
|
189
189
|
|
190
190
|
arg_str.split(' ').zip(hash).map do |arg, opts|
|
191
|
+
opts ||= {}
|
192
|
+
|
191
193
|
if cancelled_optional
|
192
194
|
optional = false
|
193
195
|
cancelled_optional = false
|
@@ -201,7 +203,9 @@ class Clive
|
|
201
203
|
raise InvalidArgumentStringError.new(opts[:arg])
|
202
204
|
end
|
203
205
|
|
204
|
-
|
206
|
+
opts[:infinite] = true if arg =~ /\.\.\.\]?$/
|
207
|
+
|
208
|
+
{:name => clean(arg), :optional => optional}.merge(opts)
|
205
209
|
end
|
206
210
|
end
|
207
211
|
|
data/lib/clive/option.rb
CHANGED
@@ -143,7 +143,7 @@ class Clive
|
|
143
143
|
mapped_args = if @config[:boolean] == true
|
144
144
|
[[:truth, args.first]]
|
145
145
|
else
|
146
|
-
@args.zip(args).map {|k,v| [k.name, v] }
|
146
|
+
@args.zip(args).map {|k,v| [k.name, (k.infinite? ? v.first : v)] }
|
147
147
|
end
|
148
148
|
|
149
149
|
if block?
|
@@ -153,7 +153,7 @@ class Clive
|
|
153
153
|
state = @config[:runner]._run(mapped_args, state, @block)
|
154
154
|
end
|
155
155
|
else
|
156
|
-
state = set_state(state, args, scope)
|
156
|
+
state = set_state(state, args.flatten(1), scope)
|
157
157
|
end
|
158
158
|
|
159
159
|
state
|
@@ -189,7 +189,7 @@ class Clive
|
|
189
189
|
# @param scope [Symbol, nil]
|
190
190
|
def set_state(state, args, scope=nil)
|
191
191
|
args = (@args.max <= 1 ? args[0] : args)
|
192
|
-
|
192
|
+
|
193
193
|
if scope
|
194
194
|
state[scope.name].store [@names.long, @names.short].compact, args
|
195
195
|
else
|
data/lib/clive/parser.rb
CHANGED
@@ -24,13 +24,19 @@ class Clive
|
|
24
24
|
@config = DEFAULTS.merge(config)
|
25
25
|
end
|
26
26
|
|
27
|
-
# The parser should work how you expect. It allows you to put global options
|
28
|
-
# a command section (if it exists, which it doesn't), so
|
27
|
+
# The parser should work how you expect. It allows you to put global options
|
28
|
+
# before and after a command section (if it exists, which it doesn't), so
|
29
|
+
# you have something like.
|
29
30
|
#
|
30
|
-
#
|
31
|
-
# | global section | command section | global section
|
31
|
+
# app [global] [command] [global]
|
32
32
|
#
|
33
|
-
#
|
33
|
+
# Where the [global] sections are made of options and arguments and
|
34
|
+
# [command] is made of
|
35
|
+
#
|
36
|
+
# [command] [options/args]
|
37
|
+
#
|
38
|
+
# Only one command can be run, if you attempt to use two the other will be
|
39
|
+
# caught as an argument.
|
34
40
|
#
|
35
41
|
# @param argv [Array]
|
36
42
|
# The input to parse from the command line, usually ARGV.
|
data/lib/clive/version.rb
CHANGED
data/spec/clive/a_cli_spec.rb
CHANGED
@@ -24,6 +24,8 @@ describe 'A CLI' do
|
|
24
24
|
update key, sym, *args
|
25
25
|
end
|
26
26
|
|
27
|
+
opt :items, :args => '<item>...'
|
28
|
+
|
27
29
|
desc 'Print <message> <n> times'
|
28
30
|
opt :print, :arg => '<message> <n>', :as => [String, Integer] do
|
29
31
|
n.times { puts message }
|
@@ -40,11 +42,6 @@ describe 'A CLI' do
|
|
40
42
|
|
41
43
|
# implicit arg as "<choice>", also added default
|
42
44
|
opt :T, :type, :in => %w(post page blog), :default => :page, :as => Symbol
|
43
|
-
opt :force, 'Force overwrite' do
|
44
|
-
require 'highline/import'
|
45
|
-
answer = ask("Are you sure, this could delete stuff? [y/n]\n")
|
46
|
-
set :force, true if answer == "y"
|
47
|
-
end
|
48
45
|
|
49
46
|
action do |dir|
|
50
47
|
puts "Creating #{get :type} in #{dir}" if dir
|
@@ -129,6 +126,18 @@ describe 'A CLI' do
|
|
129
126
|
end
|
130
127
|
end
|
131
128
|
|
129
|
+
describe '--items' do
|
130
|
+
it 'returns the list passed to it' do
|
131
|
+
r = subject.run s '--items a b c'
|
132
|
+
r[:items].must_equal ['a', 'b', 'c']
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'returns a single item in an array if only one item passed' do
|
136
|
+
r = subject.run s '--items a'
|
137
|
+
r[:items].must_equal ['a']
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
132
141
|
describe '--print' do
|
133
142
|
it 'prints a message n times' do
|
134
143
|
this {
|
@@ -202,13 +211,6 @@ describe 'A CLI' do
|
|
202
211
|
end
|
203
212
|
end
|
204
213
|
|
205
|
-
describe '--force' do
|
206
|
-
#it 'asks for conformation' do
|
207
|
-
# r = subject.run s 'new --force'
|
208
|
-
# r[:force].must_be_true
|
209
|
-
#end
|
210
|
-
end
|
211
|
-
|
212
214
|
describe 'action' do
|
213
215
|
it 'prints the type and dir' do
|
214
216
|
this {
|
data/spec/clive/argument_spec.rb
CHANGED
@@ -2,12 +2,12 @@ $: << File.dirname(__FILE__) + '/..'
|
|
2
2
|
require 'helper'
|
3
3
|
|
4
4
|
describe Clive::Argument::AlwaysTrue do
|
5
|
-
subject { Clive::Argument::AlwaysTrue }
|
6
|
-
|
5
|
+
subject { Clive::Argument::AlwaysTrue }
|
6
|
+
|
7
7
|
it 'is always true for the method given' do
|
8
8
|
subject.for(:hey).hey.must_be_true
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
it 'is always true for the methods given' do
|
12
12
|
a = subject.for(:one, :two, :three)
|
13
13
|
a.one.must_be_true
|
@@ -18,126 +18,140 @@ end
|
|
18
18
|
|
19
19
|
describe Clive::Argument do
|
20
20
|
subject { Clive::Argument }
|
21
|
-
|
21
|
+
|
22
22
|
describe '#initialize' do
|
23
23
|
it 'converts name to Symbol' do
|
24
24
|
subject.new('arg').name.must_be_kind_of Symbol
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
it 'calls #to_proc on a Symbol constraint' do
|
28
28
|
c = mock
|
29
29
|
c.expects(:respond_to?).with(:to_proc).returns(true)
|
30
30
|
c.expects(:to_proc)
|
31
|
-
|
31
|
+
|
32
32
|
subject.new :a, :constraint => c
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
it 'merges given options with DEFAULTS' do
|
36
36
|
opts = {:optional => true}
|
37
37
|
Clive::Argument::DEFAULTS.expects(:merge).with(opts).returns({})
|
38
38
|
subject.new('arg', opts)
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
it 'finds the correct type class' do
|
42
42
|
subject.new(:a, :type => String).type.must_equal Clive::Type::String
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
it 'uses the class passed if type cannot be found' do
|
46
46
|
type = Class.new
|
47
47
|
subject.new(:a, :type => type).type.must_equal type
|
48
48
|
end
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
describe '#optional?' do
|
52
52
|
it 'is true if the argument is optional' do
|
53
53
|
subject.new(:arg, :optional => true).must_be :optional?
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
it 'is false if the argument is not optional' do
|
57
57
|
subject.new(:arg, :optional => false).wont_be :optional?
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
it 'is false by default' do
|
61
61
|
subject.new(:arg).wont_be :optional?
|
62
62
|
end
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
|
+
describe '#infinite?' do
|
66
|
+
it 'is true if the argument is infinite' do
|
67
|
+
subject.new(:arg, :infinite => true).must_be :infinite?
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'is false if the argument is not infinite' do
|
71
|
+
subject.new(:arg, :infinite => false).wont_be :infinite?
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'is false by default' do
|
75
|
+
subject.new(:arg).wont_be :infinite?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
65
79
|
describe '#to_s' do
|
66
80
|
it 'surrounds the name by < and >' do
|
67
81
|
subject.new(:a).to_s.must_equal '<a>'
|
68
82
|
end
|
69
|
-
|
83
|
+
|
70
84
|
it 'surrounds optional arguments with [ and ]' do
|
71
85
|
subject.new(:a, :optional => true).to_s.must_equal '[<a>]'
|
72
86
|
end
|
73
87
|
end
|
74
|
-
|
88
|
+
|
75
89
|
describe '#choice_str' do
|
76
90
|
it 'returns the array of values allowed' do
|
77
91
|
subject.new(:a, :within => %w(a b c)).choice_str.must_equal '(a, b, c)'
|
78
92
|
end
|
79
|
-
|
93
|
+
|
80
94
|
it 'returns the range of values allowed' do
|
81
95
|
subject.new(:a, :within => 1..5).choice_str.must_equal '(1..5)'
|
82
96
|
end
|
83
97
|
end
|
84
|
-
|
98
|
+
|
85
99
|
describe '#possible?' do
|
86
100
|
describe 'for @type' do
|
87
101
|
subject { Clive::Argument.new :a, :type => Clive::Type::Time }
|
88
|
-
|
102
|
+
|
89
103
|
it 'is true for correct string values' do
|
90
104
|
subject.must_be :possible?, '12:34'
|
91
105
|
end
|
92
|
-
|
106
|
+
|
93
107
|
it 'is true for objects of type' do
|
94
108
|
subject.must_be :possible?, Time.parse('12:34')
|
95
109
|
end
|
96
|
-
|
110
|
+
|
97
111
|
unless RUBY_VERSION == '1.8.7' # No big problem so just ignore
|
98
112
|
it 'is false for incorrect values' do
|
99
113
|
subject.wont_be :possible?, 'not-a-time'
|
100
114
|
end
|
101
115
|
end
|
102
116
|
end
|
103
|
-
|
117
|
+
|
104
118
|
describe 'for @match' do
|
105
119
|
subject { Clive::Argument.new :a, :match => /^[a-e]+![f-o]+\?.$/ }
|
106
|
-
|
120
|
+
|
107
121
|
it 'is true for matching values' do
|
108
122
|
subject.must_be :possible?, 'abe!off?.'
|
109
123
|
end
|
110
|
-
|
124
|
+
|
111
125
|
it 'is false for non-matching values' do
|
112
126
|
subject.wont_be :possible?, 'off?abe!.'
|
113
127
|
end
|
114
128
|
end
|
115
|
-
|
129
|
+
|
116
130
|
describe 'for @within' do
|
117
131
|
subject { Clive::Argument.new :a, :within => %w(dog cat fish) }
|
118
|
-
|
132
|
+
|
119
133
|
it 'is true for elements included in the collection' do
|
120
134
|
subject.must_be :possible?, 'dog'
|
121
135
|
end
|
122
|
-
|
136
|
+
|
123
137
|
it 'is false for elements not in the collection' do
|
124
138
|
subject.wont_be :possible?, 'mouse'
|
125
139
|
end
|
126
140
|
end
|
127
|
-
|
141
|
+
|
128
142
|
describe 'for @constraint' do
|
129
143
|
subject { Clive::Argument.new :a, :constraint => proc {|i| i.size == 5 } }
|
130
|
-
|
144
|
+
|
131
145
|
it 'is true if the proc returns true' do
|
132
146
|
subject.must_be :possible?, 'abcde'
|
133
147
|
end
|
134
|
-
|
148
|
+
|
135
149
|
it 'is false if the proc returns false' do
|
136
150
|
subject.wont_be :possible?, 'abcd'
|
137
151
|
end
|
138
152
|
end
|
139
153
|
end
|
140
|
-
|
154
|
+
|
141
155
|
describe '#coerce' do
|
142
156
|
it 'uses @type to return the correct object' do
|
143
157
|
type = mock
|
@@ -4,11 +4,11 @@ require 'helper'
|
|
4
4
|
describe Clive::Arguments do
|
5
5
|
|
6
6
|
subject {
|
7
|
-
Clive::Arguments.create :args => '[<a>] <b> <c> [<d>]',
|
7
|
+
Clive::Arguments.create :args => '[<a>] <b> <c> [<d>]',
|
8
8
|
:type => [Integer] * 4,
|
9
9
|
:constraint => [:even?, :odd?] * 2
|
10
10
|
}
|
11
|
-
|
11
|
+
|
12
12
|
describe '.create' do
|
13
13
|
it 'parses the options passed using Parser' do
|
14
14
|
parser, opts = mock, stub
|
@@ -18,7 +18,7 @@ describe Clive::Arguments do
|
|
18
18
|
end
|
19
19
|
it 'returns a list of Argument instances' do
|
20
20
|
a = Clive::Arguments.create :args => '[<a>] <b>', :as => [Integer, nil], :in => [1..5, nil]
|
21
|
-
a[0].must_be_argument :name => :a, :optional => true, :within => 1..5,
|
21
|
+
a[0].must_be_argument :name => :a, :optional => true, :within => 1..5,
|
22
22
|
:type => Clive::Type::Integer
|
23
23
|
a[1].must_be_argument :name => :b, :optional => false
|
24
24
|
end
|
@@ -26,48 +26,86 @@ describe Clive::Arguments do
|
|
26
26
|
|
27
27
|
describe '#zip' do
|
28
28
|
it 'zips arguments properly' do
|
29
|
-
def subject.
|
30
|
-
|
29
|
+
def subject.z(other); zip(other).map(&:last); end
|
30
|
+
|
31
31
|
# These behaviours are definitely what should happen
|
32
|
-
subject.
|
33
|
-
subject.
|
34
|
-
subject.
|
35
|
-
subject.
|
36
|
-
|
32
|
+
subject.z(%w(1 4)).must_equal [nil, '1', '4', nil]
|
33
|
+
subject.z(%w(1 4 3)).must_equal [nil, '1', '4', '3']
|
34
|
+
subject.z(%w(2 1 4 3)).must_equal ['2', '1', '4', '3']
|
35
|
+
subject.z(%w(2 1 4)).must_equal ['2', '1', '4', nil]
|
36
|
+
|
37
37
|
# These behaviours may change
|
38
|
-
subject.
|
39
|
-
subject.
|
40
|
-
subject.
|
41
|
-
end
|
38
|
+
subject.z(%w(2)).must_equal [nil, nil, '2', nil]
|
39
|
+
subject.z(%w(1)).must_equal [nil, '1', nil, nil]
|
40
|
+
subject.z(%w(2 1)).must_equal [nil, nil, '2', nil]
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'zips infinite arguments properly' do
|
44
|
+
a = Clive::Argument.new(:a)
|
45
|
+
b = Clive::Argument.new(:b, :optional => true)
|
46
|
+
c = Clive::Argument.new(:c, :infinite => true)
|
47
|
+
s = Clive::Arguments.new([a, b, c])
|
48
|
+
|
49
|
+
s.zip(%w(a)).must_equal [[a, 'a'], [b, nil], [c, [nil]]]
|
50
|
+
s.zip(%w(a b)).must_equal [[a, 'a'], [b, nil], [c, ['b']]]
|
51
|
+
s.zip(%w(a b c)).must_equal [[a, 'a'], [b, 'b'], [c, ['c']]]
|
52
|
+
s.zip(%w(a b c d)).must_equal [[a, 'a'], [b, 'b'], [c, ['c', 'd']]]
|
53
|
+
end
|
42
54
|
end
|
43
|
-
|
55
|
+
|
44
56
|
describe '#to_s' do
|
45
57
|
subject {
|
46
58
|
Clive::Arguments.new( [
|
47
|
-
Clive::Argument.new(:a),
|
59
|
+
Clive::Argument.new(:a),
|
48
60
|
Clive::Argument.new(:b, :optional => true),
|
49
61
|
Clive::Argument.new(:c, :optional => true),
|
50
62
|
Clive::Argument.new(:d)
|
51
63
|
])
|
52
64
|
}
|
53
|
-
|
65
|
+
|
54
66
|
it 'removes extra square brackets' do
|
55
67
|
subject.to_s.must_equal "<a> [<b> <c>] <d>"
|
56
68
|
end
|
57
69
|
end
|
58
|
-
|
70
|
+
|
59
71
|
describe '#min' do
|
60
72
|
it 'returns the number of non-optional arguments' do
|
61
73
|
subject.min.must_equal 2
|
62
74
|
end
|
63
75
|
end
|
64
|
-
|
76
|
+
|
65
77
|
describe '#max' do
|
66
78
|
it 'returns the total number of arguments' do
|
67
79
|
subject.max.must_equal 4
|
68
80
|
end
|
81
|
+
|
82
|
+
it 'returns Infinity if last arg is infinite' do
|
83
|
+
as = Clive::Arguments.new([Clive::Argument.new(:a, :infinite => true)])
|
84
|
+
as.max.must_equal 1.0/0.0
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#[]' do
|
89
|
+
let(:a) { Clive::Argument.new(:a) }
|
90
|
+
let(:b) { Clive::Argument.new(:b) }
|
91
|
+
let(:c) { Clive::Argument.new(:c, :infinite => true) }
|
92
|
+
|
93
|
+
it 'returns the indexed item normally' do
|
94
|
+
subject = Clive::Arguments.new([a, b])
|
95
|
+
subject[0].must_equal a
|
96
|
+
subject[1].must_equal b
|
97
|
+
subject[2].must_equal nil
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'returns the last item if infinite' do
|
101
|
+
subject = Clive::Arguments.new([a, c])
|
102
|
+
subject[0].must_equal a
|
103
|
+
subject[1].must_equal c
|
104
|
+
subject[2].must_equal c
|
105
|
+
subject[99].must_equal c
|
106
|
+
end
|
69
107
|
end
|
70
|
-
|
108
|
+
|
71
109
|
describe '#possible?' do
|
72
110
|
it 'is true if each argument is possible and list is not too long' do
|
73
111
|
subject.must_be :possible?, [2]
|
@@ -75,29 +113,29 @@ describe Clive::Arguments do
|
|
75
113
|
subject.must_be :possible?, [2, 3, 4]
|
76
114
|
subject.must_be :possible?, [2, 3, 4, 5]
|
77
115
|
end
|
78
|
-
|
116
|
+
|
79
117
|
it 'is false if the list is too long' do
|
80
118
|
subject.wont_be :possible?, [2, 3, 4, 5, 6]
|
81
119
|
end
|
82
|
-
|
120
|
+
|
83
121
|
it 'is true even if an argument is not possible' do
|
84
122
|
subject.must_be :possible?, ['hello']
|
85
123
|
end
|
86
|
-
|
124
|
+
|
87
125
|
it 'single' do
|
88
126
|
subject = Clive::Arguments.create :arg => '<name>'
|
89
127
|
subject.must_be :possible?, []
|
90
128
|
subject.must_be :possible?, %w(John)
|
91
129
|
subject.wont_be :possible?, %w(John Doe)
|
92
130
|
end
|
93
|
-
|
131
|
+
|
94
132
|
it 'single optional' do
|
95
133
|
subject = Clive::Arguments.create :arg => '[<name>]'
|
96
134
|
subject.must_be :possible?, []
|
97
135
|
subject.must_be :possible?, %w(John)
|
98
136
|
subject.wont_be :possible?, %w(John Doe)
|
99
137
|
end
|
100
|
-
|
138
|
+
|
101
139
|
it 'single with constraint' do
|
102
140
|
subject = Clive::Arguments.create :arg => '<name>', :constraint => proc {|i| i.size == 4 }
|
103
141
|
subject.must_be :possible?, []
|
@@ -105,7 +143,7 @@ describe Clive::Arguments do
|
|
105
143
|
subject.wont_be :possible?, %w(James)
|
106
144
|
subject.wont_be :possible?, %w(John Doe)
|
107
145
|
end
|
108
|
-
|
146
|
+
|
109
147
|
it 'single optional with constraint' do
|
110
148
|
subject = Clive::Arguments.create :arg => '[<name>]', :constraint => proc {|i| i.size == 4 }
|
111
149
|
subject.must_be :possible?, []
|
@@ -113,7 +151,7 @@ describe Clive::Arguments do
|
|
113
151
|
subject.wont_be :possible?, %w(James)
|
114
152
|
subject.wont_be :possible?, %w(John Doe)
|
115
153
|
end
|
116
|
-
|
154
|
+
|
117
155
|
it 'multiple surrounding' do
|
118
156
|
subject = Clive::Arguments.create :args => '<first> [<middle>] <last>'
|
119
157
|
subject.must_be :possible?, []
|
@@ -122,7 +160,7 @@ describe Clive::Arguments do
|
|
122
160
|
subject.must_be :possible?, %w(John David Doe)
|
123
161
|
subject.wont_be :possible?, %w(John David James Doe)
|
124
162
|
end
|
125
|
-
|
163
|
+
|
126
164
|
it 'multiple middle' do
|
127
165
|
subject = Clive::Arguments.create :args => '[<first>] <middle> [<last>]'
|
128
166
|
subject.must_be :possible?, []
|
@@ -131,36 +169,75 @@ describe Clive::Arguments do
|
|
131
169
|
subject.must_be :possible?, %w(John David Doe)
|
132
170
|
subject.wont_be :possible?, %w(John David James Doe)
|
133
171
|
end
|
134
|
-
|
172
|
+
|
135
173
|
it 'multiple surrounding with constraints' do
|
136
174
|
subject = Clive::Arguments.create :args => '<first> [<middle>] <last>',
|
137
175
|
:constraint => [proc {|i| i.size == 3 }, proc {|i| i.size == 4 }, proc {|i| i.size == 5 }]
|
138
176
|
subject.must_be :possible?, []
|
139
|
-
|
177
|
+
|
140
178
|
subject.must_be :possible?, %w(Joe) # [Joe, ..., ...]
|
141
179
|
subject.wont_be :possible?, %w(Gary) # [!!!, Gary, ...]
|
142
180
|
subject.wont_be :possible?, %w(David) # [!!!, nil, David]
|
143
|
-
|
181
|
+
|
144
182
|
subject.must_be :possible?, %w(Joe Gary) # [Joe, Gary, ...]
|
145
183
|
subject.must_be :possible?, %w(Joe David) # [Joe, ..., David]
|
146
184
|
subject.wont_be :possible?, %w(Gary David) # [!!!, Gary, David]
|
147
|
-
|
185
|
+
|
148
186
|
subject.must_be :possible?, %w(Joe Gary David) # [Joe, Gary, David]
|
149
187
|
subject.wont_be :possible?, %w(Joe Gary David Doe) # [Joe, Gary, David] Doe
|
150
188
|
end
|
189
|
+
|
190
|
+
it 'infinite' do
|
191
|
+
subject = Clive::Arguments.create :args => '<arg>...'
|
192
|
+
|
193
|
+
subject.must_be :possible?, []
|
194
|
+
subject.must_be :possible?, %w(a)
|
195
|
+
subject.must_be :possible?, %w(a b)
|
196
|
+
subject.must_be :possible?, %w(a b c)
|
197
|
+
subject.must_be :possible?, %w(a b c d)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'infinite with constraints' do
|
201
|
+
subject = Clive::Arguments.create :args => '<arg>...', :in => 'a'..'f'
|
202
|
+
|
203
|
+
subject.must_be :possible?, []
|
204
|
+
subject.must_be :possible?, %w(a)
|
205
|
+
subject.must_be :possible?, %w(a b)
|
206
|
+
|
207
|
+
subject.wont_be :possible?, %w(a z)
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'optional infinite' do
|
211
|
+
subject = Clive::Arguments.create :args => '[<arg>...]'
|
212
|
+
|
213
|
+
subject.must_be :possible?, []
|
214
|
+
subject.must_be :possible?, %w(a)
|
215
|
+
subject.must_be :possible?, %w(a b)
|
216
|
+
subject.must_be :possible?, %w(a b c)
|
217
|
+
subject.must_be :possible?, %w(a b c d)
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'normal and infinite' do
|
221
|
+
subject = Clive::Arguments.create :args => '<a> <arg>...'
|
222
|
+
|
223
|
+
subject.must_be :possible?, []
|
224
|
+
subject.must_be :possible?, %w(a)
|
225
|
+
subject.must_be :possible?, %w(a b)
|
226
|
+
subject.must_be :possible?, %w(a b c)
|
227
|
+
end
|
151
228
|
end
|
152
|
-
|
229
|
+
|
153
230
|
describe '#valid?' do
|
154
231
|
it 'is false if the list is not #possible' do
|
155
232
|
subject.stubs(:possible?).returns(false)
|
156
233
|
subject.wont_be :valid?, [1, 2]
|
157
234
|
end
|
158
|
-
|
235
|
+
|
159
236
|
it 'is false if the list is too short' do
|
160
237
|
subject.stubs(:possible?).returns(true)
|
161
238
|
subject.wont_be :valid?, [1]
|
162
239
|
end
|
163
|
-
|
240
|
+
|
164
241
|
it 'is true if the list is #possible? and not too short' do
|
165
242
|
subject.stubs(:possible?).returns(true)
|
166
243
|
subject.must_be :valid?, [1, 2]
|
@@ -168,7 +245,7 @@ describe Clive::Arguments do
|
|
168
245
|
subject.must_be :valid?, [0, 1, 2, 3]
|
169
246
|
end
|
170
247
|
end
|
171
|
-
|
248
|
+
|
172
249
|
describe '#create_valid' do
|
173
250
|
it 'returns the correct arguments' do
|
174
251
|
a = Clive::Arguments.create :args => '[<a>] <b> [<c>]'
|
@@ -176,12 +253,12 @@ describe Clive::Arguments do
|
|
176
253
|
a.create_valid(%w(a b)).must_equal ['a', 'b', nil]
|
177
254
|
a.create_valid(%w(a)).must_equal [nil, 'a', nil]
|
178
255
|
end
|
179
|
-
|
256
|
+
|
180
257
|
it 'coerces the arguments' do
|
181
258
|
a = Clive::Arguments.create :args => '<a> <b>', :as => [Integer, Float]
|
182
259
|
a.create_valid(%w(50.55 50.55)).must_equal [50, 50.55]
|
183
260
|
end
|
184
|
-
|
261
|
+
|
185
262
|
it 'uses defaults where needed' do
|
186
263
|
a = Clive::Arguments.create :args => '[<a>] <b> [<c>]', :defaults => ['y', nil, 'y']
|
187
264
|
a.create_valid(%w(n)).must_equal %w(y n y)
|
data/spec/clive/parser_spec.rb
CHANGED
@@ -143,6 +143,38 @@ EOS
|
|
143
143
|
r.must_have :auto
|
144
144
|
end
|
145
145
|
end
|
146
|
+
|
147
|
+
describe 'Infinite arguments' do
|
148
|
+
subject {
|
149
|
+
Clive.new { opt :items, :arg => '<item>...' }
|
150
|
+
}
|
151
|
+
|
152
|
+
it 'requires at least one argument' do
|
153
|
+
this {
|
154
|
+
subject.run s '--items'
|
155
|
+
}.must_raise Clive::Parser::MissingArgumentError
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'takes infinite arguments' do
|
159
|
+
r = subject.run s '--items a b c d e f g h'
|
160
|
+
r.items.must_equal %w(a b c d e f g h)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe 'Optional infinite arguments' do
|
165
|
+
subject {
|
166
|
+
Clive.new { opt :items, :arg => '[<item>...]' }
|
167
|
+
}
|
168
|
+
|
169
|
+
it 'does not require an argument' do
|
170
|
+
subject.run s '--items'
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'takes infinite arguments' do
|
174
|
+
r = subject.run s '--items a b c d e f g h'
|
175
|
+
r.items.must_equal %w(a b c d e f g h)
|
176
|
+
end
|
177
|
+
end
|
146
178
|
|
147
179
|
describe 'Commands' do
|
148
180
|
describe 'with options and arguments' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clive
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
16
|
-
requirement: &
|
16
|
+
requirement: &2152595180 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '2.6'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2152595180
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: mocha
|
27
|
-
requirement: &
|
27
|
+
requirement: &2152592780 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0.10'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2152592780
|
36
36
|
description: ! " Clive provides a DSL for building command line interfaces. It
|
37
37
|
allows \n you to define commands and options, which can also take arguments,
|
38
38
|
\n and then runs the correct stuff!\n"
|