ruby-contract 0.1.1
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/COPYING +56 -0
- data/Manifest +85 -0
- data/README +32 -0
- data/TODO +83 -0
- data/doc/classes/Contract.html +599 -0
- data/doc/classes/Contract/Check.html +229 -0
- data/doc/classes/Contract/Check/All.html +172 -0
- data/doc/classes/Contract/Check/Any.html +172 -0
- data/doc/classes/Contract/Check/Block.html +172 -0
- data/doc/classes/Contract/Check/None.html +173 -0
- data/doc/classes/Contract/Check/Quack.html +172 -0
- data/doc/classes/Contract/ContractError.html +151 -0
- data/doc/classes/Contract/ContractException.html +162 -0
- data/doc/classes/Contract/ContractMismatch.html +134 -0
- data/doc/classes/Kernel.html +256 -0
- data/doc/classes/Method.html +135 -0
- data/doc/classes/MethodSignatureMixin.html +208 -0
- data/doc/classes/Module.html +526 -0
- data/doc/created.rid +1 -0
- data/doc/dot/f_0.dot +14 -0
- data/doc/dot/f_0.png +0 -0
- data/doc/dot/f_1.dot +14 -0
- data/doc/dot/f_1.png +0 -0
- data/doc/dot/f_2.dot +14 -0
- data/doc/dot/f_2.png +0 -0
- data/doc/dot/f_3.dot +112 -0
- data/doc/dot/f_3.png +0 -0
- data/doc/dot/f_4.dot +62 -0
- data/doc/dot/f_4.png +0 -0
- data/doc/dot/f_5.dot +62 -0
- data/doc/dot/f_5.png +0 -0
- data/doc/dot/f_6.dot +224 -0
- data/doc/dot/f_6.png +0 -0
- data/doc/dot/f_6_0.dot +24 -0
- data/doc/dot/f_6_0.png +0 -0
- data/doc/dot/f_6_1.dot +24 -0
- data/doc/dot/f_6_1.png +0 -0
- data/doc/dot/f_7.dot +62 -0
- data/doc/dot/f_7.png +0 -0
- data/doc/files/COPYING.html +168 -0
- data/doc/files/README.html +146 -0
- data/doc/files/TODO.html +240 -0
- data/doc/files/lib/contract/assertions_rb.html +118 -0
- data/doc/files/lib/contract/exception_rb.html +125 -0
- data/doc/files/lib/contract/integration_rb.html +130 -0
- data/doc/files/lib/contract/overrides_rb.html +118 -0
- data/doc/files/lib/contract_rb.html +127 -0
- data/doc/fr_class_index.html +40 -0
- data/doc/fr_file_index.html +34 -0
- data/doc/fr_method_index.html +45 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/lib/contract.rb +146 -0
- data/lib/contract/assertions.rb +42 -0
- data/lib/contract/exception.rb +95 -0
- data/lib/contract/integration.rb +664 -0
- data/lib/contract/overrides.rb +41 -0
- data/setup.rb +1360 -0
- data/test/coverage/_-lib-contract-assertions_rb.html +526 -0
- data/test/coverage/_-lib-contract-exception_rb.html +632 -0
- data/test/coverage/_-lib-contract-integration_rb.html +1450 -0
- data/test/coverage/_-lib-contract-overrides_rb.html +524 -0
- data/test/coverage/_-lib-contract_rb.html +724 -0
- data/test/coverage/__-lib-contract-assertions_rb.html +484 -0
- data/test/coverage/__-lib-contract-exception_rb.html +537 -0
- data/test/coverage/__-lib-contract-integration_rb.html +946 -0
- data/test/coverage/__-lib-contract-overrides_rb.html +483 -0
- data/test/coverage/__-lib-contract_rb.html +583 -0
- data/test/coverage/index.html +93 -0
- data/test/tc_all.rb +8 -0
- data/test/tc_contract.rb +109 -0
- data/test/tc_exception.rb +43 -0
- data/test/tc_integration.rb +357 -0
- metadata +136 -0
@@ -0,0 +1,1450 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-15"?>
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
5
|
+
<head><title>./lib/contract/integration.rb - coverage</title>
|
6
|
+
<style type="text/css">body {
|
7
|
+
background-color: rgb(180, 180, 180);
|
8
|
+
}
|
9
|
+
span.marked {
|
10
|
+
background-color: rgb(185, 200, 200);
|
11
|
+
display: block;
|
12
|
+
}
|
13
|
+
span.inferred {
|
14
|
+
background-color: rgb(180, 195, 195);
|
15
|
+
display: block;
|
16
|
+
}
|
17
|
+
span.run0 {
|
18
|
+
background-color: rgb(178, 204, 255);
|
19
|
+
display: block;
|
20
|
+
}
|
21
|
+
span.run1 {
|
22
|
+
background-color: rgb(178, 206, 255);
|
23
|
+
display: block;
|
24
|
+
}
|
25
|
+
span.run2 {
|
26
|
+
background-color: rgb(178, 209, 255);
|
27
|
+
display: block;
|
28
|
+
}
|
29
|
+
span.run3 {
|
30
|
+
background-color: rgb(178, 211, 255);
|
31
|
+
display: block;
|
32
|
+
}
|
33
|
+
span.run4 {
|
34
|
+
background-color: rgb(178, 214, 255);
|
35
|
+
display: block;
|
36
|
+
}
|
37
|
+
span.run5 {
|
38
|
+
background-color: rgb(178, 218, 255);
|
39
|
+
display: block;
|
40
|
+
}
|
41
|
+
span.run6 {
|
42
|
+
background-color: rgb(178, 220, 255);
|
43
|
+
display: block;
|
44
|
+
}
|
45
|
+
span.run7 {
|
46
|
+
background-color: rgb(178, 223, 255);
|
47
|
+
display: block;
|
48
|
+
}
|
49
|
+
span.run8 {
|
50
|
+
background-color: rgb(178, 225, 255);
|
51
|
+
display: block;
|
52
|
+
}
|
53
|
+
span.run9 {
|
54
|
+
background-color: rgb(178, 228, 255);
|
55
|
+
display: block;
|
56
|
+
}
|
57
|
+
span.run10 {
|
58
|
+
background-color: rgb(178, 232, 255);
|
59
|
+
display: block;
|
60
|
+
}
|
61
|
+
span.run11 {
|
62
|
+
background-color: rgb(178, 234, 255);
|
63
|
+
display: block;
|
64
|
+
}
|
65
|
+
span.run12 {
|
66
|
+
background-color: rgb(178, 237, 255);
|
67
|
+
display: block;
|
68
|
+
}
|
69
|
+
span.run13 {
|
70
|
+
background-color: rgb(178, 239, 255);
|
71
|
+
display: block;
|
72
|
+
}
|
73
|
+
span.run14 {
|
74
|
+
background-color: rgb(178, 242, 255);
|
75
|
+
display: block;
|
76
|
+
}
|
77
|
+
span.run15 {
|
78
|
+
background-color: rgb(178, 246, 255);
|
79
|
+
display: block;
|
80
|
+
}
|
81
|
+
span.run16 {
|
82
|
+
background-color: rgb(178, 248, 255);
|
83
|
+
display: block;
|
84
|
+
}
|
85
|
+
span.run17 {
|
86
|
+
background-color: rgb(178, 251, 255);
|
87
|
+
display: block;
|
88
|
+
}
|
89
|
+
span.run18 {
|
90
|
+
background-color: rgb(178, 253, 255);
|
91
|
+
display: block;
|
92
|
+
}
|
93
|
+
span.run19 {
|
94
|
+
background-color: rgb(178, 255, 253);
|
95
|
+
display: block;
|
96
|
+
}
|
97
|
+
span.run20 {
|
98
|
+
background-color: rgb(178, 255, 249);
|
99
|
+
display: block;
|
100
|
+
}
|
101
|
+
span.run21 {
|
102
|
+
background-color: rgb(178, 255, 247);
|
103
|
+
display: block;
|
104
|
+
}
|
105
|
+
span.run22 {
|
106
|
+
background-color: rgb(178, 255, 244);
|
107
|
+
display: block;
|
108
|
+
}
|
109
|
+
span.run23 {
|
110
|
+
background-color: rgb(178, 255, 242);
|
111
|
+
display: block;
|
112
|
+
}
|
113
|
+
span.run24 {
|
114
|
+
background-color: rgb(178, 255, 239);
|
115
|
+
display: block;
|
116
|
+
}
|
117
|
+
span.run25 {
|
118
|
+
background-color: rgb(178, 255, 235);
|
119
|
+
display: block;
|
120
|
+
}
|
121
|
+
span.run26 {
|
122
|
+
background-color: rgb(178, 255, 233);
|
123
|
+
display: block;
|
124
|
+
}
|
125
|
+
span.run27 {
|
126
|
+
background-color: rgb(178, 255, 230);
|
127
|
+
display: block;
|
128
|
+
}
|
129
|
+
span.run28 {
|
130
|
+
background-color: rgb(178, 255, 228);
|
131
|
+
display: block;
|
132
|
+
}
|
133
|
+
span.run29 {
|
134
|
+
background-color: rgb(178, 255, 225);
|
135
|
+
display: block;
|
136
|
+
}
|
137
|
+
span.run30 {
|
138
|
+
background-color: rgb(178, 255, 221);
|
139
|
+
display: block;
|
140
|
+
}
|
141
|
+
span.run31 {
|
142
|
+
background-color: rgb(178, 255, 219);
|
143
|
+
display: block;
|
144
|
+
}
|
145
|
+
span.run32 {
|
146
|
+
background-color: rgb(178, 255, 216);
|
147
|
+
display: block;
|
148
|
+
}
|
149
|
+
span.run33 {
|
150
|
+
background-color: rgb(178, 255, 214);
|
151
|
+
display: block;
|
152
|
+
}
|
153
|
+
span.run34 {
|
154
|
+
background-color: rgb(178, 255, 211);
|
155
|
+
display: block;
|
156
|
+
}
|
157
|
+
span.run35 {
|
158
|
+
background-color: rgb(178, 255, 207);
|
159
|
+
display: block;
|
160
|
+
}
|
161
|
+
span.run36 {
|
162
|
+
background-color: rgb(178, 255, 205);
|
163
|
+
display: block;
|
164
|
+
}
|
165
|
+
span.run37 {
|
166
|
+
background-color: rgb(178, 255, 202);
|
167
|
+
display: block;
|
168
|
+
}
|
169
|
+
span.run38 {
|
170
|
+
background-color: rgb(178, 255, 200);
|
171
|
+
display: block;
|
172
|
+
}
|
173
|
+
span.run39 {
|
174
|
+
background-color: rgb(178, 255, 197);
|
175
|
+
display: block;
|
176
|
+
}
|
177
|
+
span.run40 {
|
178
|
+
background-color: rgb(178, 255, 193);
|
179
|
+
display: block;
|
180
|
+
}
|
181
|
+
span.run41 {
|
182
|
+
background-color: rgb(178, 255, 191);
|
183
|
+
display: block;
|
184
|
+
}
|
185
|
+
span.run42 {
|
186
|
+
background-color: rgb(178, 255, 188);
|
187
|
+
display: block;
|
188
|
+
}
|
189
|
+
span.run43 {
|
190
|
+
background-color: rgb(178, 255, 186);
|
191
|
+
display: block;
|
192
|
+
}
|
193
|
+
span.run44 {
|
194
|
+
background-color: rgb(178, 255, 183);
|
195
|
+
display: block;
|
196
|
+
}
|
197
|
+
span.run45 {
|
198
|
+
background-color: rgb(178, 255, 179);
|
199
|
+
display: block;
|
200
|
+
}
|
201
|
+
span.run46 {
|
202
|
+
background-color: rgb(179, 255, 178);
|
203
|
+
display: block;
|
204
|
+
}
|
205
|
+
span.run47 {
|
206
|
+
background-color: rgb(182, 255, 178);
|
207
|
+
display: block;
|
208
|
+
}
|
209
|
+
span.run48 {
|
210
|
+
background-color: rgb(184, 255, 178);
|
211
|
+
display: block;
|
212
|
+
}
|
213
|
+
span.run49 {
|
214
|
+
background-color: rgb(187, 255, 178);
|
215
|
+
display: block;
|
216
|
+
}
|
217
|
+
span.run50 {
|
218
|
+
background-color: rgb(191, 255, 178);
|
219
|
+
display: block;
|
220
|
+
}
|
221
|
+
span.run51 {
|
222
|
+
background-color: rgb(193, 255, 178);
|
223
|
+
display: block;
|
224
|
+
}
|
225
|
+
span.run52 {
|
226
|
+
background-color: rgb(196, 255, 178);
|
227
|
+
display: block;
|
228
|
+
}
|
229
|
+
span.run53 {
|
230
|
+
background-color: rgb(198, 255, 178);
|
231
|
+
display: block;
|
232
|
+
}
|
233
|
+
span.run54 {
|
234
|
+
background-color: rgb(201, 255, 178);
|
235
|
+
display: block;
|
236
|
+
}
|
237
|
+
span.run55 {
|
238
|
+
background-color: rgb(205, 255, 178);
|
239
|
+
display: block;
|
240
|
+
}
|
241
|
+
span.run56 {
|
242
|
+
background-color: rgb(207, 255, 178);
|
243
|
+
display: block;
|
244
|
+
}
|
245
|
+
span.run57 {
|
246
|
+
background-color: rgb(210, 255, 178);
|
247
|
+
display: block;
|
248
|
+
}
|
249
|
+
span.run58 {
|
250
|
+
background-color: rgb(212, 255, 178);
|
251
|
+
display: block;
|
252
|
+
}
|
253
|
+
span.run59 {
|
254
|
+
background-color: rgb(215, 255, 178);
|
255
|
+
display: block;
|
256
|
+
}
|
257
|
+
span.run60 {
|
258
|
+
background-color: rgb(219, 255, 178);
|
259
|
+
display: block;
|
260
|
+
}
|
261
|
+
span.run61 {
|
262
|
+
background-color: rgb(221, 255, 178);
|
263
|
+
display: block;
|
264
|
+
}
|
265
|
+
span.run62 {
|
266
|
+
background-color: rgb(224, 255, 178);
|
267
|
+
display: block;
|
268
|
+
}
|
269
|
+
span.run63 {
|
270
|
+
background-color: rgb(226, 255, 178);
|
271
|
+
display: block;
|
272
|
+
}
|
273
|
+
span.run64 {
|
274
|
+
background-color: rgb(229, 255, 178);
|
275
|
+
display: block;
|
276
|
+
}
|
277
|
+
span.run65 {
|
278
|
+
background-color: rgb(233, 255, 178);
|
279
|
+
display: block;
|
280
|
+
}
|
281
|
+
span.run66 {
|
282
|
+
background-color: rgb(235, 255, 178);
|
283
|
+
display: block;
|
284
|
+
}
|
285
|
+
span.run67 {
|
286
|
+
background-color: rgb(238, 255, 178);
|
287
|
+
display: block;
|
288
|
+
}
|
289
|
+
span.run68 {
|
290
|
+
background-color: rgb(240, 255, 178);
|
291
|
+
display: block;
|
292
|
+
}
|
293
|
+
span.run69 {
|
294
|
+
background-color: rgb(243, 255, 178);
|
295
|
+
display: block;
|
296
|
+
}
|
297
|
+
span.run70 {
|
298
|
+
background-color: rgb(247, 255, 178);
|
299
|
+
display: block;
|
300
|
+
}
|
301
|
+
span.run71 {
|
302
|
+
background-color: rgb(249, 255, 178);
|
303
|
+
display: block;
|
304
|
+
}
|
305
|
+
span.run72 {
|
306
|
+
background-color: rgb(252, 255, 178);
|
307
|
+
display: block;
|
308
|
+
}
|
309
|
+
span.run73 {
|
310
|
+
background-color: rgb(255, 255, 178);
|
311
|
+
display: block;
|
312
|
+
}
|
313
|
+
span.run74 {
|
314
|
+
background-color: rgb(255, 252, 178);
|
315
|
+
display: block;
|
316
|
+
}
|
317
|
+
span.run75 {
|
318
|
+
background-color: rgb(255, 248, 178);
|
319
|
+
display: block;
|
320
|
+
}
|
321
|
+
span.run76 {
|
322
|
+
background-color: rgb(255, 246, 178);
|
323
|
+
display: block;
|
324
|
+
}
|
325
|
+
span.run77 {
|
326
|
+
background-color: rgb(255, 243, 178);
|
327
|
+
display: block;
|
328
|
+
}
|
329
|
+
span.run78 {
|
330
|
+
background-color: rgb(255, 240, 178);
|
331
|
+
display: block;
|
332
|
+
}
|
333
|
+
span.run79 {
|
334
|
+
background-color: rgb(255, 238, 178);
|
335
|
+
display: block;
|
336
|
+
}
|
337
|
+
span.run80 {
|
338
|
+
background-color: rgb(255, 234, 178);
|
339
|
+
display: block;
|
340
|
+
}
|
341
|
+
span.run81 {
|
342
|
+
background-color: rgb(255, 232, 178);
|
343
|
+
display: block;
|
344
|
+
}
|
345
|
+
span.run82 {
|
346
|
+
background-color: rgb(255, 229, 178);
|
347
|
+
display: block;
|
348
|
+
}
|
349
|
+
span.run83 {
|
350
|
+
background-color: rgb(255, 226, 178);
|
351
|
+
display: block;
|
352
|
+
}
|
353
|
+
span.run84 {
|
354
|
+
background-color: rgb(255, 224, 178);
|
355
|
+
display: block;
|
356
|
+
}
|
357
|
+
span.run85 {
|
358
|
+
background-color: rgb(255, 220, 178);
|
359
|
+
display: block;
|
360
|
+
}
|
361
|
+
span.run86 {
|
362
|
+
background-color: rgb(255, 218, 178);
|
363
|
+
display: block;
|
364
|
+
}
|
365
|
+
span.run87 {
|
366
|
+
background-color: rgb(255, 215, 178);
|
367
|
+
display: block;
|
368
|
+
}
|
369
|
+
span.run88 {
|
370
|
+
background-color: rgb(255, 212, 178);
|
371
|
+
display: block;
|
372
|
+
}
|
373
|
+
span.run89 {
|
374
|
+
background-color: rgb(255, 210, 178);
|
375
|
+
display: block;
|
376
|
+
}
|
377
|
+
span.run90 {
|
378
|
+
background-color: rgb(255, 206, 178);
|
379
|
+
display: block;
|
380
|
+
}
|
381
|
+
span.run91 {
|
382
|
+
background-color: rgb(255, 204, 178);
|
383
|
+
display: block;
|
384
|
+
}
|
385
|
+
span.run92 {
|
386
|
+
background-color: rgb(255, 201, 178);
|
387
|
+
display: block;
|
388
|
+
}
|
389
|
+
span.run93 {
|
390
|
+
background-color: rgb(255, 198, 178);
|
391
|
+
display: block;
|
392
|
+
}
|
393
|
+
span.run94 {
|
394
|
+
background-color: rgb(255, 196, 178);
|
395
|
+
display: block;
|
396
|
+
}
|
397
|
+
span.run95 {
|
398
|
+
background-color: rgb(255, 192, 178);
|
399
|
+
display: block;
|
400
|
+
}
|
401
|
+
span.run96 {
|
402
|
+
background-color: rgb(255, 189, 178);
|
403
|
+
display: block;
|
404
|
+
}
|
405
|
+
span.run97 {
|
406
|
+
background-color: rgb(255, 187, 178);
|
407
|
+
display: block;
|
408
|
+
}
|
409
|
+
span.run98 {
|
410
|
+
background-color: rgb(255, 184, 178);
|
411
|
+
display: block;
|
412
|
+
}
|
413
|
+
span.run99 {
|
414
|
+
background-color: rgb(255, 182, 178);
|
415
|
+
display: block;
|
416
|
+
}
|
417
|
+
|
418
|
+
div.overview {
|
419
|
+
border-bottom: 8px solid black;
|
420
|
+
}
|
421
|
+
</style></head>
|
422
|
+
<body><div class="overview">
|
423
|
+
<table>
|
424
|
+
<tr><td>filename</td><td><tt>./lib/contract/integration.rb</tt></td></tr>
|
425
|
+
<tr><td>total coverage</td><td>46.6</td></tr>
|
426
|
+
<tr><td>code coverage</td><td>43.9</td></tr>
|
427
|
+
</table>
|
428
|
+
</div>
|
429
|
+
<pre><span class="inferred"> 1 # This file contains code for integrating the contract library with built-in
|
430
|
+
2 # classes and methods.
|
431
|
+
3 #
|
432
|
+
4 # See Contract::Check, Module#signature and Module#fulfills.
|
433
|
+
5
|
434
|
+
6
|
435
|
+
</span><span class="marked"> 7 class Contract < Test::Unit::TestCase
|
436
|
+
</span><span class="inferred"> 8 # Implements checks that can for example be used in Module#signature
|
437
|
+
9 # specifications. They are implemented simply by overriding the === case
|
438
|
+
10 # equality operator. They can also be nested like this:
|
439
|
+
11 # # Matches something that is an Enumerable and that responds to
|
440
|
+
12 # # either :to_ary or :to_a.
|
441
|
+
13 # signature :x, Contract::Check::All[
|
442
|
+
14 # Enumerable,
|
443
|
+
15 # Contract::Check::Any[
|
444
|
+
16 # Contract::Check::Quack[:to_a],
|
445
|
+
17 # Contract::Check::Quack[:to_ary]
|
446
|
+
18 # ]
|
447
|
+
19 # ]
|
448
|
+
</span><span class="marked"> 20 module Check
|
449
|
+
</span><span class="inferred"> 21 # An abstract Base class for Contract::Check classes.
|
450
|
+
22 # Contains logic for instantation.
|
451
|
+
</span><span class="marked"> 23 class Base # :nodoc:
|
452
|
+
24 class << self
|
453
|
+
25 alias :[] :new
|
454
|
+
</span><span class="inferred"> 26 end
|
455
|
+
27
|
456
|
+
</span><span class="marked"> 28 def initialize(*args, &block)
|
457
|
+
29 @args, @block = args, block
|
458
|
+
</span><span class="inferred"> 30 end
|
459
|
+
31 end
|
460
|
+
32
|
461
|
+
33 # Checks that the specified block matches.
|
462
|
+
34 # Example:
|
463
|
+
35 # signature :x, Contract::Check.block { |arg| arg > 0 }
|
464
|
+
</span><span class="marked"> 36 class Block < Base
|
465
|
+
37 def ===(other)
|
466
|
+
38 @block.call(other)
|
467
|
+
</span><span class="inferred"> 39 end
|
468
|
+
40 end
|
469
|
+
41 # Short-cut for creating a Contract::Check::Block.
|
470
|
+
</span><span class="marked"> 42 def self.block(&block) # :yields: arg
|
471
|
+
43 Block.new(&block)
|
472
|
+
</span><span class="inferred"> 44 end
|
473
|
+
45
|
474
|
+
46 # Checks that all the specified methods are answered.
|
475
|
+
47 # Example:
|
476
|
+
48 # signature :x, Contract::Check::Quack[:to_sym]
|
477
|
+
</span><span class="marked"> 49 class Quack < Base
|
478
|
+
50 def ===(other)
|
479
|
+
51 @args.all? { |arg| other.respond_to?(arg) }
|
480
|
+
</span><span class="inferred"> 52 end
|
481
|
+
53 end
|
482
|
+
54
|
483
|
+
55 # Checks that all the specified conditions match.
|
484
|
+
56 # Example:
|
485
|
+
57 # signature :x, Contract::Check::All[Array, Enumerable]
|
486
|
+
</span><span class="marked"> 58 class All < Base
|
487
|
+
59 def ===(other)
|
488
|
+
60 @args.all? { |arg| arg === other }
|
489
|
+
</span><span class="inferred"> 61 end
|
490
|
+
62 end
|
491
|
+
63 # Alias for Contract::Check::All
|
492
|
+
</span><span class="marked"> 64 And = All unless defined?(And)
|
493
|
+
</span><span class="inferred"> 65
|
494
|
+
66 # Checks that at least one of the specified conditions match.
|
495
|
+
67 # Example:
|
496
|
+
68 # signature :x, Contract::Check::Any[String, Symbol]
|
497
|
+
</span><span class="marked"> 69 class Any < Base
|
498
|
+
70 def ===(other)
|
499
|
+
71 @args.any? { |arg| arg === other }
|
500
|
+
</span><span class="inferred"> 72 end
|
501
|
+
73 end
|
502
|
+
74 # Alias for Contract::Check::Any
|
503
|
+
</span><span class="marked"> 75 Or = Any unless defined?(Or)
|
504
|
+
</span><span class="inferred"> 76
|
505
|
+
77 # Checks that none of the specified conditions match.
|
506
|
+
78 # Example:
|
507
|
+
79 # signature :x, Contract::Check::None[Numeric, Symbol]
|
508
|
+
80 # signature :x, Contract::Check::Not[Comparable]
|
509
|
+
</span><span class="marked"> 81 class None < Base
|
510
|
+
82 def ===(other)
|
511
|
+
83 not @args.any? { |arg| arg === other }
|
512
|
+
</span><span class="inferred"> 84 end
|
513
|
+
85 end
|
514
|
+
86 # Alias for Contract::Check::None
|
515
|
+
</span><span class="marked"> 87 Not = None unless defined?(Not)
|
516
|
+
</span><span class="inferred"> 88 end
|
517
|
+
89
|
518
|
+
</span><span class="marked"> 90 class << self
|
519
|
+
</span><span class="inferred"> 91 # Whether signatures should be checked. By default signatures are checked
|
520
|
+
92 # only when the application is run in $DEBUG mode. (By specifying the -d
|
521
|
+
93 # switch on the invocation of Ruby.)
|
522
|
+
94 #
|
523
|
+
95 # Note: If you want to change this you need to do so before doing any
|
524
|
+
96 # Module#signature calls or it will not be applied. It's probably best
|
525
|
+
97 # set right after requiring the contract library.
|
526
|
+
</span><span class="marked"> 98 attr_accessor :check_signatures
|
527
|
+
99 alias :check_signatures? :check_signatures
|
528
|
+
</span><span class="inferred"> 100
|
529
|
+
101 # Whether fulfills should be checked. This is enabled by default.
|
530
|
+
102 #
|
531
|
+
103 # Note: If you want to change this you need to do so before doing any
|
532
|
+
104 # Module#fulfills calls or it will not be applied. It's probably best
|
533
|
+
105 # set right after requiring the contract library.
|
534
|
+
</span><span class="marked"> 106 attr_accessor :check_fulfills
|
535
|
+
107 alias :check_fulfills? :check_fulfills
|
536
|
+
</span><span class="inferred"> 108
|
537
|
+
109 # All adaption routes.
|
538
|
+
</span><span class="marked"> 110 attr_accessor :adaptions # :nodoc:
|
539
|
+
</span><span class="inferred"> 111 end
|
540
|
+
</span><span class="marked"> 112 self.check_signatures = $DEBUG if self.check_signatures.nil?
|
541
|
+
113 self.check_fulfills = true if self.check_fulfills.nil?
|
542
|
+
114 if self.adaptions.nil? then
|
543
|
+
115 self.adaptions = Hash.new { |hash, key| hash[key] = Array.new }
|
544
|
+
</span><span class="inferred"> 116 end
|
545
|
+
117
|
546
|
+
118 # Tries to adapt the specified object to the specified type.
|
547
|
+
119 # Returns the old object if no suitable adaption route was found or if
|
548
|
+
120 # it already is of the specified type.
|
549
|
+
121 #
|
550
|
+
122 # This will only use adaptions where the :to part is equal to the specified
|
551
|
+
123 # type. No multi-step conversion will be performed.
|
552
|
+
</span><span class="marked"> 124 def self.adapt(object, type)
|
553
|
+
125 return object if type === object
|
554
|
+
</span><span class="inferred"> 126
|
555
|
+
</span><span class="marked"> 127 @adaptions[type].each do |adaption|
|
556
|
+
</span><span class="false"> 128 if adaption[:from] === object and
|
557
|
+
</span><span class="marked"> 129 (adaption[:if].nil? or adaption[:if] === object)
|
558
|
+
</span><span class="false"> 130 then
|
559
|
+
</span><span class="marked"> 131 result = adaption[:via].call(object)
|
560
|
+
132 return result if type === result
|
561
|
+
</span><span class="inferred"> 133 end
|
562
|
+
134 end
|
563
|
+
135
|
564
|
+
</span><span class="marked"> 136 return object
|
565
|
+
</span><span class="inferred"> 137 end
|
566
|
+
138 end
|
567
|
+
139
|
568
|
+
140
|
569
|
+
</span><span class="marked"> 141 class Module
|
570
|
+
</span><span class="inferred"> 142 # Checks that the arguments and return value of a method match the specified
|
571
|
+
143 # signature. Checks are only actually done when Contract.check_signatures is
|
572
|
+
144 # set to true or if the <code>:no_adaption</code> option is +false+. The
|
573
|
+
145 # method will return +true+ in case it actually inserted the signature check
|
574
|
+
146 # logic and +nil+ in case it didn't.
|
575
|
+
147 #
|
576
|
+
148 # You will usually specify one type specifier (<code>:any</code> which will
|
577
|
+
149 # allow anything to appear at that position of the argument list or something
|
578
|
+
150 # that implements the === case equality operator -- samples are Contracts,
|
579
|
+
151 # Ranges, Classes, Modules, Regexps, Contract::Checks and so on) per argument.
|
580
|
+
152 # You can also use objects that implement the +call+ method as type specifiers
|
581
|
+
153 # which includes Methods and Procs.
|
582
|
+
154 #
|
583
|
+
155 # If you don't use the <code>:repeated</code> or <code>:allow_trailing</code>
|
584
|
+
156 # options the method will take exactly as many arguments as there are type
|
585
|
+
157 # specifiers which means that <code>signature :a_method</code> enforces
|
586
|
+
158 # +a_method+ having exactly zero arguments.
|
587
|
+
159 #
|
588
|
+
160 # The checks are done by wrapping the type checks around the method.
|
589
|
+
161 # ArgumentError exceptions will be raised in case the signature contract is
|
590
|
+
162 # not adhered to by your caller.
|
591
|
+
163 #
|
592
|
+
164 # An ArgumentError exception will be raised in case the methods natural
|
593
|
+
165 # argument list size and the signature you specified via Module.signature are
|
594
|
+
166 # incompatible. (Note that they don't have to be completely equivalent, you
|
595
|
+
167 # can still have a method taking zero or more arguments and apply a signature
|
596
|
+
168 # that limits the actual argument count to three arguments.)
|
597
|
+
169 #
|
598
|
+
170 # This method can take quite a few options. Here's a complete list:
|
599
|
+
171 #
|
600
|
+
172 # <code>:return</code>::
|
601
|
+
173 # A return type that the method must comply to. Note that this check (if
|
602
|
+
174 # failed) will actually raise a StandardError instead of an ArgumentError
|
603
|
+
175 # because the failure likely lies in the method itself and not in what the
|
604
|
+
176 # caller did.
|
605
|
+
177 #
|
606
|
+
178 # <code>:block</code>::
|
607
|
+
179 # +true+ or +false+ -- whether the method must take a block or not. So
|
608
|
+
180 # specifying <code>:block => false</code> enforces that the method is not
|
609
|
+
181 # allowed to have a block supplied.
|
610
|
+
182 #
|
611
|
+
183 # <code>:allow_trailing</code>::
|
612
|
+
184 # +true+ or +false+ -- whether the argument list may contain trailing,
|
613
|
+
185 # unchecked arguments.
|
614
|
+
186 #
|
615
|
+
187 # <code>:repeated</code>::
|
616
|
+
188 # An Array that specifies arguments of a method that will be repeated over
|
617
|
+
189 # and over again at the end of the argument list.
|
618
|
+
190 #
|
619
|
+
191 # A good sample of this are Array#values_at which takes zero or or more
|
620
|
+
192 # Numeric arguments and Enumerable#zip which takes zero or more other
|
621
|
+
193 # Enumerable arguments.
|
622
|
+
194 #
|
623
|
+
195 # Note that the Array that was associated with the <code>:repeated</code>
|
624
|
+
196 # option must not be empty or an ArgumentError exception will be raised.
|
625
|
+
197 # If there's just one repeated type you can omit the Array and directly
|
626
|
+
198 # specify the type identifier.
|
627
|
+
199 #
|
628
|
+
200 # <code>:no_adaption</code>::
|
629
|
+
201 # +true+ or +false+ -- whether no type adaption should be performed.
|
630
|
+
202 #
|
631
|
+
203 # Usage:
|
632
|
+
204 # signature(:to_s) # no arguments
|
633
|
+
205 # signature(:+, :any) # one argument, type unchecked
|
634
|
+
206 # signature(:+, Fixnum) # one argument, type Fixnum
|
635
|
+
207 # signature(:+, NumericContract)
|
636
|
+
208 # signature(:+, 1 .. 10)
|
637
|
+
209 # signature(:sqrt, lambda { |arg| arg > 0 })
|
638
|
+
210 #
|
639
|
+
211 # signature(:each, :block => true) # has to have block
|
640
|
+
212 # signature(:to_i, :block => false) # not allowed to have block
|
641
|
+
213 # signature(:to_i, :result => Fixnum) # return value must be Fixnum
|
642
|
+
214 # signature(:zip, :allow_trailing => true) # unchecked trailing args
|
643
|
+
215 # signature(:zip, :repeated => [Enumerable]) # repeated trailing args
|
644
|
+
216 # signature(:zip, :repeated => Enumerable)
|
645
|
+
217 # # foo(3, 6, 4, 7) works; foo(5), foo(3, 2) etc. don't
|
646
|
+
218 # signature(:foo, :repeated => [1..4, 5..9])
|
647
|
+
</span><span class="marked"> 219 def signature(method, *args)
|
648
|
+
220 options = {}
|
649
|
+
221 signature = args.dup
|
650
|
+
222 options.update(signature.pop) if signature.last.is_a?(Hash)
|
651
|
+
</span><span class="inferred"> 223
|
652
|
+
</span><span class="marked"> 224 return if not Contract.check_signatures? and options[:no_adaption]
|
653
|
+
</span><span class="inferred"> 225
|
654
|
+
</span><span class="marked"> 226 old_method = instance_method(method)
|
655
|
+
227 remove_method(method) if instance_methods(false).include?(method.to_s)
|
656
|
+
</span><span class="inferred"> 228
|
657
|
+
</span><span class="marked"> 229 arity = old_method.arity
|
658
|
+
</span><span class="false"> 230 if arity != signature.size and
|
659
|
+
</span><span class="marked"> 231 (arity >= 0 or signature.size < ~arity) then
|
660
|
+
232 raise(ArgumentError, "signature isn't compatible with arity")
|
661
|
+
</span><span class="inferred"> 233 end
|
662
|
+
234
|
663
|
+
235 # Normalizes specifiers to Objects that respond to === so that the run-time
|
664
|
+
236 # checks only have to deal with that case. Also checks that a specifier is
|
665
|
+
237 # actually valid.
|
666
|
+
</span><span class="marked"> 238 convert_specifier = lambda do |item|
|
667
|
+
</span><span class="inferred"> 239 # Procs, Methods etc.
|
668
|
+
</span><span class="marked"> 240 if item.respond_to?(:call) then
|
669
|
+
241 Contract::Check.block { |arg| item.call(arg) }
|
670
|
+
</span><span class="inferred"> 242 # Already okay
|
671
|
+
</span><span class="marked"> 243 elsif item.respond_to?(:===) or item == :any then
|
672
|
+
244 item
|
673
|
+
</span><span class="inferred"> 245 # Unknown specifier
|
674
|
+
246 else
|
675
|
+
</span><span class="marked"> 247 raise(ArgumentError, "unsupported argument specifier #{item.inspect}")
|
676
|
+
</span><span class="inferred"> 248 end
|
677
|
+
249 end
|
678
|
+
250
|
679
|
+
</span><span class="marked"> 251 signature.map!(&convert_specifier)
|
680
|
+
</span><span class="inferred"> 252
|
681
|
+
</span><span class="marked"> 253 if options.include?(:repeated) then
|
682
|
+
254 options[:repeated] = Array(options[:repeated])
|
683
|
+
255 if options[:repeated].size == 0 then
|
684
|
+
256 raise(ArgumentError, "repeated arguments may not be an empty Array")
|
685
|
+
</span><span class="inferred"> 257 else
|
686
|
+
</span><span class="marked"> 258 options[:repeated].map!(&convert_specifier)
|
687
|
+
</span><span class="inferred"> 259 end
|
688
|
+
260 end
|
689
|
+
261
|
690
|
+
</span><span class="marked"> 262 if options.include?(:return) then
|
691
|
+
263 options[:return] = convert_specifier.call(options[:return])
|
692
|
+
</span><span class="inferred"> 264 end
|
693
|
+
265
|
694
|
+
266 # We need to keep around references to our arguments because we will
|
695
|
+
267 # need to access them via ObjectSpace._id2ref so that they do not
|
696
|
+
268 # get garbage collected.
|
697
|
+
</span><span class="marked"> 269 @signatures ||= Hash.new { |hash, key| hash[key] = Array.new }
|
698
|
+
270 @signatures[method] << [signature, options, old_method]
|
699
|
+
</span><span class="inferred"> 271
|
700
|
+
</span><span class="marked"> 272 adapted = Proc.new do |obj, type, assign_to|
|
701
|
+
273 if options[:no_adaption] then
|
702
|
+
274 obj
|
703
|
+
275 elsif assign_to then
|
704
|
+
276 %{(#{assign_to} = Contract.adapt(#{obj}, #{type}))}
|
705
|
+
</span><span class="inferred"> 277 else
|
706
|
+
</span><span class="marked"> 278 %{Contract.adapt(#{obj}, #{type})}
|
707
|
+
</span><span class="inferred"> 279 end
|
708
|
+
280 end
|
709
|
+
281
|
710
|
+
282 # We have to use class_eval so that signatures can be specified for
|
711
|
+
283 # methods taking blocks in Ruby 1.8. (This will be obsolete in 1.9)
|
712
|
+
284 # We also make the checks as efficient as we can.
|
713
|
+
</span><span class="marked"> 285 code = %{
|
714
|
+
</span><span class="inferred"> 286 def #{method}(*args, &block)
|
715
|
+
</span><span class="false"> 287 old_args = args.dup
|
716
|
+
</span><span class="inferred"> 288
|
717
|
+
</span><span class="marked"> 289 #{if options.include?(:block) then
|
718
|
+
290 if options[:block] then
|
719
|
+
291 %{raise(ArgumentError, "no block given") unless block}
|
720
|
+
</span><span class="inferred"> 292 else
|
721
|
+
</span><span class="marked"> 293 %{raise(ArgumentError, "block given") if block}
|
722
|
+
</span><span class="inferred"> 294 end
|
723
|
+
295 end
|
724
|
+
296 }
|
725
|
+
297
|
726
|
+
</span><span class="marked"> 298 #{if not(options[:allow_trailing] or options.include?(:repeated))
|
727
|
+
299 msg = "wrong number of arguments (\#{args.size} for " +
|
728
|
+
</span><span class="inferred"> 300 "#{signature.size})"
|
729
|
+
</span><span class="marked"> 301 %{if args.size != #{signature.size} then
|
730
|
+
</span><span class="inferred"> 302 raise(ArgumentError, "#{msg}")
|
731
|
+
303 end
|
732
|
+
304 }
|
733
|
+
</span><span class="marked"> 305 elsif signature.size > 0
|
734
|
+
306 msg = "wrong number of arguments (\#{args.size} for " +
|
735
|
+
</span><span class="inferred"> 307 "at least #{signature.size}"
|
736
|
+
</span><span class="marked"> 308 %{if args.size < #{signature.size} then
|
737
|
+
</span><span class="inferred"> 309 raise(ArgumentError, "#{msg}")
|
738
|
+
310 end
|
739
|
+
311 }
|
740
|
+
312 end
|
741
|
+
313 }
|
742
|
+
314
|
743
|
+
</span><span class="marked"> 315 #{index = 0
|
744
|
+
316 signature.map do |part|
|
745
|
+
317 next if part == :any
|
746
|
+
318 index += 1
|
747
|
+
319 msg = "argument #{index} (\#{arg.inspect}) does not match " +
|
748
|
+
</span><span class="inferred"> 320 "#{part.inspect}"
|
749
|
+
</span><span class="marked"> 321 %{type = ObjectSpace._id2ref(#{part.object_id})
|
750
|
+
</span><span class="inferred"> 322 arg = args.shift
|
751
|
+
</span><span class="false"> 323 unless type === #{adapted[%{arg}, %{type}, %{old_args[#{index - 1}]}]}
|
752
|
+
324 raise(ArgumentError, "#{msg}")
|
753
|
+
325 end
|
754
|
+
326 }
|
755
|
+
327 end
|
756
|
+
328 }
|
757
|
+
</span><span class="inferred"> 329
|
758
|
+
</span><span class="marked"> 330 #{if repeated = options[:repeated] then
|
759
|
+
331 msg = "argument \#{idx + #{signature.size}}" +
|
760
|
+
</span><span class="inferred"> 332 "(\#{arg.inspect}) does not match \#{part.inspect}"
|
761
|
+
</span><span class="marked"> 333 %{parts = ObjectSpace._id2ref(#{repeated.object_id})
|
762
|
+
</span><span class="inferred"> 334 args.each_with_index do |arg, idx|
|
763
|
+
</span><span class="false"> 335 part = parts[idx % #{repeated.size}]
|
764
|
+
336 if part != :any and
|
765
|
+
337 not part === (#{adapted[%{arg}, %{part}, %{old_args[idx]}]})
|
766
|
+
338 then
|
767
|
+
339 raise(ArgumentError, "#{msg}")
|
768
|
+
340 end
|
769
|
+
341 end
|
770
|
+
342 }
|
771
|
+
343 end
|
772
|
+
344 }
|
773
|
+
345
|
774
|
+
346 result = ObjectSpace._id2ref(#{old_method.object_id}).bind(self).
|
775
|
+
347 call(*old_args, &block)
|
776
|
+
</span><span class="marked"> 348 #{if rt = options[:return] and rt != :any then
|
777
|
+
349 msg = "return value (\#{result.inspect}) does not match #{rt.inspect}"
|
778
|
+
350 %{type = ObjectSpace._id2ref(#{rt.object_id})
|
779
|
+
</span><span class="inferred"> 351 unless type === #{adapted[%{result}, %{type}]}
|
780
|
+
352 raise(StandardError, "#{msg}")
|
781
|
+
353 end
|
782
|
+
354 }
|
783
|
+
355 end
|
784
|
+
356 }
|
785
|
+
357 end
|
786
|
+
358 }
|
787
|
+
</span><span class="marked"> 359 class_eval code, "(signature check for #{old_method.inspect[/: (.+?)>\Z/, 1]})"
|
788
|
+
</span><span class="inferred"> 360
|
789
|
+
</span><span class="marked"> 361 return true
|
790
|
+
</span><span class="inferred"> 362 end
|
791
|
+
363
|
792
|
+
364 # Specifies that this Module/Class fulfills one or more contracts. The contracts
|
793
|
+
365 # will automatically be verified after an instance has been successfully created.
|
794
|
+
366 # This only actually does the checks when Contract.check_fulfills is enabled.
|
795
|
+
367 # The method will return +true+ in case it actually inserted the check logic and
|
796
|
+
368 # +nil+ in case it didn't.
|
797
|
+
369 #
|
798
|
+
370 # Note that this works by overriding the #initialize method which means that you
|
799
|
+
371 # should either add the fulfills statements after your initialize method or call
|
800
|
+
372 # the previously defined initialize method from your new one.
|
801
|
+
</span><span class="marked"> 373 def fulfills(*contracts)
|
802
|
+
374 return unless Contract.check_fulfills?
|
803
|
+
</span><span class="inferred"> 375
|
804
|
+
</span><span class="marked"> 376 contracts.each do |contract|
|
805
|
+
377 contract.implications.each do |implication|
|
806
|
+
378 include implication
|
807
|
+
</span><span class="inferred"> 379 end
|
808
|
+
380 end
|
809
|
+
381
|
810
|
+
</span><span class="marked"> 382 old_method = instance_method(:initialize)
|
811
|
+
383 remove_method(:initialize) if instance_methods(false).include?("initialize")
|
812
|
+
</span><span class="inferred"> 384
|
813
|
+
385 # Keep visible references around so that the GC will not eat these up.
|
814
|
+
</span><span class="marked"> 386 @fulfills ||= Array.new
|
815
|
+
387 @fulfills << [contracts, old_method]
|
816
|
+
</span><span class="false"> 388
|
817
|
+
389 # Have to use class_eval because define_method does not allow methods to take
|
818
|
+
390 # blocks. This can be cleaned up when Ruby 1.9 has become current.
|
819
|
+
391 class_eval %{
|
820
|
+
392 def initialize(*args, &block)
|
821
|
+
393 ObjectSpace._id2ref(#{old_method.object_id}).bind(self).call(*args, &block)
|
822
|
+
394 ObjectSpace._id2ref(#{contracts.object_id}).each do |contract|
|
823
|
+
395 contract.enforce self
|
824
|
+
396 end
|
825
|
+
397 end
|
826
|
+
</span><span class="marked"> 398 }, "(post initialization contract check for #{self.inspect})"
|
827
|
+
</span><span class="inferred"> 399
|
828
|
+
</span><span class="marked"> 400 return true
|
829
|
+
</span><span class="inferred"> 401 end
|
830
|
+
402 end
|
831
|
+
403
|
832
|
+
404
|
833
|
+
</span><span class="marked"> 405 module Kernel
|
834
|
+
</span><span class="inferred"> 406 # Adds an adaption route from the specified type to the specified type.
|
835
|
+
407 # Basic usage looks like this:
|
836
|
+
408 # adaption :from => StringIO, :to => String, :via => :read
|
837
|
+
409 #
|
838
|
+
410 # This method takes various options. Here's a complete list:
|
839
|
+
411 #
|
840
|
+
412 # <code>:from</code>::
|
841
|
+
413 # The type that can be converted from. Defaults to +self+ meaning you
|
842
|
+
414 # can safely omit it in Class, Module or Contract context.
|
843
|
+
415 #
|
844
|
+
416 # <code>:to</code>::
|
845
|
+
417 # The type that can be converted to. Defaults to +self+ meaning you
|
846
|
+
418 # can safely omit it in Class, Module or Contract context.
|
847
|
+
419 #
|
848
|
+
420 # Note that you need to specify either <code>:from</code> or
|
849
|
+
421 # <code>:to</code>.
|
850
|
+
422 #
|
851
|
+
423 # <code>:via</code>::
|
852
|
+
424 # How the <code>:from</code> type will be converted to the
|
853
|
+
425 # <code>:to</code> type. If this is a Symbol the conversion will be
|
854
|
+
426 # done by invoking the method identified by that Symbol on the
|
855
|
+
427 # source object. Otherwise this should be something that responds to
|
856
|
+
428 # the +call+ method (for example Methods and Procs) which will get
|
857
|
+
429 # the source object as its argument and which should return the
|
858
|
+
430 # target object.
|
859
|
+
431 #
|
860
|
+
432 # <code>:if</code>::
|
861
|
+
433 # The conversion can only be performed if this condition is met.
|
862
|
+
434 # This can either be something that implements the === case
|
863
|
+
435 # equivalence operator or something that implements the +call+
|
864
|
+
436 # method. So Methods, Procs, Modules, Classes and Contracts all
|
865
|
+
437 # make sense in this context. You can also specify a Symbol in
|
866
|
+
438 # which case the conversion can only be performed if the source
|
867
|
+
439 # object responds to the method identified by that Symbol.
|
868
|
+
440 #
|
869
|
+
441 # Note that the <code>:if</code> option will default to the same
|
870
|
+
442 # value as the <code>:via</code> option if the <code>:via</code>
|
871
|
+
443 # option is a Symbol.
|
872
|
+
444 #
|
873
|
+
445 # If you invoke this method with a block it will be used instead of
|
874
|
+
446 # the <code>:via</code> option.
|
875
|
+
447 #
|
876
|
+
448 # See Contract.adapt for how conversion look-ups are performed.
|
877
|
+
</span><span class="marked"> 449 def adaption(options = {}, &block) # :yield: source_object
|
878
|
+
450 options = {
|
879
|
+
</span><span class="inferred"> 451 :from => self,
|
880
|
+
452 :to => self
|
881
|
+
</span><span class="false"> 453 }.merge(options)
|
882
|
+
</span><span class="inferred"> 454
|
883
|
+
</span><span class="marked"> 455 if block then
|
884
|
+
456 if options.include?(:via) then
|
885
|
+
457 raise(ArgumentError, "Can't use both block and :via")
|
886
|
+
</span><span class="inferred"> 458 else
|
887
|
+
</span><span class="marked"> 459 options[:via] = block
|
888
|
+
</span><span class="inferred"> 460 end
|
889
|
+
461 end
|
890
|
+
462
|
891
|
+
</span><span class="marked"> 463 if options[:via].respond_to?(:to_sym) then
|
892
|
+
464 options[:via] = options[:via].to_sym
|
893
|
+
</span><span class="inferred"> 465 end
|
894
|
+
466
|
895
|
+
</span><span class="marked"> 467 options[:if] ||= options[:via] if options[:via].is_a?(Symbol)
|
896
|
+
</span><span class="inferred"> 468
|
897
|
+
</span><span class="marked"> 469 if options[:via].is_a?(Symbol) then
|
898
|
+
470 symbol = options[:via]
|
899
|
+
471 options[:via] = lambda { |obj| obj.send(symbol) }
|
900
|
+
</span><span class="inferred"> 472 end
|
901
|
+
473
|
902
|
+
</span><span class="marked"> 474 if options[:if].respond_to?(:to_sym) then
|
903
|
+
475 options[:if] = options[:if].to_sym
|
904
|
+
</span><span class="inferred"> 476 end
|
905
|
+
477
|
906
|
+
</span><span class="marked"> 478 if options[:if].is_a?(Symbol) then
|
907
|
+
479 options[:if] = Contract::Check::Quack[options[:if]]
|
908
|
+
480 elsif options[:if].respond_to?(:call) then
|
909
|
+
481 callable = options[:if]
|
910
|
+
482 options[:if] = Contract::Check.block { |obj| callable.call(obj) }
|
911
|
+
</span><span class="inferred"> 483 end
|
912
|
+
484
|
913
|
+
</span><span class="marked"> 485 if options[:from] == self and options[:to] == self then
|
914
|
+
486 raise(ArgumentError, "Need to specify either :from or :to")
|
915
|
+
487 elsif options[:from] == options[:to] then
|
916
|
+
488 raise(ArgumentError, "Self-adaption: :from and :to both are " +
|
917
|
+
</span><span class="inferred"> 489 options[:to].inspect)
|
918
|
+
490 end
|
919
|
+
491
|
920
|
+
</span><span class="marked"> 492 unless options[:via]
|
921
|
+
493 raise(ArgumentError, "Need to specify how to adapt (use :via or block)")
|
922
|
+
</span><span class="inferred"> 494 end
|
923
|
+
495
|
924
|
+
</span><span class="marked"> 496 Contract.adaptions[options[:to]] << options
|
925
|
+
</span><span class="inferred"> 497 end
|
926
|
+
498
|
927
|
+
499 # Built-in adaption routes that Ruby already uses in its C code.
|
928
|
+
</span><span class="marked"> 500 adaption :to => Symbol, :via => :to_sym
|
929
|
+
501 adaption :to => String, :via => :to_str
|
930
|
+
502 adaption :to => Array, :via => :to_ary
|
931
|
+
503 adaption :to => Integer, :via => :to_int
|
932
|
+
</span><span class="inferred"> 504 end
|
933
|
+
</span><span class="false"> 505 # This file contains code for integrating the contract library with built-in
|
934
|
+
506 # classes and methods.
|
935
|
+
507 #
|
936
|
+
508 # See Contract::Check, Module#signature and Module#fulfills.
|
937
|
+
509
|
938
|
+
510
|
939
|
+
511 class Contract < Test::Unit::TestCase
|
940
|
+
512 # Implements checks that can for example be used in Module#signature
|
941
|
+
513 # specifications. They are implemented simply by overriding the === case
|
942
|
+
514 # equality operator. They can also be nested like this:
|
943
|
+
515 # # Matches something that is an Enumerable and that responds to
|
944
|
+
516 # # either :to_ary or :to_a.
|
945
|
+
517 # signature :x, Contract::Check::All[
|
946
|
+
518 # Enumerable,
|
947
|
+
519 # Contract::Check::Any[
|
948
|
+
520 # Contract::Check::Quack[:to_a],
|
949
|
+
521 # Contract::Check::Quack[:to_ary]
|
950
|
+
522 # ]
|
951
|
+
523 # ]
|
952
|
+
524 module Check
|
953
|
+
525 # An abstract Base class for Contract::Check classes.
|
954
|
+
526 # Contains logic for instantation.
|
955
|
+
527 class Base # :nodoc:
|
956
|
+
528 class << self
|
957
|
+
529 alias :[] :new
|
958
|
+
530 end
|
959
|
+
531
|
960
|
+
532 def initialize(*args, &block)
|
961
|
+
533 @args, @block = args, block
|
962
|
+
534 end
|
963
|
+
535 end
|
964
|
+
536
|
965
|
+
537 # Checks that the specified block matches.
|
966
|
+
538 # Example:
|
967
|
+
539 # signature :x, Contract::Check.block { |arg| arg > 0 }
|
968
|
+
540 class Block < Base
|
969
|
+
541 def ===(other)
|
970
|
+
542 @block.call(other)
|
971
|
+
543 end
|
972
|
+
544 end
|
973
|
+
545 # Short-cut for creating a Contract::Check::Block.
|
974
|
+
546 def self.block(&block) # :yields: arg
|
975
|
+
547 Block.new(&block)
|
976
|
+
548 end
|
977
|
+
549
|
978
|
+
550 # Checks that all the specified methods are answered.
|
979
|
+
551 # Example:
|
980
|
+
552 # signature :x, Contract::Check::Quack[:to_sym]
|
981
|
+
553 class Quack < Base
|
982
|
+
554 def ===(other)
|
983
|
+
555 @args.all? { |arg| other.respond_to?(arg) }
|
984
|
+
556 end
|
985
|
+
557 end
|
986
|
+
558
|
987
|
+
559 # Checks that all the specified conditions match.
|
988
|
+
560 # Example:
|
989
|
+
561 # signature :x, Contract::Check::All[Array, Enumerable]
|
990
|
+
562 class All < Base
|
991
|
+
563 def ===(other)
|
992
|
+
564 @args.all? { |arg| arg === other }
|
993
|
+
565 end
|
994
|
+
566 end
|
995
|
+
567 # Alias for Contract::Check::All
|
996
|
+
568 And = All unless defined?(And)
|
997
|
+
569
|
998
|
+
570 # Checks that at least one of the specified conditions match.
|
999
|
+
571 # Example:
|
1000
|
+
572 # signature :x, Contract::Check::Any[String, Symbol]
|
1001
|
+
573 class Any < Base
|
1002
|
+
574 def ===(other)
|
1003
|
+
575 @args.any? { |arg| arg === other }
|
1004
|
+
576 end
|
1005
|
+
577 end
|
1006
|
+
578 # Alias for Contract::Check::Any
|
1007
|
+
579 Or = Any unless defined?(Or)
|
1008
|
+
580
|
1009
|
+
581 # Checks that none of the specified conditions match.
|
1010
|
+
582 # Example:
|
1011
|
+
583 # signature :x, Contract::Check::None[Numeric, Symbol]
|
1012
|
+
584 # signature :x, Contract::Check::Not[Comparable]
|
1013
|
+
585 class None < Base
|
1014
|
+
586 def ===(other)
|
1015
|
+
587 not @args.any? { |arg| arg === other }
|
1016
|
+
588 end
|
1017
|
+
589 end
|
1018
|
+
590 # Alias for Contract::Check::None
|
1019
|
+
591 Not = None unless defined?(Not)
|
1020
|
+
592 end
|
1021
|
+
593
|
1022
|
+
594 class << self
|
1023
|
+
595 # Whether signatures should be checked. By default signatures are checked
|
1024
|
+
596 # only when the application is run in $DEBUG mode. (By specifying the -d
|
1025
|
+
597 # switch on the invocation of Ruby.)
|
1026
|
+
598 #
|
1027
|
+
599 # Note: If you want to change this you need to do so before doing any
|
1028
|
+
600 # Module#signature calls or it will not be applied. It's probably best
|
1029
|
+
601 # set right after requiring the contract library.
|
1030
|
+
602 attr_accessor :check_signatures
|
1031
|
+
603 alias :check_signatures? :check_signatures
|
1032
|
+
604
|
1033
|
+
605 # Whether fulfills should be checked. This is enabled by default.
|
1034
|
+
606 #
|
1035
|
+
607 # Note: If you want to change this you need to do so before doing any
|
1036
|
+
608 # Module#fulfills calls or it will not be applied. It's probably best
|
1037
|
+
609 # set right after requiring the contract library.
|
1038
|
+
610 attr_accessor :check_fulfills
|
1039
|
+
611 alias :check_fulfills? :check_fulfills
|
1040
|
+
612
|
1041
|
+
613 # All adaption routes.
|
1042
|
+
614 attr_accessor :adaptions # :nodoc:
|
1043
|
+
615 end
|
1044
|
+
616 self.check_signatures = $DEBUG if self.check_signatures.nil?
|
1045
|
+
617 self.check_fulfills = true if self.check_fulfills.nil?
|
1046
|
+
618 if self.adaptions.nil? then
|
1047
|
+
619 self.adaptions = Hash.new { |hash, key| hash[key] = Array.new }
|
1048
|
+
620 end
|
1049
|
+
621
|
1050
|
+
622 # Tries to adapt the specified object to the specified type.
|
1051
|
+
623 # Returns the old object if no suitable adaption route was found or if
|
1052
|
+
624 # it already is of the specified type.
|
1053
|
+
625 #
|
1054
|
+
626 # This will only use adaptions where the :to part is equal to the specified
|
1055
|
+
627 # type. No multi-step conversion will be performed.
|
1056
|
+
628 def self.adapt(object, type)
|
1057
|
+
629 return object if type === object
|
1058
|
+
630
|
1059
|
+
631 @adaptions[type].each do |adaption|
|
1060
|
+
632 if adaption[:from] === object and
|
1061
|
+
633 (adaption[:if].nil? or adaption[:if] === object)
|
1062
|
+
634 then
|
1063
|
+
635 result = adaption[:via].call(object)
|
1064
|
+
636 return result if type === result
|
1065
|
+
637 end
|
1066
|
+
638 end
|
1067
|
+
639
|
1068
|
+
640 return object
|
1069
|
+
641 end
|
1070
|
+
642 end
|
1071
|
+
643
|
1072
|
+
644
|
1073
|
+
645 class Module
|
1074
|
+
646 # Checks that the arguments and return value of a method match the specified
|
1075
|
+
647 # signature. Checks are only actually done when Contract.check_signatures is
|
1076
|
+
648 # set to true or if the <code>:no_adaption</code> option is +false+. The
|
1077
|
+
649 # method will return +true+ in case it actually inserted the signature check
|
1078
|
+
650 # logic and +nil+ in case it didn't.
|
1079
|
+
651 #
|
1080
|
+
652 # You will usually specify one type specifier (<code>:any</code> which will
|
1081
|
+
653 # allow anything to appear at that position of the argument list or something
|
1082
|
+
654 # that implements the === case equality operator -- samples are Contracts,
|
1083
|
+
655 # Ranges, Classes, Modules, Regexps, Contract::Checks and so on) per argument.
|
1084
|
+
656 # You can also use objects that implement the +call+ method as type specifiers
|
1085
|
+
657 # which includes Methods and Procs.
|
1086
|
+
658 #
|
1087
|
+
659 # If you don't use the <code>:repeated</code> or <code>:allow_trailing</code>
|
1088
|
+
660 # options the method will take exactly as many arguments as there are type
|
1089
|
+
661 # specifiers which means that <code>signature :a_method</code> enforces
|
1090
|
+
662 # +a_method+ having exactly zero arguments.
|
1091
|
+
663 #
|
1092
|
+
664 # The checks are done by wrapping the type checks around the method.
|
1093
|
+
665 # ArgumentError exceptions will be raised in case the signature contract is
|
1094
|
+
666 # not adhered to by your caller.
|
1095
|
+
667 #
|
1096
|
+
668 # An ArgumentError exception will be raised in case the methods natural
|
1097
|
+
669 # argument list size and the signature you specified via Module.signature are
|
1098
|
+
670 # incompatible. (Note that they don't have to be completely equivalent, you
|
1099
|
+
671 # can still have a method taking zero or more arguments and apply a signature
|
1100
|
+
672 # that limits the actual argument count to three arguments.)
|
1101
|
+
673 #
|
1102
|
+
674 # This method can take quite a few options. Here's a complete list:
|
1103
|
+
675 #
|
1104
|
+
676 # <code>:return</code>::
|
1105
|
+
677 # A return type that the method must comply to. Note that this check (if
|
1106
|
+
678 # failed) will actually raise a StandardError instead of an ArgumentError
|
1107
|
+
679 # because the failure likely lies in the method itself and not in what the
|
1108
|
+
680 # caller did.
|
1109
|
+
681 #
|
1110
|
+
682 # <code>:block</code>::
|
1111
|
+
683 # +true+ or +false+ -- whether the method must take a block or not. So
|
1112
|
+
684 # specifying <code>:block => false</code> enforces that the method is not
|
1113
|
+
685 # allowed to have a block supplied.
|
1114
|
+
686 #
|
1115
|
+
687 # <code>:allow_trailing</code>::
|
1116
|
+
688 # +true+ or +false+ -- whether the argument list may contain trailing,
|
1117
|
+
689 # unchecked arguments.
|
1118
|
+
690 #
|
1119
|
+
691 # <code>:repeated</code>::
|
1120
|
+
692 # An Array that specifies arguments of a method that will be repeated over
|
1121
|
+
693 # and over again at the end of the argument list.
|
1122
|
+
694 #
|
1123
|
+
695 # A good sample of this are Array#values_at which takes zero or or more
|
1124
|
+
696 # Numeric arguments and Enumerable#zip which takes zero or more other
|
1125
|
+
697 # Enumerable arguments.
|
1126
|
+
698 #
|
1127
|
+
699 # Note that the Array that was associated with the <code>:repeated</code>
|
1128
|
+
700 # option must not be empty or an ArgumentError exception will be raised.
|
1129
|
+
701 # If there's just one repeated type you can omit the Array and directly
|
1130
|
+
702 # specify the type identifier.
|
1131
|
+
703 #
|
1132
|
+
704 # <code>:no_adaption</code>::
|
1133
|
+
705 # +true+ or +false+ -- whether no type adaption should be performed.
|
1134
|
+
706 #
|
1135
|
+
707 # Usage:
|
1136
|
+
708 # signature(:to_s) # no arguments
|
1137
|
+
709 # signature(:+, :any) # one argument, type unchecked
|
1138
|
+
710 # signature(:+, Fixnum) # one argument, type Fixnum
|
1139
|
+
711 # signature(:+, NumericContract)
|
1140
|
+
712 # signature(:+, 1 .. 10)
|
1141
|
+
713 # signature(:sqrt, lambda { |arg| arg > 0 })
|
1142
|
+
714 #
|
1143
|
+
715 # signature(:each, :block => true) # has to have block
|
1144
|
+
716 # signature(:to_i, :block => false) # not allowed to have block
|
1145
|
+
717 # signature(:to_i, :result => Fixnum) # return value must be Fixnum
|
1146
|
+
718 # signature(:zip, :allow_trailing => true) # unchecked trailing args
|
1147
|
+
719 # signature(:zip, :repeated => [Enumerable]) # repeated trailing args
|
1148
|
+
720 # signature(:zip, :repeated => Enumerable)
|
1149
|
+
721 # # foo(3, 6, 4, 7) works; foo(5), foo(3, 2) etc. don't
|
1150
|
+
722 # signature(:foo, :repeated => [1..4, 5..9])
|
1151
|
+
723 def signature(method, *args)
|
1152
|
+
724 options = {}
|
1153
|
+
725 signature = args.dup
|
1154
|
+
726 options.update(signature.pop) if signature.last.is_a?(Hash)
|
1155
|
+
727
|
1156
|
+
728 return if not Contract.check_signatures? and options[:no_adaption]
|
1157
|
+
729
|
1158
|
+
730 old_method = instance_method(method)
|
1159
|
+
731 remove_method(method) if instance_methods(false).include?(method.to_s)
|
1160
|
+
732
|
1161
|
+
733 arity = old_method.arity
|
1162
|
+
734 if arity != signature.size and
|
1163
|
+
735 (arity >= 0 or signature.size < ~arity) then
|
1164
|
+
736 raise(ArgumentError, "signature isn't compatible with arity")
|
1165
|
+
737 end
|
1166
|
+
738
|
1167
|
+
739 # Normalizes specifiers to Objects that respond to === so that the run-time
|
1168
|
+
740 # checks only have to deal with that case. Also checks that a specifier is
|
1169
|
+
741 # actually valid.
|
1170
|
+
742 convert_specifier = lambda do |item|
|
1171
|
+
743 # Procs, Methods etc.
|
1172
|
+
744 if item.respond_to?(:call) then
|
1173
|
+
745 Contract::Check.block { |arg| item.call(arg) }
|
1174
|
+
746 # Already okay
|
1175
|
+
747 elsif item.respond_to?(:===) or item == :any then
|
1176
|
+
748 item
|
1177
|
+
749 # Unknown specifier
|
1178
|
+
750 else
|
1179
|
+
751 raise(ArgumentError, "unsupported argument specifier #{item.inspect}")
|
1180
|
+
752 end
|
1181
|
+
753 end
|
1182
|
+
754
|
1183
|
+
755 signature.map!(&convert_specifier)
|
1184
|
+
756
|
1185
|
+
757 if options.include?(:repeated) then
|
1186
|
+
758 options[:repeated] = Array(options[:repeated])
|
1187
|
+
759 if options[:repeated].size == 0 then
|
1188
|
+
760 raise(ArgumentError, "repeated arguments may not be an empty Array")
|
1189
|
+
761 else
|
1190
|
+
762 options[:repeated].map!(&convert_specifier)
|
1191
|
+
763 end
|
1192
|
+
764 end
|
1193
|
+
765
|
1194
|
+
766 if options.include?(:return) then
|
1195
|
+
767 options[:return] = convert_specifier.call(options[:return])
|
1196
|
+
768 end
|
1197
|
+
769
|
1198
|
+
770 # We need to keep around references to our arguments because we will
|
1199
|
+
771 # need to access them via ObjectSpace._id2ref so that they do not
|
1200
|
+
772 # get garbage collected.
|
1201
|
+
773 @signatures ||= Hash.new { |hash, key| hash[key] = Array.new }
|
1202
|
+
774 @signatures[method] << [signature, options, old_method]
|
1203
|
+
775
|
1204
|
+
776 adapted = Proc.new do |obj, type, assign_to|
|
1205
|
+
777 if options[:no_adaption] then
|
1206
|
+
778 obj
|
1207
|
+
779 elsif assign_to then
|
1208
|
+
780 %{(#{assign_to} = Contract.adapt(#{obj}, #{type}))}
|
1209
|
+
781 else
|
1210
|
+
782 %{Contract.adapt(#{obj}, #{type})}
|
1211
|
+
783 end
|
1212
|
+
784 end
|
1213
|
+
785
|
1214
|
+
786 # We have to use class_eval so that signatures can be specified for
|
1215
|
+
787 # methods taking blocks in Ruby 1.8. (This will be obsolete in 1.9)
|
1216
|
+
788 # We also make the checks as efficient as we can.
|
1217
|
+
789 code = %{
|
1218
|
+
790 def #{method}(*args, &block)
|
1219
|
+
791 old_args = args.dup
|
1220
|
+
792
|
1221
|
+
793 #{if options.include?(:block) then
|
1222
|
+
794 if options[:block] then
|
1223
|
+
795 %{raise(ArgumentError, "no block given") unless block}
|
1224
|
+
796 else
|
1225
|
+
797 %{raise(ArgumentError, "block given") if block}
|
1226
|
+
798 end
|
1227
|
+
799 end
|
1228
|
+
800 }
|
1229
|
+
801
|
1230
|
+
802 #{if not(options[:allow_trailing] or options.include?(:repeated))
|
1231
|
+
803 msg = "wrong number of arguments (\#{args.size} for " +
|
1232
|
+
804 "#{signature.size})"
|
1233
|
+
805 %{if args.size != #{signature.size} then
|
1234
|
+
806 raise(ArgumentError, "#{msg}")
|
1235
|
+
807 end
|
1236
|
+
808 }
|
1237
|
+
809 elsif signature.size > 0
|
1238
|
+
810 msg = "wrong number of arguments (\#{args.size} for " +
|
1239
|
+
811 "at least #{signature.size}"
|
1240
|
+
812 %{if args.size < #{signature.size} then
|
1241
|
+
813 raise(ArgumentError, "#{msg}")
|
1242
|
+
814 end
|
1243
|
+
815 }
|
1244
|
+
816 end
|
1245
|
+
817 }
|
1246
|
+
818
|
1247
|
+
819 #{index = 0
|
1248
|
+
820 signature.map do |part|
|
1249
|
+
821 next if part == :any
|
1250
|
+
822 index += 1
|
1251
|
+
823 msg = "argument #{index} (\#{arg.inspect}) does not match " +
|
1252
|
+
824 "#{part.inspect}"
|
1253
|
+
825 %{type = ObjectSpace._id2ref(#{part.object_id})
|
1254
|
+
826 arg = args.shift
|
1255
|
+
827 unless type === #{adapted[%{arg}, %{type}, %{old_args[#{index - 1}]}]}
|
1256
|
+
828 raise(ArgumentError, "#{msg}")
|
1257
|
+
829 end
|
1258
|
+
830 }
|
1259
|
+
831 end
|
1260
|
+
832 }
|
1261
|
+
833
|
1262
|
+
834 #{if repeated = options[:repeated] then
|
1263
|
+
835 msg = "argument \#{idx + #{signature.size}}" +
|
1264
|
+
836 "(\#{arg.inspect}) does not match \#{part.inspect}"
|
1265
|
+
837 %{parts = ObjectSpace._id2ref(#{repeated.object_id})
|
1266
|
+
838 args.each_with_index do |arg, idx|
|
1267
|
+
839 part = parts[idx % #{repeated.size}]
|
1268
|
+
840 if part != :any and
|
1269
|
+
841 not part === (#{adapted[%{arg}, %{part}, %{old_args[idx]}]})
|
1270
|
+
842 then
|
1271
|
+
843 raise(ArgumentError, "#{msg}")
|
1272
|
+
844 end
|
1273
|
+
845 end
|
1274
|
+
846 }
|
1275
|
+
847 end
|
1276
|
+
848 }
|
1277
|
+
849
|
1278
|
+
850 result = ObjectSpace._id2ref(#{old_method.object_id}).bind(self).
|
1279
|
+
851 call(*old_args, &block)
|
1280
|
+
852 #{if rt = options[:return] and rt != :any then
|
1281
|
+
853 msg = "return value (\#{result.inspect}) does not match #{rt.inspect}"
|
1282
|
+
854 %{type = ObjectSpace._id2ref(#{rt.object_id})
|
1283
|
+
855 unless type === #{adapted[%{result}, %{type}]}
|
1284
|
+
856 raise(StandardError, "#{msg}")
|
1285
|
+
857 end
|
1286
|
+
858 }
|
1287
|
+
859 end
|
1288
|
+
860 }
|
1289
|
+
861 end
|
1290
|
+
862 }
|
1291
|
+
863 class_eval code, "(signature check for #{old_method.inspect[/: (.+?)>\Z/, 1]})"
|
1292
|
+
864
|
1293
|
+
865 return true
|
1294
|
+
866 end
|
1295
|
+
867
|
1296
|
+
868 # Specifies that this Module/Class fulfills one or more contracts. The contracts
|
1297
|
+
869 # will automatically be verified after an instance has been successfully created.
|
1298
|
+
870 # This only actually does the checks when Contract.check_fulfills is enabled.
|
1299
|
+
871 # The method will return +true+ in case it actually inserted the check logic and
|
1300
|
+
872 # +nil+ in case it didn't.
|
1301
|
+
873 #
|
1302
|
+
874 # Note that this works by overriding the #initialize method which means that you
|
1303
|
+
875 # should either add the fulfills statements after your initialize method or call
|
1304
|
+
876 # the previously defined initialize method from your new one.
|
1305
|
+
877 def fulfills(*contracts)
|
1306
|
+
878 return unless Contract.check_fulfills?
|
1307
|
+
879
|
1308
|
+
880 contracts.each do |contract|
|
1309
|
+
881 contract.implications.each do |implication|
|
1310
|
+
882 include implication
|
1311
|
+
883 end
|
1312
|
+
884 end
|
1313
|
+
885
|
1314
|
+
886 old_method = instance_method(:initialize)
|
1315
|
+
887 remove_method(:initialize) if instance_methods(false).include?("initialize")
|
1316
|
+
888
|
1317
|
+
889 # Keep visible references around so that the GC will not eat these up.
|
1318
|
+
890 @fulfills ||= Array.new
|
1319
|
+
891 @fulfills << [contracts, old_method]
|
1320
|
+
892
|
1321
|
+
893 # Have to use class_eval because define_method does not allow methods to take
|
1322
|
+
894 # blocks. This can be cleaned up when Ruby 1.9 has become current.
|
1323
|
+
895 class_eval %{
|
1324
|
+
896 def initialize(*args, &block)
|
1325
|
+
897 ObjectSpace._id2ref(#{old_method.object_id}).bind(self).call(*args, &block)
|
1326
|
+
898 ObjectSpace._id2ref(#{contracts.object_id}).each do |contract|
|
1327
|
+
899 contract.enforce self
|
1328
|
+
900 end
|
1329
|
+
901 end
|
1330
|
+
902 }, "(post initialization contract check for #{self.inspect})"
|
1331
|
+
903
|
1332
|
+
904 return true
|
1333
|
+
905 end
|
1334
|
+
906 end
|
1335
|
+
907
|
1336
|
+
908
|
1337
|
+
909 module Kernel
|
1338
|
+
910 # Adds an adaption route from the specified type to the specified type.
|
1339
|
+
911 # Basic usage looks like this:
|
1340
|
+
912 # adaption :from => StringIO, :to => String, :via => :read
|
1341
|
+
913 #
|
1342
|
+
914 # This method takes various options. Here's a complete list:
|
1343
|
+
915 #
|
1344
|
+
916 # <code>:from</code>::
|
1345
|
+
917 # The type that can be converted from. Defaults to +self+ meaning you
|
1346
|
+
918 # can safely omit it in Class, Module or Contract context.
|
1347
|
+
919 #
|
1348
|
+
920 # <code>:to</code>::
|
1349
|
+
921 # The type that can be converted to. Defaults to +self+ meaning you
|
1350
|
+
922 # can safely omit it in Class, Module or Contract context.
|
1351
|
+
923 #
|
1352
|
+
924 # Note that you need to specify either <code>:from</code> or
|
1353
|
+
925 # <code>:to</code>.
|
1354
|
+
926 #
|
1355
|
+
927 # <code>:via</code>::
|
1356
|
+
928 # How the <code>:from</code> type will be converted to the
|
1357
|
+
929 # <code>:to</code> type. If this is a Symbol the conversion will be
|
1358
|
+
930 # done by invoking the method identified by that Symbol on the
|
1359
|
+
931 # source object. Otherwise this should be something that responds to
|
1360
|
+
932 # the +call+ method (for example Methods and Procs) which will get
|
1361
|
+
933 # the source object as its argument and which should return the
|
1362
|
+
934 # target object.
|
1363
|
+
935 #
|
1364
|
+
936 # <code>:if</code>::
|
1365
|
+
937 # The conversion can only be performed if this condition is met.
|
1366
|
+
938 # This can either be something that implements the === case
|
1367
|
+
939 # equivalence operator or something that implements the +call+
|
1368
|
+
940 # method. So Methods, Procs, Modules, Classes and Contracts all
|
1369
|
+
941 # make sense in this context. You can also specify a Symbol in
|
1370
|
+
942 # which case the conversion can only be performed if the source
|
1371
|
+
943 # object responds to the method identified by that Symbol.
|
1372
|
+
944 #
|
1373
|
+
945 # Note that the <code>:if</code> option will default to the same
|
1374
|
+
946 # value as the <code>:via</code> option if the <code>:via</code>
|
1375
|
+
947 # option is a Symbol.
|
1376
|
+
948 #
|
1377
|
+
949 # If you invoke this method with a block it will be used instead of
|
1378
|
+
950 # the <code>:via</code> option.
|
1379
|
+
951 #
|
1380
|
+
952 # See Contract.adapt for how conversion look-ups are performed.
|
1381
|
+
953 def adaption(options = {}, &block) # :yield: source_object
|
1382
|
+
954 options = {
|
1383
|
+
955 :from => self,
|
1384
|
+
956 :to => self
|
1385
|
+
957 }.merge(options)
|
1386
|
+
958
|
1387
|
+
959 if block then
|
1388
|
+
960 if options.include?(:via) then
|
1389
|
+
961 raise(ArgumentError, "Can't use both block and :via")
|
1390
|
+
962 else
|
1391
|
+
963 options[:via] = block
|
1392
|
+
964 end
|
1393
|
+
965 end
|
1394
|
+
966
|
1395
|
+
967 if options[:via].respond_to?(:to_sym) then
|
1396
|
+
968 options[:via] = options[:via].to_sym
|
1397
|
+
969 end
|
1398
|
+
970
|
1399
|
+
971 options[:if] ||= options[:via] if options[:via].is_a?(Symbol)
|
1400
|
+
972
|
1401
|
+
973 if options[:via].is_a?(Symbol) then
|
1402
|
+
974 symbol = options[:via]
|
1403
|
+
975 options[:via] = lambda { |obj| obj.send(symbol) }
|
1404
|
+
976 end
|
1405
|
+
977
|
1406
|
+
978 if options[:if].respond_to?(:to_sym) then
|
1407
|
+
979 options[:if] = options[:if].to_sym
|
1408
|
+
980 end
|
1409
|
+
981
|
1410
|
+
982 if options[:if].is_a?(Symbol) then
|
1411
|
+
983 options[:if] = Contract::Check::Quack[options[:if]]
|
1412
|
+
984 elsif options[:if].respond_to?(:call) then
|
1413
|
+
985 callable = options[:if]
|
1414
|
+
986 options[:if] = Contract::Check.block { |obj| callable.call(obj) }
|
1415
|
+
987 end
|
1416
|
+
988
|
1417
|
+
989 if options[:from] == self and options[:to] == self then
|
1418
|
+
990 raise(ArgumentError, "Need to specify either :from or :to")
|
1419
|
+
991 elsif options[:from] == options[:to] then
|
1420
|
+
992 raise(ArgumentError, "Self-adaption: :from and :to both are " +
|
1421
|
+
993 options[:to].inspect)
|
1422
|
+
994 end
|
1423
|
+
995
|
1424
|
+
996 unless options[:via]
|
1425
|
+
997 raise(ArgumentError, "Need to specify how to adapt (use :via or block)")
|
1426
|
+
998 end
|
1427
|
+
999
|
1428
|
+
1000 Contract.adaptions[options[:to]] << options
|
1429
|
+
1001 end
|
1430
|
+
1002
|
1431
|
+
1003 # Built-in adaption routes that Ruby already uses in its C code.
|
1432
|
+
1004 adaption :to => Symbol, :via => :to_sym
|
1433
|
+
1005 adaption :to => String, :via => :to_str
|
1434
|
+
1006 adaption :to => Array, :via => :to_ary
|
1435
|
+
1007 adaption :to => Integer, :via => :to_int
|
1436
|
+
1008 end
|
1437
|
+
</span></pre>
|
1438
|
+
<hr />
|
1439
|
+
<p>
|
1440
|
+
<a href="http://validator.w3.org/check/referer">
|
1441
|
+
<img src="http://www.w3.org/Icons/valid-xhtml11"
|
1442
|
+
alt="Valid XHTML 1.1!" height="31" width="88" />
|
1443
|
+
</a>
|
1444
|
+
<a href="http://jigsaw.w3.org/css-validator/">
|
1445
|
+
<img style="border:0;width:88px;height:31px"
|
1446
|
+
src="http://jigsaw.w3.org/css-validator/images/vcss"
|
1447
|
+
alt="Valid CSS!" />
|
1448
|
+
</a>
|
1449
|
+
</p>
|
1450
|
+
</body></html>
|