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.
Files changed (74) hide show
  1. data/COPYING +56 -0
  2. data/Manifest +85 -0
  3. data/README +32 -0
  4. data/TODO +83 -0
  5. data/doc/classes/Contract.html +599 -0
  6. data/doc/classes/Contract/Check.html +229 -0
  7. data/doc/classes/Contract/Check/All.html +172 -0
  8. data/doc/classes/Contract/Check/Any.html +172 -0
  9. data/doc/classes/Contract/Check/Block.html +172 -0
  10. data/doc/classes/Contract/Check/None.html +173 -0
  11. data/doc/classes/Contract/Check/Quack.html +172 -0
  12. data/doc/classes/Contract/ContractError.html +151 -0
  13. data/doc/classes/Contract/ContractException.html +162 -0
  14. data/doc/classes/Contract/ContractMismatch.html +134 -0
  15. data/doc/classes/Kernel.html +256 -0
  16. data/doc/classes/Method.html +135 -0
  17. data/doc/classes/MethodSignatureMixin.html +208 -0
  18. data/doc/classes/Module.html +526 -0
  19. data/doc/created.rid +1 -0
  20. data/doc/dot/f_0.dot +14 -0
  21. data/doc/dot/f_0.png +0 -0
  22. data/doc/dot/f_1.dot +14 -0
  23. data/doc/dot/f_1.png +0 -0
  24. data/doc/dot/f_2.dot +14 -0
  25. data/doc/dot/f_2.png +0 -0
  26. data/doc/dot/f_3.dot +112 -0
  27. data/doc/dot/f_3.png +0 -0
  28. data/doc/dot/f_4.dot +62 -0
  29. data/doc/dot/f_4.png +0 -0
  30. data/doc/dot/f_5.dot +62 -0
  31. data/doc/dot/f_5.png +0 -0
  32. data/doc/dot/f_6.dot +224 -0
  33. data/doc/dot/f_6.png +0 -0
  34. data/doc/dot/f_6_0.dot +24 -0
  35. data/doc/dot/f_6_0.png +0 -0
  36. data/doc/dot/f_6_1.dot +24 -0
  37. data/doc/dot/f_6_1.png +0 -0
  38. data/doc/dot/f_7.dot +62 -0
  39. data/doc/dot/f_7.png +0 -0
  40. data/doc/files/COPYING.html +168 -0
  41. data/doc/files/README.html +146 -0
  42. data/doc/files/TODO.html +240 -0
  43. data/doc/files/lib/contract/assertions_rb.html +118 -0
  44. data/doc/files/lib/contract/exception_rb.html +125 -0
  45. data/doc/files/lib/contract/integration_rb.html +130 -0
  46. data/doc/files/lib/contract/overrides_rb.html +118 -0
  47. data/doc/files/lib/contract_rb.html +127 -0
  48. data/doc/fr_class_index.html +40 -0
  49. data/doc/fr_file_index.html +34 -0
  50. data/doc/fr_method_index.html +45 -0
  51. data/doc/index.html +24 -0
  52. data/doc/rdoc-style.css +208 -0
  53. data/lib/contract.rb +146 -0
  54. data/lib/contract/assertions.rb +42 -0
  55. data/lib/contract/exception.rb +95 -0
  56. data/lib/contract/integration.rb +664 -0
  57. data/lib/contract/overrides.rb +41 -0
  58. data/setup.rb +1360 -0
  59. data/test/coverage/_-lib-contract-assertions_rb.html +526 -0
  60. data/test/coverage/_-lib-contract-exception_rb.html +632 -0
  61. data/test/coverage/_-lib-contract-integration_rb.html +1450 -0
  62. data/test/coverage/_-lib-contract-overrides_rb.html +524 -0
  63. data/test/coverage/_-lib-contract_rb.html +724 -0
  64. data/test/coverage/__-lib-contract-assertions_rb.html +484 -0
  65. data/test/coverage/__-lib-contract-exception_rb.html +537 -0
  66. data/test/coverage/__-lib-contract-integration_rb.html +946 -0
  67. data/test/coverage/__-lib-contract-overrides_rb.html +483 -0
  68. data/test/coverage/__-lib-contract_rb.html +583 -0
  69. data/test/coverage/index.html +93 -0
  70. data/test/tc_all.rb +8 -0
  71. data/test/tc_contract.rb +109 -0
  72. data/test/tc_exception.rb +43 -0
  73. data/test/tc_integration.rb +357 -0
  74. 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 &lt; 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 &lt;&lt; self
