clive 0.8.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.md +328 -227
- data/lib/clive.rb +130 -50
- data/lib/clive/argument.rb +170 -0
- data/lib/clive/arguments.rb +139 -0
- data/lib/clive/arguments/parser.rb +210 -0
- data/lib/clive/base.rb +189 -0
- data/lib/clive/command.rb +342 -444
- data/lib/clive/error.rb +66 -0
- data/lib/clive/formatter.rb +57 -141
- data/lib/clive/formatter/colour.rb +37 -0
- data/lib/clive/formatter/plain.rb +172 -0
- data/lib/clive/option.rb +185 -75
- data/lib/clive/option/runner.rb +163 -0
- data/lib/clive/output.rb +141 -16
- data/lib/clive/parser.rb +180 -87
- data/lib/clive/struct_hash.rb +109 -0
- data/lib/clive/type.rb +117 -0
- data/lib/clive/type/definitions.rb +170 -0
- data/lib/clive/type/lookup.rb +23 -0
- data/lib/clive/version.rb +3 -3
- data/spec/clive/a_cli_spec.rb +245 -0
- data/spec/clive/argument_spec.rb +148 -0
- data/spec/clive/arguments/parser_spec.rb +35 -0
- data/spec/clive/arguments_spec.rb +191 -0
- data/spec/clive/command_spec.rb +276 -209
- data/spec/clive/formatter/colour_spec.rb +129 -0
- data/spec/clive/formatter/plain_spec.rb +129 -0
- data/spec/clive/option/runner_spec.rb +92 -0
- data/spec/clive/option_spec.rb +149 -23
- data/spec/clive/output_spec.rb +86 -2
- data/spec/clive/parser_spec.rb +201 -81
- data/spec/clive/struct_hash_spec.rb +82 -0
- data/spec/clive/type/definitions_spec.rb +312 -0
- data/spec/clive/type_spec.rb +107 -0
- data/spec/clive_spec.rb +60 -0
- data/spec/extras/expectations.rb +86 -0
- data/spec/extras/focus.rb +22 -0
- data/spec/helper.rb +35 -0
- metadata +56 -36
- data/lib/clive/bool.rb +0 -67
- data/lib/clive/exceptions.rb +0 -54
- data/lib/clive/flag.rb +0 -199
- data/lib/clive/switch.rb +0 -31
- data/lib/clive/tokens.rb +0 -141
- data/spec/clive/bool_spec.rb +0 -54
- data/spec/clive/flag_spec.rb +0 -117
- data/spec/clive/formatter_spec.rb +0 -108
- data/spec/clive/switch_spec.rb +0 -14
- data/spec/clive/tokens_spec.rb +0 -38
- data/spec/shared_specs.rb +0 -16
- data/spec/spec_helper.rb +0 -12
@@ -0,0 +1,170 @@
|
|
1
|
+
# can't autoload time as the constant is already defined
|
2
|
+
require 'time'
|
3
|
+
autoload :Pathname, 'pathname'
|
4
|
+
|
5
|
+
class Clive
|
6
|
+
class Type
|
7
|
+
|
8
|
+
# Basic object, all arguments are valid and will simply return
|
9
|
+
# themselves.
|
10
|
+
class Object < Type
|
11
|
+
|
12
|
+
# Test the value to see if it is a valid value for this Tyoe.
|
13
|
+
# @param arg [String] The value to be tested
|
14
|
+
def valid?(arg)
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
# Cast the arg (String) to the correct type.
|
19
|
+
# @param arg [String] The value to be cast
|
20
|
+
def typecast(arg)
|
21
|
+
arg
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# String will accept any argument which is not +nil+ and will
|
26
|
+
# return the argument with #to_s called on it.
|
27
|
+
class String < Object
|
28
|
+
refute :nil?
|
29
|
+
cast :to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
# Symbol will accept and argument which is not +nil+ and will
|
33
|
+
# return the argument with #to_sym called on it.
|
34
|
+
class Symbol < Object
|
35
|
+
refute :nil?
|
36
|
+
cast :to_sym
|
37
|
+
end
|
38
|
+
|
39
|
+
# Integer will match anything that float matches, but will
|
40
|
+
# return an integer. If you need something that only matches
|
41
|
+
# integer values properly use {StrictInteger}.
|
42
|
+
class Integer < Object
|
43
|
+
match /^[-+]?\d*\.?\d+([eE][-+]?\d+)?$/
|
44
|
+
cast :to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
# StrictInteger only matches strings that look like integers,
|
48
|
+
# it returns Integers.
|
49
|
+
# @see Integer
|
50
|
+
class StrictInteger < Object
|
51
|
+
match /^[-+]?\d+([eE][-+]?\d+)?$/
|
52
|
+
cast :to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
# Binary matches any binary number which may or may not have a "0b" prefix
|
56
|
+
# and returns the number as an Integer.
|
57
|
+
class Binary < Object
|
58
|
+
match /^[-+]?(0b)?[01]*$/i
|
59
|
+
cast :to_i, 2
|
60
|
+
end
|
61
|
+
|
62
|
+
# Octal matches any octal number which may (or may not) be prefixed with "0"
|
63
|
+
# or "0o" (or even "0O") so 25, 025, 0o25 and 0O25 are all valid and will
|
64
|
+
# give the same result, the Integer 21.
|
65
|
+
class Octal < Object
|
66
|
+
match /^[-+]?(0o?)?[0-7]*$/i
|
67
|
+
cast :to_i, 8
|
68
|
+
end
|
69
|
+
|
70
|
+
# Hexadecimal matches any hexadecimal number which may or may not have a
|
71
|
+
# "0x" prefix, it returns the number as an Integer.
|
72
|
+
class Hexadecimal < Object
|
73
|
+
match /^[-+]?(0x)?[0-9a-f]*$/i
|
74
|
+
cast :to_i, 16
|
75
|
+
end
|
76
|
+
|
77
|
+
class Float < Object
|
78
|
+
match /^[-+]?\d*\.?\d+([eE][-+]?\d+)?$/
|
79
|
+
cast :to_f
|
80
|
+
end
|
81
|
+
|
82
|
+
# Boolean will accept 'true', 't', 'yes', 'y' or 'on' as +true+ and
|
83
|
+
# 'false', 'f', 'no', 'n' or 'off' as +false+.
|
84
|
+
class Boolean < Object
|
85
|
+
TRUE_VALUES = %w(true t yes y on)
|
86
|
+
FALSE_VALUES = %w(false f no n off)
|
87
|
+
|
88
|
+
def valid?(arg)
|
89
|
+
(TRUE_VALUES + FALSE_VALUES).include? arg
|
90
|
+
end
|
91
|
+
|
92
|
+
def typecast(arg)
|
93
|
+
case arg
|
94
|
+
when *TRUE_VALUES then true
|
95
|
+
when *FALSE_VALUES then false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Pathname < Object
|
101
|
+
refute :nil?
|
102
|
+
|
103
|
+
def typecast(arg)
|
104
|
+
::Pathname.new arg
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Range accepts 'a..b', 'a...b' which behave as in ruby and
|
109
|
+
# 'a-b' which behaves like 'a..b'. It returns the correct
|
110
|
+
# Range object.
|
111
|
+
class Range < Object
|
112
|
+
match /^(\w+\.\.\.?\w+|\w+\-\w+)$/
|
113
|
+
|
114
|
+
def typecast(arg)
|
115
|
+
if arg.include?('...')
|
116
|
+
a,b = arg.split('...')
|
117
|
+
::Range.new a, b, true
|
118
|
+
elsif arg.include?('..')
|
119
|
+
a,b = arg.split('..')
|
120
|
+
::Range.new a, b, false
|
121
|
+
else
|
122
|
+
a,b = arg.split('-')
|
123
|
+
::Range.new a, b, false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Array accepts a list of arguments separated by a comma, no
|
129
|
+
# spaces are allowed. It returns an array of the elements.
|
130
|
+
class Array < Object
|
131
|
+
match /^(.+,)*.+[^,]$/
|
132
|
+
cast :split, ','
|
133
|
+
end
|
134
|
+
|
135
|
+
# Time accepts any value which can be parsed by {::Time.parse},
|
136
|
+
# it returns the correct {::Time} object.
|
137
|
+
class Time < Object
|
138
|
+
def valid?(arg)
|
139
|
+
::Time.parse arg
|
140
|
+
true
|
141
|
+
rescue
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
145
|
+
def typecast(arg)
|
146
|
+
::Time.parse arg
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class Regexp < Object
|
151
|
+
match /^\/.*?\/[imxou]*$/
|
152
|
+
|
153
|
+
OPTS = {
|
154
|
+
'x' => ::Regexp::EXTENDED,
|
155
|
+
'i' => ::Regexp::IGNORECASE,
|
156
|
+
'm' => ::Regexp::MULTILINE
|
157
|
+
}
|
158
|
+
|
159
|
+
def typecast(arg)
|
160
|
+
parts = arg.split('/')
|
161
|
+
mods = parts.pop
|
162
|
+
arg = parts.join('')
|
163
|
+
mods = mods.split('').map {|a| OPTS[a] }.inject{|a,e| a | e }
|
164
|
+
|
165
|
+
::Regexp.new arg, mods
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Clive
|
2
|
+
class Type
|
3
|
+
|
4
|
+
# Clive::Type::Lookup is an almost carbon copy of DataMapper::Property::Lookup
|
5
|
+
# @see https://github.com/datamapper/dm-core/blob/master/lib/dm-core/property/lookup.rb
|
6
|
+
module Lookup
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
# Provides access to Types defined under {Type} as if accessed
|
11
|
+
# normally.
|
12
|
+
#
|
13
|
+
# @param name [#to_s] The name of the Type to lookup.
|
14
|
+
# @return [Type] The type with the given name.
|
15
|
+
# @raise [NameError] The property could not be found.
|
16
|
+
#
|
17
|
+
def const_missing(name)
|
18
|
+
Type.find_class(name.to_s) || super
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/clive/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = '0.
|
3
|
-
end
|
1
|
+
class Clive
|
2
|
+
VERSION = '1.0.0'
|
3
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + '/..'
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
describe 'A CLI' do
|
5
|
+
subject {
|
6
|
+
Clive.new {
|
7
|
+
|
8
|
+
header 'Usage: clive_test.rb [command] [options]'
|
9
|
+
|
10
|
+
opt :version, :tail => true do
|
11
|
+
puts "Version 1"
|
12
|
+
end
|
13
|
+
|
14
|
+
set :something, []
|
15
|
+
|
16
|
+
bool :v, :verbose
|
17
|
+
bool :a, :auto
|
18
|
+
|
19
|
+
opt :s, :size, 'Size of thing', :arg => '<size>', :as => Float
|
20
|
+
opt :S, :super_size
|
21
|
+
|
22
|
+
opt :name, :args => '<name>'
|
23
|
+
opt :modify, :arg => '<key> <sym> [<args>]', :as => [Symbol, Symbol, Array] do
|
24
|
+
update key, sym, *args
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Print <message> <n> times'
|
28
|
+
opt :print, :arg => '<message> <n>', :as => [String, Integer] do
|
29
|
+
n.times { puts message }
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'A super long description for a super stupid option, this should test the _extreme_ wrapping abilities as it should all be aligned. Maybe I should go for another couple of lines just for good measure. That\'s all'
|
33
|
+
opt :complex, :arg => '[<one>] <two> [<three>]', :match => [ /^\d$/, /^\d\d$/, /^\d\d\d$/ ] do |a,b,c|
|
34
|
+
puts "a: #{a}, b: #{b}, c: #{c}"
|
35
|
+
end
|
36
|
+
|
37
|
+
command :new, :create, 'Creates new things', :arg => '[<dir>]', :match => /\// do
|
38
|
+
|
39
|
+
set :something, []
|
40
|
+
|
41
|
+
# implicit arg as "<choice>", also added default
|
42
|
+
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
|
+
|
49
|
+
action do |dir|
|
50
|
+
puts "Creating #{get :type} in #{dir}" if dir
|
51
|
+
end
|
52
|
+
end
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
describe '--version' do
|
57
|
+
it 'prints version string' do
|
58
|
+
this {
|
59
|
+
subject.run s '--version'
|
60
|
+
}.must_output "Version 1\n"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'set :something' do
|
65
|
+
it 'is set to an empty Array' do
|
66
|
+
r = subject.run []
|
67
|
+
r[:something].must_equal []
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '-a, --[no-]auto' do
|
72
|
+
it 'sets to true' do
|
73
|
+
r = subject.run s '--auto'
|
74
|
+
r[:auto].must_be_true
|
75
|
+
r[:a].must_be_true
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'sets to false if no passed' do
|
79
|
+
r = subject.run s '--no-auto'
|
80
|
+
r[:auto].must_be_false
|
81
|
+
r[:a].must_be_false
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'allows the short version' do
|
85
|
+
r = subject.run s '-a'
|
86
|
+
r[:auto].must_be_true
|
87
|
+
r[:a].must_be_true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '-s, --size' do
|
92
|
+
it 'takes a Float as an argument' do
|
93
|
+
r = subject.run s '--size 50.56'
|
94
|
+
r[:size].must_equal 50.56
|
95
|
+
r[:s].must_equal 50.56
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'raises an error if the argument is not passed' do
|
99
|
+
this {
|
100
|
+
subject.run s '--size'
|
101
|
+
}.must_raise Clive::Parser::MissingArgumentError
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'raises an error if a Float is not given' do
|
105
|
+
this {
|
106
|
+
subject.run s '--size hello'
|
107
|
+
}.must_raise Clive::Parser::MissingArgumentError
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '-S, --super-size' do
|
112
|
+
it 'can be called with dashes' do
|
113
|
+
r = subject.run s '--super-size'
|
114
|
+
r[:super_size].must_be_true
|
115
|
+
r[:S].must_be_true
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'can be called with underscores' do
|
119
|
+
r = subject.run s '--super_size'
|
120
|
+
r[:super_size].must_be_true
|
121
|
+
r[:S].must_be_true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '--modify' do
|
126
|
+
it 'updates the key' do
|
127
|
+
r = subject.run s '--name "John Doe" --modify name count oe,e'
|
128
|
+
r[:name].must_equal 1
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '--print' do
|
133
|
+
it 'prints a message n times' do
|
134
|
+
this {
|
135
|
+
subject.run s '--print "Hello World!" 5'
|
136
|
+
}.must_output "Hello World!\n" * 5
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '--complex' do
|
141
|
+
it 'takes one argument' do
|
142
|
+
this {
|
143
|
+
subject.run s '--complex 55'
|
144
|
+
}.must_output "a: , b: 55, c: \n"
|
145
|
+
|
146
|
+
this {
|
147
|
+
subject.run s '--complex 4'
|
148
|
+
}.must_raise Clive::Parser::MissingArgumentError
|
149
|
+
|
150
|
+
this {
|
151
|
+
subject.run s '--complex 666'
|
152
|
+
}.must_raise Clive::Parser::MissingArgumentError
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'takes two arguments' do
|
156
|
+
this {
|
157
|
+
subject.run s '--complex 4 55'
|
158
|
+
}.must_output "a: 4, b: 55, c: \n"
|
159
|
+
|
160
|
+
this {
|
161
|
+
subject.run s '--complex 55 666'
|
162
|
+
}.must_output "a: , b: 55, c: 666\n"
|
163
|
+
|
164
|
+
this {
|
165
|
+
subject.run s '--complex 4 666'
|
166
|
+
}.must_raise Clive::Parser::MissingArgumentError
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'takes three arguments' do
|
170
|
+
this {
|
171
|
+
subject.run s '--complex 4 55 666'
|
172
|
+
}.must_output "a: 4, b: 55, c: 666\n"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe 'new' do
|
177
|
+
describe 'set :something' do
|
178
|
+
it 'sets :something in :new to []' do
|
179
|
+
r = subject.run s 'new'
|
180
|
+
r[:new][:something].must_equal []
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '-T, --type' do
|
185
|
+
it 'sets the type' do
|
186
|
+
r = subject.run s 'new --type blog'
|
187
|
+
r[:new][:type].must_equal :blog
|
188
|
+
r[:new][:T].must_equal :blog
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'uses the default' do
|
192
|
+
r = subject.run s 'new --type'
|
193
|
+
r[:new][:type].must_equal :page
|
194
|
+
r[:new][:T].must_equal :page
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'ignores non valid options' do
|
198
|
+
r = subject.run s 'new --type crazy'
|
199
|
+
r[:new][:type].must_equal :page
|
200
|
+
r[:new][:T].must_equal :page
|
201
|
+
r.args.must_equal ['crazy']
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
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
|
+
describe 'action' do
|
213
|
+
it 'prints the type and dir' do
|
214
|
+
this {
|
215
|
+
subject.run s 'new --type ~/dir'
|
216
|
+
}.must_output "Creating page in ~/dir\n"
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'only accepts directories' do
|
220
|
+
this {
|
221
|
+
subject.run s 'new not-a-dir'
|
222
|
+
}.must_output ""
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'should be able to do this' do
|
228
|
+
this {
|
229
|
+
r = subject.run s('-v new --type post ~/my_site --no-auto arg arg2')
|
230
|
+
r.args.must_equal %w(arg arg2)
|
231
|
+
r.to_h.must_equal :something => [], :verbose => true,
|
232
|
+
:new => {:something => [], :type => :post}, :auto => false
|
233
|
+
}.must_output "Creating post in ~/my_site\n"
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'should be able to do combined short switches' do
|
237
|
+
r = subject.run s '-vas 2.45'
|
238
|
+
|
239
|
+
r.to_h.must_equal :something => [], :verbose => true, :auto => true, :size => 2.45
|
240
|
+
|
241
|
+
this {
|
242
|
+
subject.run %w(-vsa 2.45)
|
243
|
+
}.must_raise Clive::Parser::MissingArgumentError
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + '/..'
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
describe Clive::Argument::AlwaysTrue do
|
5
|
+
subject { Clive::Argument::AlwaysTrue }
|
6
|
+
|
7
|
+
it 'is always true for the method given' do
|
8
|
+
subject.for(:hey).hey.must_be_true
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'is always true for the methods given' do
|
12
|
+
a = subject.for(:one, :two, :three)
|
13
|
+
a.one.must_be_true
|
14
|
+
a.two.must_be_true
|
15
|
+
a.three.must_be_true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Clive::Argument do
|
20
|
+
subject { Clive::Argument }
|
21
|
+
|
22
|
+
describe '#initialize' do
|
23
|
+
it 'converts name to Symbol' do
|
24
|
+
subject.new('arg').name.must_be_kind_of Symbol
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'calls #to_proc on a Symbol constraint' do
|
28
|
+
c = mock
|
29
|
+
c.expects(:respond_to?).with(:to_proc).returns(true)
|
30
|
+
c.expects(:to_proc)
|
31
|
+
|
32
|
+
subject.new :a, :constraint => c
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'merges given options with DEFAULTS' do
|
36
|
+
opts = {:optional => true}
|
37
|
+
Clive::Argument::DEFAULTS.expects(:merge).with(opts).returns({})
|
38
|
+
subject.new('arg', opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'finds the correct type class' do
|
42
|
+
subject.new(:a, :type => String).type.must_equal Clive::Type::String
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'uses the class passed if type cannot be found' do
|
46
|
+
type = Class.new
|
47
|
+
subject.new(:a, :type => type).type.must_equal type
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#optional?' do
|
52
|
+
it 'is true if the argument is optional' do
|
53
|
+
subject.new(:arg, :optional => true).must_be :optional?
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'is false if the argument is not optional' do
|
57
|
+
subject.new(:arg, :optional => false).wont_be :optional?
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'is false by default' do
|
61
|
+
subject.new(:arg).wont_be :optional?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#to_s' do
|
66
|
+
it 'surrounds the name by < and >' do
|
67
|
+
subject.new(:a).to_s.must_equal '<a>'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'surrounds optional arguments with [ and ]' do
|
71
|
+
subject.new(:a, :optional => true).to_s.must_equal '[<a>]'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#choice_str' do
|
76
|
+
it 'returns the array of values allowed' do
|
77
|
+
subject.new(:a, :within => %w(a b c)).choice_str.must_equal '(a, b, c)'
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'returns the range of values allowed' do
|
81
|
+
subject.new(:a, :within => 1..5).choice_str.must_equal '(1..5)'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#possible?' do
|
86
|
+
describe 'for @type' do
|
87
|
+
subject { Clive::Argument.new :a, :type => Clive::Type::Time }
|
88
|
+
|
89
|
+
it 'is true for correct string values' do
|
90
|
+
subject.must_be :possible?, '12:34'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'is true for objects of type' do
|
94
|
+
subject.must_be :possible?, Time.parse('12:34')
|
95
|
+
end
|
96
|
+
|
97
|
+
unless RUBY_VERSION == '1.8.7' # No big problem so just ignore
|
98
|
+
it 'is false for incorrect values' do
|
99
|
+
subject.wont_be :possible?, 'not-a-time'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'for @match' do
|
105
|
+
subject { Clive::Argument.new :a, :match => /^[a-e]+![f-o]+\?.$/ }
|
106
|
+
|
107
|
+
it 'is true for matching values' do
|
108
|
+
subject.must_be :possible?, 'abe!off?.'
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'is false for non-matching values' do
|
112
|
+
subject.wont_be :possible?, 'off?abe!.'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'for @within' do
|
117
|
+
subject { Clive::Argument.new :a, :within => %w(dog cat fish) }
|
118
|
+
|
119
|
+
it 'is true for elements included in the collection' do
|
120
|
+
subject.must_be :possible?, 'dog'
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'is false for elements not in the collection' do
|
124
|
+
subject.wont_be :possible?, 'mouse'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'for @constraint' do
|
129
|
+
subject { Clive::Argument.new :a, :constraint => proc {|i| i.size == 5 } }
|
130
|
+
|
131
|
+
it 'is true if the proc returns true' do
|
132
|
+
subject.must_be :possible?, 'abcde'
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'is false if the proc returns false' do
|
136
|
+
subject.wont_be :possible?, 'abcd'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#coerce' do
|
142
|
+
it 'uses @type to return the correct object' do
|
143
|
+
type = mock
|
144
|
+
type.expects(:typecast).with('str').returns(5)
|
145
|
+
subject.new(:a, :type => type).coerce("str").must_equal 5
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|