contracts 0.5 → 0.6
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -0
- data/TUTORIAL.md +42 -1
- data/benchmarks/bench.rb +3 -3
- data/contracts.gemspec +0 -1
- data/lib/contracts.rb +65 -54
- data/lib/contracts/core_ext.rb +15 -0
- data/lib/contracts/decorators.rb +78 -46
- data/lib/contracts/eigenclass.rb +41 -0
- data/lib/contracts/errors.rb +65 -0
- data/lib/contracts/invariants.rb +0 -6
- data/lib/contracts/method_reference.rb +75 -0
- data/lib/contracts/modules.rb +17 -0
- data/lib/contracts/support.rb +16 -0
- data/lib/contracts/version.rb +1 -1
- data/spec/builtin_contracts_spec.rb +3 -5
- data/spec/contracts_spec.rb +193 -5
- data/spec/fixtures/fixtures.rb +244 -113
- data/spec/module_spec.rb +2 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support.rb +6 -0
- metadata +8 -2
data/spec/fixtures/fixtures.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
include Contracts
|
2
|
-
|
3
1
|
class A
|
2
|
+
include Contracts
|
4
3
|
|
5
4
|
Contract Num => Num
|
6
5
|
def self.a_class_method x
|
@@ -28,6 +27,8 @@ class A
|
|
28
27
|
end
|
29
28
|
|
30
29
|
class B
|
30
|
+
include Contracts
|
31
|
+
|
31
32
|
def bad
|
32
33
|
false
|
33
34
|
end
|
@@ -39,6 +40,8 @@ class B
|
|
39
40
|
end
|
40
41
|
|
41
42
|
class C
|
43
|
+
include Contracts
|
44
|
+
|
42
45
|
def good
|
43
46
|
false
|
44
47
|
end
|
@@ -47,146 +50,176 @@ class C
|
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
def Object.a_class_method x
|
53
|
-
x + 1
|
54
|
-
end
|
53
|
+
class GenericExample
|
54
|
+
include Contracts
|
55
55
|
|
56
|
-
Contract Num =>
|
57
|
-
def
|
58
|
-
|
59
|
-
end
|
56
|
+
Contract Num => Num
|
57
|
+
def GenericExample.a_class_method x
|
58
|
+
x + 1
|
59
|
+
end
|
60
60
|
|
61
|
-
Contract Num =>
|
62
|
-
def
|
63
|
-
|
64
|
-
end
|
61
|
+
Contract Num => nil
|
62
|
+
def bad_double(x)
|
63
|
+
x * 2
|
64
|
+
end
|
65
65
|
|
66
|
-
Contract
|
67
|
-
def
|
68
|
-
|
66
|
+
Contract Num => Num
|
67
|
+
def double(x)
|
68
|
+
x * 2
|
69
|
+
end
|
69
70
|
|
70
|
-
Contract
|
71
|
-
def
|
72
|
-
|
73
|
-
end
|
71
|
+
Contract String => nil
|
72
|
+
def hello(name)
|
73
|
+
end
|
74
74
|
|
75
|
-
Contract
|
76
|
-
def
|
77
|
-
|
78
|
-
acc + x
|
75
|
+
Contract lambda { |x| x.is_a? Numeric } => Num
|
76
|
+
def square(x)
|
77
|
+
x ** 2
|
79
78
|
end
|
80
|
-
end
|
81
79
|
|
82
|
-
Contract
|
83
|
-
def
|
84
|
-
|
80
|
+
Contract [Num, Num, Num] => Num
|
81
|
+
def sum_three(vals)
|
82
|
+
vals.inject(0) do |acc, x|
|
83
|
+
acc + x
|
84
|
+
end
|
85
|
+
end
|
85
86
|
|
86
|
-
Contract
|
87
|
-
def
|
88
|
-
|
89
|
-
end
|
87
|
+
Contract ({:name => String, :age => Fixnum}) => nil
|
88
|
+
def person(data)
|
89
|
+
end
|
90
90
|
|
91
|
-
Contract
|
92
|
-
def
|
93
|
-
|
94
|
-
acc + val
|
91
|
+
Contract Proc => Any
|
92
|
+
def do_call(&blk)
|
93
|
+
blk.call
|
95
94
|
end
|
96
|
-
end
|
97
95
|
|
98
|
-
Contract
|
99
|
-
def
|
100
|
-
|
96
|
+
Contract Args[Num] => Num
|
97
|
+
def sum(*vals)
|
98
|
+
vals.inject(0) do |acc, val|
|
99
|
+
acc + val
|
100
|
+
end
|
101
|
+
end
|
101
102
|
|
102
|
-
Contract
|
103
|
-
def
|
104
|
-
|
103
|
+
Contract Args[Num], Proc => Num
|
104
|
+
def with_partial_sums(*vals, &blk)
|
105
|
+
sum = vals.inject(0) do |acc, val|
|
106
|
+
blk[acc]
|
107
|
+
acc + val
|
108
|
+
end
|
109
|
+
blk[sum]
|
110
|
+
end
|
105
111
|
|
106
|
-
Contract
|
107
|
-
def
|
108
|
-
|
112
|
+
Contract Args[Num], Func[Num => Num] => Num
|
113
|
+
def with_partial_sums_contracted(*vals, &blk)
|
114
|
+
sum = vals.inject(0) do |acc, val|
|
115
|
+
blk[acc]
|
116
|
+
acc + val
|
117
|
+
end
|
118
|
+
blk[sum]
|
119
|
+
end
|
109
120
|
|
110
|
-
Contract
|
111
|
-
def
|
112
|
-
|
121
|
+
Contract Num, Proc => nil
|
122
|
+
def double_with_proc(x, &blk)
|
123
|
+
blk.call(x * 2)
|
124
|
+
nil
|
125
|
+
end
|
113
126
|
|
114
|
-
Contract
|
115
|
-
def
|
116
|
-
end
|
127
|
+
Contract Pos => nil
|
128
|
+
def pos_test(x)
|
129
|
+
end
|
117
130
|
|
118
|
-
Contract
|
119
|
-
def
|
120
|
-
end
|
131
|
+
Contract Neg => nil
|
132
|
+
def neg_test(x)
|
133
|
+
end
|
121
134
|
|
122
|
-
Contract
|
123
|
-
def
|
124
|
-
end
|
135
|
+
Contract Any => nil
|
136
|
+
def show(x)
|
137
|
+
end
|
125
138
|
|
126
|
-
Contract
|
127
|
-
def
|
128
|
-
end
|
139
|
+
Contract None => nil
|
140
|
+
def fail_all(x)
|
141
|
+
end
|
129
142
|
|
130
|
-
Contract
|
131
|
-
def
|
132
|
-
end
|
143
|
+
Contract Or[Num, String] => nil
|
144
|
+
def num_or_string(x)
|
145
|
+
end
|
133
146
|
|
134
|
-
Contract
|
135
|
-
def
|
136
|
-
end
|
147
|
+
Contract Xor[RespondTo[:good], RespondTo[:bad]] => nil
|
148
|
+
def xor_test(x)
|
149
|
+
end
|
137
150
|
|
138
|
-
Contract
|
139
|
-
def
|
140
|
-
vals.inject(1) do |acc, x|
|
141
|
-
acc * x
|
151
|
+
Contract And[A, RespondTo[:good]] => nil
|
152
|
+
def and_test(x)
|
142
153
|
end
|
143
|
-
end
|
144
154
|
|
145
|
-
Contract
|
146
|
-
def
|
147
|
-
end
|
155
|
+
Contract RespondTo[:good] => nil
|
156
|
+
def responds_test(x)
|
157
|
+
end
|
148
158
|
|
149
|
-
Contract
|
150
|
-
def
|
151
|
-
|
152
|
-
end
|
159
|
+
Contract Send[:good] => nil
|
160
|
+
def send_test(x)
|
161
|
+
end
|
153
162
|
|
154
|
-
Contract
|
155
|
-
def
|
156
|
-
ret = []
|
157
|
-
arr.each do |x|
|
158
|
-
ret << func[x]
|
163
|
+
Contract Not[nil] => nil
|
164
|
+
def not_nil(x)
|
159
165
|
end
|
160
|
-
ret
|
161
|
-
end
|
162
166
|
|
163
|
-
Contract Num => Num
|
164
|
-
def
|
165
|
-
|
166
|
-
|
167
|
+
Contract ArrayOf[Num] => Num
|
168
|
+
def product(vals)
|
169
|
+
vals.inject(1) do |acc, x|
|
170
|
+
acc * x
|
171
|
+
end
|
172
|
+
end
|
167
173
|
|
168
|
-
Contract
|
169
|
-
def
|
170
|
-
if x.nil?
|
171
|
-
nil
|
172
|
-
else
|
173
|
-
x * 2
|
174
|
+
Contract Bool => nil
|
175
|
+
def bool_test(x)
|
174
176
|
end
|
175
|
-
end
|
176
177
|
|
177
|
-
Contract
|
178
|
-
def
|
179
|
-
|
180
|
-
end
|
178
|
+
Contract nil => Num
|
179
|
+
def no_args
|
180
|
+
1
|
181
|
+
end
|
182
|
+
|
183
|
+
Contract ArrayOf[Num], Func[Num => Num] => ArrayOf[Num]
|
184
|
+
def map(arr, func)
|
185
|
+
ret = []
|
186
|
+
arr.each do |x|
|
187
|
+
ret << func[x]
|
188
|
+
end
|
189
|
+
ret
|
190
|
+
end
|
191
|
+
|
192
|
+
Contract Num => Num
|
193
|
+
def default_args(x = 1)
|
194
|
+
2
|
195
|
+
end
|
196
|
+
|
197
|
+
Contract Maybe[Num] => Maybe[Num]
|
198
|
+
def maybe_double x
|
199
|
+
if x.nil?
|
200
|
+
nil
|
201
|
+
else
|
202
|
+
x * 2
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
Contract HashOf[Symbol, Num] => Num
|
207
|
+
def gives_max_value(hash)
|
208
|
+
hash.values.max
|
209
|
+
end
|
210
|
+
|
211
|
+
Contract nil => String
|
212
|
+
def a_private_method
|
213
|
+
"works"
|
214
|
+
end
|
215
|
+
private :a_private_method
|
181
216
|
|
182
|
-
Contract nil => String
|
183
|
-
def a_private_method
|
184
|
-
"works"
|
185
217
|
end
|
186
|
-
private :a_private_method
|
187
218
|
|
188
219
|
# for testing inheritance
|
189
220
|
class Parent
|
221
|
+
include Contracts
|
222
|
+
|
190
223
|
Contract Num => Num
|
191
224
|
def double x
|
192
225
|
x * 2
|
@@ -196,17 +229,21 @@ end
|
|
196
229
|
class Child < Parent
|
197
230
|
end
|
198
231
|
|
199
|
-
|
200
|
-
|
201
|
-
a
|
202
|
-
|
232
|
+
class GenericExample
|
233
|
+
Contract Parent => Parent
|
234
|
+
def id_ a
|
235
|
+
a
|
236
|
+
end
|
203
237
|
|
204
|
-
Contract Exactly[Parent] => nil
|
205
|
-
def exactly_test(x)
|
238
|
+
Contract Exactly[Parent] => nil
|
239
|
+
def exactly_test(x)
|
240
|
+
end
|
206
241
|
end
|
207
242
|
|
208
243
|
# pattern matching example with possible deep contract violation
|
209
244
|
class PatternMatchingExample
|
245
|
+
include Contracts
|
246
|
+
|
210
247
|
class Success < Struct.new(:request)
|
211
248
|
end
|
212
249
|
|
@@ -274,3 +311,97 @@ class MyBirthday < Struct.new(:day, :month)
|
|
274
311
|
self.day = 1
|
275
312
|
end
|
276
313
|
end
|
314
|
+
|
315
|
+
class SingletonClassExample
|
316
|
+
# This turned out to be required line here to make singleton classes
|
317
|
+
# work properly under all platforms. Not sure if it worth trying to
|
318
|
+
# do something with it.
|
319
|
+
include Contracts
|
320
|
+
|
321
|
+
class << self
|
322
|
+
Contract String => String
|
323
|
+
def hoge(str)
|
324
|
+
"super#{str}"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
with_enabled_no_contracts do
|
330
|
+
class NoContractsSimpleExample
|
331
|
+
include Contracts
|
332
|
+
|
333
|
+
Contract String => nil
|
334
|
+
def some_method(x)
|
335
|
+
nil
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
class NoContractsInvariantsExample
|
340
|
+
include Contracts
|
341
|
+
include Contracts::Invariants
|
342
|
+
|
343
|
+
attr_accessor :day
|
344
|
+
|
345
|
+
Invariant(:day_rule) { 1 <= day && day <= 7 }
|
346
|
+
|
347
|
+
Contract None => nil
|
348
|
+
def next_day
|
349
|
+
self.day += 1
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
class NoContractsPatternMatchingExample
|
354
|
+
include Contracts
|
355
|
+
|
356
|
+
Contract 200, String => String
|
357
|
+
def on_response(status, body)
|
358
|
+
body + "!"
|
359
|
+
end
|
360
|
+
|
361
|
+
Contract Fixnum, String => String
|
362
|
+
def on_response(status, body)
|
363
|
+
"error #{status}: #{body}"
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
module ModuleExample
|
369
|
+
# This inclusion is required to actually override `method_added`
|
370
|
+
# hooks for module.
|
371
|
+
include Contracts
|
372
|
+
include Contracts::Modules
|
373
|
+
|
374
|
+
Contract Num, Num => Num
|
375
|
+
def plus(a, b)
|
376
|
+
a + b
|
377
|
+
end
|
378
|
+
|
379
|
+
Contract String => String
|
380
|
+
def self.hoge(str)
|
381
|
+
"super#{str}"
|
382
|
+
end
|
383
|
+
|
384
|
+
class << self
|
385
|
+
Contract String => nil
|
386
|
+
def eat(food)
|
387
|
+
# yummy
|
388
|
+
nil
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
class KlassWithModuleExample
|
394
|
+
include ModuleExample
|
395
|
+
end
|
396
|
+
|
397
|
+
class SingletonInheritanceExample
|
398
|
+
include Contracts
|
399
|
+
|
400
|
+
Contract Any => Any
|
401
|
+
def self.a_contracted_self
|
402
|
+
self
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
class SingletonInheritanceExampleSubclass < SingletonInheritanceExample
|
407
|
+
end
|
data/spec/module_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Mod
|
2
2
|
include Contracts
|
3
|
+
|
3
4
|
Contract Num => Num
|
4
5
|
def self.a_module_method a
|
5
6
|
a + 1
|
@@ -12,6 +13,6 @@ RSpec.describe "module methods" do
|
|
12
13
|
end
|
13
14
|
|
14
15
|
it "should fail for incorrect input" do
|
15
|
-
expect { Mod.a_module_method("bad") }.to raise_error
|
16
|
+
expect { Mod.a_module_method("bad") }.to raise_error(ContractError)
|
16
17
|
end
|
17
18
|
end
|