453
+ 25 alias :[] :new
454
+ </span><span class="inferred"> 26 end
455
+ 27
456
+ </span><span class="marked"> 28 def initialize(*args, &amp;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 &gt; 0 }
464
+ </span><span class="marked"> 36 class Block &lt; 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(&amp;block) # :yields: arg
471
+ 43 Block.new(&amp;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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt;&lt; 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 &lt;code&gt;:no_adaption&lt;/code&gt; 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 (&lt;code&gt;:any&lt;/code&gt; 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 &lt;code&gt;:repeated&lt;/code&gt; or &lt;code&gt;:allow_trailing&lt;/code&gt;
584
+ 156 # options the method will take exactly as many arguments as there are type
585
+ 157 # specifiers which means that &lt;code&gt;signature :a_method&lt;/code&gt; 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 # &lt;code&gt;:return&lt;/code&gt;::
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 # &lt;code&gt;:block&lt;/code&gt;::
607
+ 179 # +true+ or +false+ -- whether the method must take a block or not. So
608
+ 180 # specifying &lt;code&gt;:block =&gt; false&lt;/code&gt; enforces that the method is not
609
+ 181 # allowed to have a block supplied.
610
+ 182 #
611
+ 183 # &lt;code&gt;:allow_trailing&lt;/code&gt;::
612
+ 184 # +true+ or +false+ -- whether the argument list may contain trailing,
613
+ 185 # unchecked arguments.
614
+ 186 #
615
+ 187 # &lt;code&gt;:repeated&lt;/code&gt;::
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 &lt;code&gt;:repeated&lt;/code&gt;
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 # &lt;code&gt;:no_adaption&lt;/code&gt;::
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 &gt; 0 })
638
+ 210 #
639
+ 211 # signature(:each, :block =&gt; true) # has to have block
640
+ 212 # signature(:to_i, :block =&gt; false) # not allowed to have block
641
+ 213 # signature(:to_i, :result =&gt; Fixnum) # return value must be Fixnum
642
+ 214 # signature(:zip, :allow_trailing =&gt; true) # unchecked trailing args
643
+ 215 # signature(:zip, :repeated =&gt; [Enumerable]) # repeated trailing args
644
+ 216 # signature(:zip, :repeated =&gt; Enumerable)
645
+ 217 # # foo(3, 6, 4, 7) works; foo(5), foo(3, 2) etc. don't
646
+ 218 # signature(:foo, :repeated =&gt; [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 &gt;= 0 or signature.size &lt; ~arity) then
660
+ 232 raise(ArgumentError, &quot;signature isn't compatible with arity&quot;)
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, &quot;unsupported argument specifier #{item.inspect}&quot;)
676
+ </span><span class="inferred"> 248 end
677
+ 249 end
678
+ 250
679
+ </span><span class="marked"> 251 signature.map!(&amp;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, &quot;repeated arguments may not be an empty Array&quot;)
685
+ </span><span class="inferred"> 257 else
686
+ </span><span class="marked"> 258 options[:repeated].map!(&amp;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] &lt;&lt; [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, &amp;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, &quot;no block given&quot;) unless block}
720
+ </span><span class="inferred"> 292 else
721
+ </span><span class="marked"> 293 %{raise(ArgumentError, &quot;block given&quot;) 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 = &quot;wrong number of arguments (\#{args.size} for &quot; +
728
+ </span><span class="inferred"> 300 &quot;#{signature.size})&quot;
729
+ </span><span class="marked"> 301 %{if args.size != #{signature.size} then
730
+ </span><span class="inferred"> 302 raise(ArgumentError, &quot;#{msg}&quot;)
731
+ 303 end
732
+ 304 }
733
+ </span><span class="marked"> 305 elsif signature.size &gt; 0
734
+ 306 msg = &quot;wrong number of arguments (\#{args.size} for &quot; +
735
+ </span><span class="inferred"> 307 &quot;at least #{signature.size}&quot;
736
+ </span><span class="marked"> 308 %{if args.size &lt; #{signature.size} then
737
+ </span><span class="inferred"> 309 raise(ArgumentError, &quot;#{msg}&quot;)
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 = &quot;argument #{index} (\#{arg.inspect}) does not match &quot; +
748
+ </span><span class="inferred"> 320 &quot;#{part.inspect}&quot;
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, &quot;#{msg}&quot;)
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 = &quot;argument \#{idx + #{signature.size}}&quot; +
760
+ </span><span class="inferred"> 332 &quot;(\#{arg.inspect}) does not match \#{part.inspect}&quot;
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, &quot;#{msg}&quot;)
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, &amp;block)
776
+ </span><span class="marked"> 348 #{if rt = options[:return] and rt != :any then
777
+ 349 msg = &quot;return value (\#{result.inspect}) does not match #{rt.inspect}&quot;
778
+ 350 %{type = ObjectSpace._id2ref(#{rt.object_id})
779
+ </span><span class="inferred"> 351 unless type === #{adapted[%{result}, %{type}]}
780
+ 352 raise(StandardError, &quot;#{msg}&quot;)
781
+ 353 end
782
+ 354 }
783
+ 355 end
784
+ 356 }
785
+ 357 end
786
+ 358 }
787
+ </span><span class="marked"> 359 class_eval code, &quot;(signature check for #{old_method.inspect[/: (.+?)&gt;\Z/, 1]})&quot;
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?(&quot;initialize&quot;)
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 &lt;&lt; [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, &amp;block)
821
+ 393 ObjectSpace._id2ref(#{old_method.object_id}).bind(self).call(*args, &amp;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 }, &quot;(post initialization contract check for #{self.inspect})&quot;
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 =&gt; StringIO, :to =&gt; String, :via =&gt; :read
837
+ 409 #
838
+ 410 # This method takes various options. Here's a complete list:
839
+ 411 #
840
+ 412 # &lt;code&gt;:from&lt;/code&gt;::
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 # &lt;code&gt;:to&lt;/code&gt;::
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 &lt;code&gt;:from&lt;/code&gt; or
849
+ 421 # &lt;code&gt;:to&lt;/code&gt;.
850
+ 422 #
851
+ 423 # &lt;code&gt;:via&lt;/code&gt;::
852
+ 424 # How the &lt;code&gt;:from&lt;/code&gt; type will be converted to the
853
+ 425 # &lt;code&gt;:to&lt;/code&gt; 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 # &lt;code&gt;:if&lt;/code&gt;::
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 &lt;code&gt;:if&lt;/code&gt; option will default to the same
870
+ 442 # value as the &lt;code&gt;:via&lt;/code&gt; option if the &lt;code&gt;:via&lt;/code&gt;
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 &lt;code&gt;:via&lt;/code&gt; option.
875
+ 447 #
876
+ 448 # See Contract.adapt for how conversion look-ups are performed.
877
+ </span><span class="marked"> 449 def adaption(options = {}, &amp;block) # :yield: source_object
878
+ 450 options = {
879
+ </span><span class="inferred"> 451 :from =&gt; self,
880
+ 452 :to =&gt; 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, &quot;Can't use both block and :via&quot;)
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, &quot;Need to specify either :from or :to&quot;)
915
+ 487 elsif options[:from] == options[:to] then
916
+ 488 raise(ArgumentError, &quot;Self-adaption: :from and :to both are &quot; +
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, &quot;Need to specify how to adapt (use :via or block)&quot;)
922
+ </span><span class="inferred"> 494 end
923
+ 495
924
+ </span><span class="marked"> 496 Contract.adaptions[options[:to]] &lt;&lt; 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 =&gt; Symbol, :via =&gt; :to_sym
929
+ 501 adaption :to =&gt; String, :via =&gt; :to_str
930
+ 502 adaption :to =&gt; Array, :via =&gt; :to_ary
931
+ 503 adaption :to =&gt; Integer, :via =&gt; :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 &lt; 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 &lt;&lt; self
957
+ 529 alias :[] :new
958
+ 530 end
959
+ 531
960
+ 532 def initialize(*args, &amp;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 &gt; 0 }
968
+ 540 class Block &lt; 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(&amp;block) # :yields: arg
975
+ 547 Block.new(&amp;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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt;&lt; 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 &lt;code&gt;:no_adaption&lt;/code&gt; 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 (&lt;code&gt;:any&lt;/code&gt; 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 &lt;code&gt;:repeated&lt;/code&gt; or &lt;code&gt;:allow_trailing&lt;/code&gt;
1088
+ 660 # options the method will take exactly as many arguments as there are type
1089
+ 661 # specifiers which means that &lt;code&gt;signature :a_method&lt;/code&gt; 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 # &lt;code&gt;:return&lt;/code&gt;::
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 # &lt;code&gt;:block&lt;/code&gt;::
1111
+ 683 # +true+ or +false+ -- whether the method must take a block or not. So
1112
+ 684 # specifying &lt;code&gt;:block =&gt; false&lt;/code&gt; enforces that the method is not
1113
+ 685 # allowed to have a block supplied.
1114
+ 686 #
1115
+ 687 # &lt;code&gt;:allow_trailing&lt;/code&gt;::
1116
+ 688 # +true+ or +false+ -- whether the argument list may contain trailing,
1117
+ 689 # unchecked arguments.
1118
+ 690 #
1119
+ 691 # &lt;code&gt;:repeated&lt;/code&gt;::
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 &lt;code&gt;:repeated&lt;/code&gt;
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 # &lt;code&gt;:no_adaption&lt;/code&gt;::
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 &gt; 0 })
1142
+ 714 #
1143
+ 715 # signature(:each, :block =&gt; true) # has to have block
1144
+ 716 # signature(:to_i, :block =&gt; false) # not allowed to have block
1145
+ 717 # signature(:to_i, :result =&gt; Fixnum) # return value must be Fixnum
1146
+ 718 # signature(:zip, :allow_trailing =&gt; true) # unchecked trailing args
1147
+ 719 # signature(:zip, :repeated =&gt; [Enumerable]) # repeated trailing args
1148
+ 720 # signature(:zip, :repeated =&gt; Enumerable)
1149
+ 721 # # foo(3, 6, 4, 7) works; foo(5), foo(3, 2) etc. don't
1150
+ 722 # signature(:foo, :repeated =&gt; [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 &gt;= 0 or signature.size &lt; ~arity) then
1164
+ 736 raise(ArgumentError, &quot;signature isn't compatible with arity&quot;)
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, &quot;unsupported argument specifier #{item.inspect}&quot;)
1180
+ 752 end
1181
+ 753 end
1182
+ 754
1183
+ 755 signature.map!(&amp;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, &quot;repeated arguments may not be an empty Array&quot;)
1189
+ 761 else
1190
+ 762 options[:repeated].map!(&amp;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] &lt;&lt; [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, &amp;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, &quot;no block given&quot;) unless block}
1224
+ 796 else
1225
+ 797 %{raise(ArgumentError, &quot;block given&quot;) 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 = &quot;wrong number of arguments (\#{args.size} for &quot; +
1232
+ 804 &quot;#{signature.size})&quot;
1233
+ 805 %{if args.size != #{signature.size} then
1234
+ 806 raise(ArgumentError, &quot;#{msg}&quot;)
1235
+ 807 end
1236
+ 808 }
1237
+ 809 elsif signature.size &gt; 0
1238
+ 810 msg = &quot;wrong number of arguments (\#{args.size} for &quot; +
1239
+ 811 &quot;at least #{signature.size}&quot;
1240
+ 812 %{if args.size &lt; #{signature.size} then
1241
+ 813 raise(ArgumentError, &quot;#{msg}&quot;)
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 = &quot;argument #{index} (\#{arg.inspect}) does not match &quot; +
1252
+ 824 &quot;#{part.inspect}&quot;
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, &quot;#{msg}&quot;)
1257
+ 829 end
1258
+ 830 }
1259
+ 831 end
1260
+ 832 }
1261
+ 833
1262
+ 834 #{if repeated = options[:repeated] then
1263
+ 835 msg = &quot;argument \#{idx + #{signature.size}}&quot; +
1264
+ 836 &quot;(\#{arg.inspect}) does not match \#{part.inspect}&quot;
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, &quot;#{msg}&quot;)
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, &amp;block)
1280
+ 852 #{if rt = options[:return] and rt != :any then
1281
+ 853 msg = &quot;return value (\#{result.inspect}) does not match #{rt.inspect}&quot;
1282
+ 854 %{type = ObjectSpace._id2ref(#{rt.object_id})
1283
+ 855 unless type === #{adapted[%{result}, %{type}]}
1284
+ 856 raise(StandardError, &quot;#{msg}&quot;)
1285
+ 857 end
1286
+ 858 }
1287
+ 859 end
1288
+ 860 }
1289
+ 861 end
1290
+ 862 }
1291
+ 863 class_eval code, &quot;(signature check for #{old_method.inspect[/: (.+?)&gt;\Z/, 1]})&quot;
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?(&quot;initialize&quot;)
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 &lt;&lt; [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, &amp;block)
1325
+ 897 ObjectSpace._id2ref(#{old_method.object_id}).bind(self).call(*args, &amp;block)
1326
+ 898 ObjectSpace._id2ref(#{contracts.object_id}).each do |contract|
1327
+ 899 contract.enforce self
1328
+ 900 end
1329
+ 901 end
1330
+ 902 }, &quot;(post initialization contract check for #{self.inspect})&quot;
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 =&gt; StringIO, :to =&gt; String, :via =&gt; :read
1341
+ 913 #
1342
+ 914 # This method takes various options. Here's a complete list:
1343
+ 915 #
1344
+ 916 # &lt;code&gt;:from&lt;/code&gt;::
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 # &lt;code&gt;:to&lt;/code&gt;::
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 &lt;code&gt;:from&lt;/code&gt; or
1353
+ 925 # &lt;code&gt;:to&lt;/code&gt;.
1354
+ 926 #
1355
+ 927 # &lt;code&gt;:via&lt;/code&gt;::
1356
+ 928 # How the &lt;code&gt;:from&lt;/code&gt; type will be converted to the
1357
+ 929 # &lt;code&gt;:to&lt;/code&gt; 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 # &lt;code&gt;:if&lt;/code&gt;::
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 &lt;code&gt;:if&lt;/code&gt; option will default to the same
1374
+ 946 # value as the &lt;code&gt;:via&lt;/code&gt; option if the &lt;code&gt;:via&lt;/code&gt;
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 &lt;code&gt;:via&lt;/code&gt; option.
1379
+ 951 #
1380
+ 952 # See Contract.adapt for how conversion look-ups are performed.
1381
+ 953 def adaption(options = {}, &amp;block) # :yield: source_object
1382
+ 954 options = {
1383
+ 955 :from =&gt; self,
1384
+ 956 :to =&gt; self
1385
+ 957 }.merge(options)
1386
+ 958
1387
+ 959 if block then
1388
+ 960 if options.include?(:via) then
1389
+ 961 raise(ArgumentError, &quot;Can't use both block and :via&quot;)
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, &quot;Need to specify either :from or :to&quot;)
1419
+ 991 elsif options[:from] == options[:to] then
1420
+ 992 raise(ArgumentError, &quot;Self-adaption: :from and :to both are &quot; +
1421
+ 993 options[:to].inspect)
1422
+ 994 end
1423
+ 995
1424
+ 996 unless options[:via]
1425
+ 997 raise(ArgumentError, &quot;Need to specify how to adapt (use :via or block)&quot;)
1426
+ 998 end
1427
+ 999
1428
+ 1000 Contract.adaptions[options[:to]] &lt;&lt; options
1429
+ 1001 end
1430
+ 1002
1431
+ 1003 # Built-in adaption routes that Ruby already uses in its C code.
1432
+ 1004 adaption :to =&gt; Symbol, :via =&gt; :to_sym
1433
+ 1005 adaption :to =&gt; String, :via =&gt; :to_str
1434
+ 1006 adaption :to =&gt; Array, :via =&gt; :to_ary
1435
+ 1007 adaption :to =&gt; Integer, :via =&gt; :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>