clive 0.8.1 → 1.0.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/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
|