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 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.
@@ -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]
@@ -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
- size
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 >= size
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 < size - 1)
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 do |a,i|
122
- if a.optional?
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 corect types.
154
+ # to the correct types.
132
155
  #
133
156
  # @return [Array]
134
157
  def create_valid(list)
135
- zip(list).map {|a,r| r ? a.coerce(r) : a.coerce(a.default) }
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
- {:name => clean(arg), :optional => optional}.merge(opts || {})
206
+ opts[:infinite] = true if arg =~ /\.\.\.\]?$/
207
+
208
+ {:name => clean(arg), :optional => optional}.merge(opts)
205
209
  end
206
210
  end
207
211
 
@@ -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
@@ -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 before and after
28
- # a command section (if it exists, which it doesn't), so you have something like.
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
- # my_app.rb [global options] ([command] [options] [args]) [g. options] [g. args] [g. options] etc.
31
- # | global section | command section | global section
31
+ # app [global] [command] [global]
32
32
  #
33
- # Only one command can be run, if you attempt to use two the other will be caught as an argument.
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.
@@ -1,3 +1,3 @@
1
1
  class Clive
2
- VERSION = '1.0.1'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -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 {
@@ -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.simple_zip(other); zip(other).map(&:last); end
30
-
29
+ def subject.z(other); zip(other).map(&:last); end
30
+
31
31
  # These behaviours are definitely what should happen
32
- subject.simple_zip(%w(1 4)).must_equal [nil, '1', '4', nil]
33
- subject.simple_zip(%w(1 4 3)).must_equal [nil, '1', '4', '3']
34
- subject.simple_zip(%w(2 1 4 3)).must_equal ['2', '1', '4', '3']
35
- subject.simple_zip(%w(2 1 4)).must_equal ['2', '1', '4', nil]
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.simple_zip(%w(2)).must_equal [nil, nil, '2', nil]
39
- subject.simple_zip(%w(1)).must_equal [nil, '1', nil, nil]
40
- subject.simple_zip(%w(2 1)).must_equal [nil, nil, '2', nil]
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)
@@ -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.1
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-01 00:00:00.000000000 Z
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: &2156107440 !ruby/object:Gem::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: *2156107440
24
+ version_requirements: *2152595180
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: mocha
27
- requirement: &2156106140 !ruby/object:Gem::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: *2156106140
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"