clive 1.0.1 → 1.1.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/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"
|