ruby-contract 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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>