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,724 @@
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.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.rb</tt></td></tr>
425
+ <tr><td>total coverage</td><td>48.9</td></tr>
426
+ <tr><td>code coverage</td><td>47.5</td></tr>
427
+ </table>
428
+ </div>
429
+ <pre><span class="inferred"> 1 # Contains the main implementation of the Contract class.
430
+ 2 # :main:Contract
431
+ 3
432
+ </span><span class="marked"> 4 require 'test/unit/testcase'
433
+ 5 require 'test/unit/testresult'
434
+ 6 require 'test/unit/testsuite'
435
+ </span><span class="inferred"> 7
436
+ </span><span class="marked"> 8 require 'contract/exception'
437
+ 9 require 'contract/overrides'
438
+ 10 require 'contract/assertions'
439
+ 11 require 'contract/integration'
440
+ </span><span class="inferred"> 12
441
+ 13 # Represents a contract between Objects as a collection of test cases.
442
+ 14 # Objects are said to fulfill a contract if all test cases suceed. This
443
+ 15 # is useful for ensuring that Objects your code is getting behave in a
444
+ 16 # way that you expect them to behave so you can fail early or execute
445
+ 17 # different logic for Objects with different interfaces.
446
+ 18 #
447
+ 19 # The tests of the test suite will be run on a copy of the tested Object
448
+ 20 # so you can safely test its behavior without having to fear data loss.
449
+ 21 # By default Contracts obtain deep copies of Objects by serializing and
450
+ 22 # unserializing them with Ruby's +Marshal+ functionality. This will work
451
+ 23 # in most cases but can fail for Objects containing unserializable parts
452
+ 24 # like Procs, Files or Sockets. In those cases it is currently of your
453
+ 25 # responsibility to provide a fitting implementation by overwriting the
454
+ 26 # Contract.deep_copy method. In the future the contract library might
455
+ 27 # provide different implementations of it via Ruby's mixin mechanism.
456
+ </span><span class="marked"> 28 class Contract &lt; Test::Unit::TestCase
457
+ 29 id = %q$Id: contract.rb 110 2005-02-05 22:33:56Z flgr $
458
+ </span><span class="inferred"> 30 # The Version of the contract library you are using.
459
+ </span><span class="marked"> 31 Version = id.split(&quot; &quot;)[2].to_i unless defined?(Version)
460
+ </span><span class="inferred"> 32
461
+ 33 # Returns true if the given object fulfills this contract.
462
+ 34 # This is useful for implementing dispatching mechanisms where you
463
+ 35 # want to hit different code branches based on whether an Object has
464
+ 36 # one or another interface.
465
+ </span><span class="marked"> 37 def self.fulfilled_by?(object)
466
+ 38 self.test(object).nil?
467
+ </span><span class="inferred"> 39 end
468
+ 40
469
+ </span><span class="marked"> 41 class &lt;&lt; self
470
+ </span><span class="inferred"> 42 # You can use contracts in +case+ ... +when+ statements or in
471
+ 43 # Module#signature checks. For example:
472
+ 44 # case obj
473
+ 45 # when Numeric then obj + 1
474
+ 46 # when ListContract then obj + [1]
475
+ 47 # end
476
+ </span><span class="marked"> 48 alias :=== :fulfilled_by?
477
+ </span><span class="inferred"> 49 end
478
+ 50
479
+ 51 # Enforces that object implements this contract. If it does not an
480
+ 52 # Exception will be raised. This is useful for example useful when you
481
+ 53 # need to ensure that the arguments given to a method fulfill a given
482
+ 54 # contract.
483
+ 55 #
484
+ 56 # Note that using Module#enforce is a higher-level way of checking
485
+ 57 # arguments and return values for the conformance of a given type. You
486
+ 58 # might however still want to use Contract.enforce directly when you
487
+ 59 # need more flexibility.
488
+ </span><span class="marked"> 60 def self.enforce(object)
489
+ 61 reason = self.test(object)
490
+ 62 raise reason if reason
491
+ </span><span class="inferred"> 63 end
492
+ 64
493
+ 65 # Tests whether the given Object fulfils this contract.
494
+ 66 #
495
+ 67 # Note: This will return the first reason for the Object not fulfilling
496
+ 68 # the contract or +nil+ in case it fulfills it.
497
+ </span><span class="marked"> 69 def self.test(object, return_all = false)
498
+ 70 reasons = []
499
+ </span><span class="inferred"> 71
500
+ </span><span class="marked"> 72 result = Test::Unit::TestResult.new
501
+ 73 result.add_listener(Test::Unit::TestResult::FAULT) do |fault|
502
+ 74 reason = Contract.fault_to_exception(fault, object, self)
503
+ 75 return reason unless return_all
504
+ 76 reasons &lt;&lt; reason
505
+ </span><span class="inferred"> 77 end
506
+ 78
507
+ </span><span class="marked"> 79 self.suite.run(result, deep_copy(object))
508
+ </span><span class="inferred"> 80
509
+ </span><span class="marked"> 81 return reasons unless result.passed?
510
+ </span><span class="inferred"> 82 end
511
+ 83
512
+ 84 # Same as Contract.test, but will return all reasons for the Object not
513
+ 85 # fulfilling the contract in an Array or nil in case of fulfillment.
514
+ 86 # (as an Array of Exceptions) or +nil+ in the case it does fulfill it.
515
+ </span><span class="marked"> 87 def self.test_all(object)
516
+ 88 test(object, true)
517
+ </span><span class="inferred"> 89 end
518
+ 90
519
+ 91 # This method is used internally for getting a copy of Objects that
520
+ 92 # the contract is checked against. By default it uses Ruby's +Marshal+
521
+ 93 # functionality for obtaining a copy, but this can fail if the Object
522
+ 94 # contains unserializable parts like Procs, Files or Sockets. It is
523
+ 95 # currently your responsibility to provide a fitting implementation
524
+ 96 # of this by overwriting the method in case the default implementation
525
+ 97 # does not work for you. In the future the contract library might offer
526
+ 98 # different implementations for this via Ruby's mixin mechanism.
527
+ </span><span class="marked"> 99 def self.deep_copy(object)
528
+ 100 Marshal.load(Marshal.dump(object))
529
+ </span><span class="inferred">101 end
530
+ 102
531
+ 103 # Fulfilling this Contract (via Module#fulfills) implies that the Object
532
+ 104 # is automatically compatible with the specified mixins which will then
533
+ 105 # be included automatically. For example the Enumerable relationship
534
+ 106 # could be expressed like this:
535
+ 107 #
536
+ 108 # class EnumerableContract &lt; Contract
537
+ 109 # provides :each
538
+ 110 # implies Enumerable
539
+ 111 # end
540
+ </span><span class="marked">112 def self.implies(*mixins)
541
+ 113 mixins.each do |mixin|
542
+ 114 if not mixin.is_a?(Module) then
543
+ 115 raise(TypeError, &quot;wrong argument type #{mixin.class} for &quot; +
544
+ </span><span class="inferred">116 &quot;#{mixin.inspect} (expected Module)&quot;)
545
+ 117 end
546
+ 118 end
547
+ 119
548
+ </span><span class="marked">120 @implications ||= Array.new
549
+ 121 @implications += mixins
550
+ </span><span class="inferred">122 end
551
+ 123
552
+ </span><span class="marked">124 class &lt;&lt; self
553
+ </span><span class="inferred">125 # Returns all implications of a given contract that were stated via
554
+ 126 # calling Contract.implies.
555
+ </span><span class="marked">127 attr_reader :implications
556
+ </span><span class="inferred">128 end
557
+ 129
558
+ </span><span class="marked">130 def self.implications() # :nodoc:
559
+ 131 @implications ||= Array.new
560
+ </span><span class="inferred">132
561
+ </span><span class="marked">133 ancestors[1 .. -1].inject(@implications) do |result, ancestor|
562
+ 134 if ancestor.respond_to?(:implications) then
563
+ 135 ancestor.implications + result
564
+ </span><span class="inferred">136 else
565
+ </span><span class="marked">137 result
566
+ </span><span class="inferred">138 end
567
+ </span><span class="false">139 end.uniq
568
+ 140 end
569
+ 141 end
570
+ 142 # Contains the main implementation of the Contract class.
571
+ 143 # :main:Contract
572
+ 144
573
+ 145 require 'test/unit/testcase'
574
+ 146 require 'test/unit/testresult'
575
+ 147 require 'test/unit/testsuite'
576
+ 148
577
+ 149 require 'contract/exception'
578
+ 150 require 'contract/overrides'
579
+ 151 require 'contract/assertions'
580
+ 152 require 'contract/integration'
581
+ 153
582
+ 154 # Represents a contract between Objects as a collection of test cases.
583
+ 155 # Objects are said to fulfill a contract if all test cases suceed. This
584
+ 156 # is useful for ensuring that Objects your code is getting behave in a
585
+ 157 # way that you expect them to behave so you can fail early or execute
586
+ 158 # different logic for Objects with different interfaces.
587
+ 159 #
588
+ 160 # The tests of the test suite will be run on a copy of the tested Object
589
+ 161 # so you can safely test its behavior without having to fear data loss.
590
+ 162 # By default Contracts obtain deep copies of Objects by serializing and
591
+ 163 # unserializing them with Ruby's +Marshal+ functionality. This will work
592
+ 164 # in most cases but can fail for Objects containing unserializable parts
593
+ 165 # like Procs, Files or Sockets. In those cases it is currently of your
594
+ 166 # responsibility to provide a fitting implementation by overwriting the
595
+ 167 # Contract.deep_copy method. In the future the contract library might
596
+ 168 # provide different implementations of it via Ruby's mixin mechanism.
597
+ 169 class Contract &lt; Test::Unit::TestCase
598
+ 170 id = %q$Id: contract.rb 110 2005-02-05 22:33:56Z flgr $
599
+ 171 # The Version of the contract library you are using.
600
+ 172 Version = id.split(&quot; &quot;)[2].to_i unless defined?(Version)
601
+ 173
602
+ 174 # Returns true if the given object fulfills this contract.
603
+ 175 # This is useful for implementing dispatching mechanisms where you
604
+ 176 # want to hit different code branches based on whether an Object has
605
+ 177 # one or another interface.
606
+ 178 def self.fulfilled_by?(object)
607
+ 179 self.test(object).nil?
608
+ 180 end
609
+ 181
610
+ 182 class &lt;&lt; self
611
+ 183 # You can use contracts in +case+ ... +when+ statements or in
612
+ 184 # Module#signature checks. For example:
613
+ 185 # case obj
614
+ 186 # when Numeric then obj + 1
615
+ 187 # when ListContract then obj + [1]
616
+ 188 # end
617
+ 189 alias :=== :fulfilled_by?
618
+ 190 end
619
+ 191
620
+ 192 # Enforces that object implements this contract. If it does not an
621
+ 193 # Exception will be raised. This is useful for example useful when you
622
+ 194 # need to ensure that the arguments given to a method fulfill a given
623
+ 195 # contract.
624
+ 196 #
625
+ 197 # Note that using Module#enforce is a higher-level way of checking
626
+ 198 # arguments and return values for the conformance of a given type. You
627
+ 199 # might however still want to use Contract.enforce directly when you
628
+ 200 # need more flexibility.
629
+ 201 def self.enforce(object)
630
+ 202 reason = self.test(object)
631
+ 203 raise reason if reason
632
+ 204 end
633
+ 205
634
+ 206 # Tests whether the given Object fulfils this contract.
635
+ 207 #
636
+ 208 # Note: This will return the first reason for the Object not fulfilling
637
+ 209 # the contract or +nil+ in case it fulfills it.
638
+ 210 def self.test(object, return_all = false)
639
+ 211 reasons = []
640
+ 212
641
+ 213 result = Test::Unit::TestResult.new
642
+ 214 result.add_listener(Test::Unit::TestResult::FAULT) do |fault|
643
+ 215 reason = Contract.fault_to_exception(fault, object, self)
644
+ 216 return reason unless return_all
645
+ 217 reasons &lt;&lt; reason
646
+ 218 end
647
+ 219
648
+ 220 self.suite.run(result, deep_copy(object))
649
+ 221
650
+ 222 return reasons unless result.passed?
651
+ 223 end
652
+ 224
653
+ 225 # Same as Contract.test, but will return all reasons for the Object not
654
+ 226 # fulfilling the contract in an Array or nil in case of fulfillment.
655
+ 227 # (as an Array of Exceptions) or +nil+ in the case it does fulfill it.
656
+ 228 def self.test_all(object)
657
+ 229 test(object, true)
658
+ 230 end
659
+ 231
660
+ 232 # This method is used internally for getting a copy of Objects that
661
+ 233 # the contract is checked against. By default it uses Ruby's +Marshal+
662
+ 234 # functionality for obtaining a copy, but this can fail if the Object
663
+ 235 # contains unserializable parts like Procs, Files or Sockets. It is
664
+ 236 # currently your responsibility to provide a fitting implementation
665
+ 237 # of this by overwriting the method in case the default implementation
666
+ 238 # does not work for you. In the future the contract library might offer
667
+ 239 # different implementations for this via Ruby's mixin mechanism.
668
+ 240 def self.deep_copy(object)
669
+ 241 Marshal.load(Marshal.dump(object))
670
+ 242 end
671
+ 243
672
+ 244 # Fulfilling this Contract (via Module#fulfills) implies that the Object
673
+ 245 # is automatically compatible with the specified mixins which will then
674
+ 246 # be included automatically. For example the Enumerable relationship
675
+ 247 # could be expressed like this:
676
+ 248 #
677
+ 249 # class EnumerableContract &lt; Contract
678
+ 250 # provides :each
679
+ 251 # implies Enumerable
680
+ 252 # end
681
+ 253 def self.implies(*mixins)
682
+ 254 mixins.each do |mixin|
683
+ 255 if not mixin.is_a?(Module) then
684
+ 256 raise(TypeError, &quot;wrong argument type #{mixin.class} for &quot; +
685
+ 257 &quot;#{mixin.inspect} (expected Module)&quot;)
686
+ 258 end
687
+ 259 end
688
+ 260
689
+ 261 @implications ||= Array.new
690
+ 262 @implications += mixins
691
+ 263 end
692
+ 264
693
+ 265 class &lt;&lt; self
694
+ 266 # Returns all implications of a given contract that were stated via
695
+ 267 # calling Contract.implies.
696
+ 268 attr_reader :implications
697
+ 269 end
698
+ 270
699
+ 271 def self.implications() # :nodoc:
700
+ 272 @implications ||= Array.new
701
+ 273
702
+ 274 ancestors[1 .. -1].inject(@implications) do |result, ancestor|
703
+ 275 if ancestor.respond_to?(:implications) then
704
+ 276 ancestor.implications + result
705
+ 277 else
706
+ 278 result
707
+ 279 end
708
+ 280 end.uniq
709
+ 281 end
710
+ 282 end
711
+ </span></pre>
712
+ <hr />
713
+ <p>
714
+ <a href="http://validator.w3.org/check/referer">
715
+ <img src="http://www.w3.org/Icons/valid-xhtml11"
716
+ alt="Valid XHTML 1.1!" height="31" width="88" />
717
+ </a>
718
+ <a href="http://jigsaw.w3.org/css-validator/">
719
+ <img style="border:0;width:88px;height:31px"
720
+ src="http://jigsaw.w3.org/css-validator/images/vcss"
721
+ alt="Valid CSS!" />
722
+ </a>
723
+ </p>
724
+ </body></html>