contracts-lite 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.markdown +80 -0
- data/Gemfile +16 -0
- data/LICENSE +23 -0
- data/README.md +102 -0
- data/TODO.markdown +6 -0
- data/TUTORIAL.md +747 -0
- data/benchmarks/bench.rb +67 -0
- data/benchmarks/hash.rb +69 -0
- data/benchmarks/invariants.rb +91 -0
- data/benchmarks/io.rb +62 -0
- data/benchmarks/wrap_test.rb +57 -0
- data/contracts.gemspec +13 -0
- data/lib/contracts.rb +231 -0
- data/lib/contracts/builtin_contracts.rb +541 -0
- data/lib/contracts/call_with.rb +97 -0
- data/lib/contracts/core.rb +52 -0
- data/lib/contracts/decorators.rb +47 -0
- data/lib/contracts/engine.rb +26 -0
- data/lib/contracts/engine/base.rb +136 -0
- data/lib/contracts/engine/eigenclass.rb +50 -0
- data/lib/contracts/engine/target.rb +70 -0
- data/lib/contracts/error_formatter.rb +121 -0
- data/lib/contracts/errors.rb +71 -0
- data/lib/contracts/formatters.rb +134 -0
- data/lib/contracts/invariants.rb +68 -0
- data/lib/contracts/method_handler.rb +195 -0
- data/lib/contracts/method_reference.rb +100 -0
- data/lib/contracts/support.rb +59 -0
- data/lib/contracts/validators.rb +139 -0
- data/lib/contracts/version.rb +3 -0
- data/script/rubocop +7 -0
- data/spec/builtin_contracts_spec.rb +461 -0
- data/spec/contracts_spec.rb +748 -0
- data/spec/error_formatter_spec.rb +68 -0
- data/spec/fixtures/fixtures.rb +710 -0
- data/spec/invariants_spec.rb +17 -0
- data/spec/module_spec.rb +18 -0
- data/spec/override_validators_spec.rb +162 -0
- data/spec/ruby_version_specific/contracts_spec_1.9.rb +24 -0
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +55 -0
- data/spec/ruby_version_specific/contracts_spec_2.1.rb +63 -0
- data/spec/spec_helper.rb +102 -0
- data/spec/support.rb +10 -0
- data/spec/support_spec.rb +21 -0
- data/spec/validators_spec.rb +47 -0
- metadata +94 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
RSpec.describe "Contracts::ErrorFormatters" do
|
2
|
+
before :all do
|
3
|
+
@o = GenericExample.new
|
4
|
+
end
|
5
|
+
C = Contracts::Builtin
|
6
|
+
|
7
|
+
describe "self.class_for" do
|
8
|
+
let(:keywordargs_contract) { C::KeywordArgs[:name => String, :age => Fixnum] }
|
9
|
+
let(:other_contract) { [C::Num, C::Num, C::Num] }
|
10
|
+
|
11
|
+
it "returns KeywordArgsErrorFormatter for KeywordArgs contract" do
|
12
|
+
data_keywordargs = {:contract => keywordargs_contract, :arg => {:b => 2}}
|
13
|
+
expect(Contracts::ErrorFormatters.class_for(data_keywordargs)).to eq(Contracts::KeywordArgsErrorFormatter)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns Contracts::DefaultErrorFormatter for other contracts" do
|
17
|
+
data_default = {:contract => other_contract, :arg => {:b => 2}}
|
18
|
+
expect(Contracts::ErrorFormatters.class_for(data_default)).to eq(Contracts::DefaultErrorFormatter)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def format_message(str)
|
23
|
+
str.split("\n").map(&:strip).join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def fails(msg, &block)
|
27
|
+
expect { block.call }.to raise_error do |e|
|
28
|
+
expect(e).to be_a(ParamContractError)
|
29
|
+
expect(format_message(e.message)).to include(format_message(msg))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if ruby_version > 1.8
|
34
|
+
describe "self.failure_msg" do
|
35
|
+
it "includes normal information" do
|
36
|
+
msg = %{Contract violation for argument 1 of 1:
|
37
|
+
Expected: (KeywordArgs[{:name=>String, :age=>Fixnum}])
|
38
|
+
Actual: {:age=>"2", :invalid_third=>1}
|
39
|
+
Missing Contract: {:invalid_third=>1}
|
40
|
+
Invalid Args: [{:age=>"2", :contract=>Fixnum}]
|
41
|
+
Missing Args: {:name=>String}
|
42
|
+
Value guarded in: GenericExample::simple_keywordargs
|
43
|
+
With Contract: KeywordArgs => NilClass}
|
44
|
+
fails msg do
|
45
|
+
@o.simple_keywordargs(:age => "2", :invalid_third => 1)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "includes Missing Contract information" do
|
50
|
+
fails %{Missing Contract: {:invalid_third=>1, :invalid_fourth=>1}} do
|
51
|
+
@o.simple_keywordargs(:age => "2", :invalid_third => 1, :invalid_fourth => 1)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it "includes Invalid Args information" do
|
56
|
+
fails %{Invalid Args: [{:age=>"2", :contract=>Fixnum}]} do
|
57
|
+
@o.simple_keywordargs(:age => "2", :invalid_third => 1)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it "includes Missing Args information" do
|
62
|
+
fails %{Missing Args: {:name=>String}} do
|
63
|
+
@o.simple_keywordargs(:age => "2", :invalid_third => 1)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,710 @@
|
|
1
|
+
require "date"
|
2
|
+
|
3
|
+
C = Contracts
|
4
|
+
|
5
|
+
class A
|
6
|
+
include Contracts::Core
|
7
|
+
|
8
|
+
Contract C::Num => C::Num
|
9
|
+
def self.a_class_method x
|
10
|
+
x + 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def good
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
Contract C::Num => C::Num
|
18
|
+
def triple x
|
19
|
+
x * 3
|
20
|
+
end
|
21
|
+
|
22
|
+
Contract C::Num => C::Num
|
23
|
+
def instance_and_class_method x
|
24
|
+
x * 2
|
25
|
+
end
|
26
|
+
|
27
|
+
Contract String => String
|
28
|
+
def self.instance_and_class_method x
|
29
|
+
x * 2
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class B
|
34
|
+
include Contracts::Core
|
35
|
+
|
36
|
+
def bad
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
Contract String => String
|
41
|
+
def triple x
|
42
|
+
x * 3
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class F
|
47
|
+
include Contracts::Core
|
48
|
+
|
49
|
+
def good
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
def bad
|
54
|
+
true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class EmptyCont
|
59
|
+
def self.to_s
|
60
|
+
""
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class GenericExample
|
65
|
+
include Contracts::Core
|
66
|
+
|
67
|
+
Contract C::Num => C::Num
|
68
|
+
def self.a_class_method x
|
69
|
+
x + 1
|
70
|
+
end
|
71
|
+
|
72
|
+
Contract C::Num => nil
|
73
|
+
def bad_double(x)
|
74
|
+
x * 2
|
75
|
+
end
|
76
|
+
|
77
|
+
Contract C::Num => C::Num
|
78
|
+
def double(x)
|
79
|
+
x * 2
|
80
|
+
end
|
81
|
+
|
82
|
+
Contract 123, nil => nil
|
83
|
+
def constanty(num, nul)
|
84
|
+
0
|
85
|
+
end
|
86
|
+
|
87
|
+
Contract String => nil
|
88
|
+
def hello(name)
|
89
|
+
end
|
90
|
+
|
91
|
+
Contract lambda { |x| x.is_a? Numeric } => C::Num
|
92
|
+
def square(x)
|
93
|
+
x ** 2
|
94
|
+
end
|
95
|
+
|
96
|
+
Contract [C::Num, C::Num, C::Num] => C::Num
|
97
|
+
def sum_three(vals)
|
98
|
+
vals.inject(0) do |acc, x|
|
99
|
+
acc + x
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
Contract ({ :name => String, :age => Fixnum }) => nil
|
104
|
+
def person(data)
|
105
|
+
end
|
106
|
+
|
107
|
+
Contract C::StrictHash[{ :name => String, :age => Fixnum }] => nil
|
108
|
+
def strict_person(data)
|
109
|
+
end
|
110
|
+
|
111
|
+
Contract ({ :rigged => C::Or[TrueClass, FalseClass] }) => nil
|
112
|
+
def hash_complex_contracts(data)
|
113
|
+
end
|
114
|
+
|
115
|
+
Contract ({ :rigged => C::Bool,
|
116
|
+
:contents => { :kind => C::Or[String, Symbol],
|
117
|
+
:total => C::Num }
|
118
|
+
}) => nil
|
119
|
+
def nested_hash_complex_contracts(data)
|
120
|
+
end
|
121
|
+
|
122
|
+
Contract C::KeywordArgs[:name => String, :age => Fixnum] => nil
|
123
|
+
def person_keywordargs(data)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Testing overloaded method
|
127
|
+
Contract String, Fixnum => nil
|
128
|
+
def person_keywordargs(name, age)
|
129
|
+
end
|
130
|
+
|
131
|
+
Contract C::KeywordArgs[:hash => C::HashOf[Symbol, C::Num]] => nil
|
132
|
+
def hash_keywordargs(data)
|
133
|
+
end
|
134
|
+
|
135
|
+
Contract C::KeywordArgs[:name => String, :age => Fixnum] => nil
|
136
|
+
def simple_keywordargs(data)
|
137
|
+
end
|
138
|
+
|
139
|
+
Contract (/foo/) => nil
|
140
|
+
def should_contain_foo(s)
|
141
|
+
end
|
142
|
+
|
143
|
+
Contract ({ :host => /foo/ }) => nil
|
144
|
+
def hash_containing_foo(s)
|
145
|
+
end
|
146
|
+
|
147
|
+
Contract C::ArrayOf[/foo/] => nil
|
148
|
+
def array_containing_foo(s)
|
149
|
+
end
|
150
|
+
|
151
|
+
Contract [C::Or[TrueClass, FalseClass]] => nil
|
152
|
+
def array_complex_contracts(data)
|
153
|
+
end
|
154
|
+
|
155
|
+
Contract [C::Bool, [C::Or[String, Symbol]]] => nil
|
156
|
+
def nested_array_complex_contracts(data)
|
157
|
+
end
|
158
|
+
|
159
|
+
Contract Proc => C::Any
|
160
|
+
def do_call(&block)
|
161
|
+
block.call
|
162
|
+
end
|
163
|
+
|
164
|
+
Contract C::Args[C::Num], C::Maybe[Proc] => C::Any
|
165
|
+
def maybe_call(*vals, &block)
|
166
|
+
block.call if block
|
167
|
+
vals
|
168
|
+
end
|
169
|
+
|
170
|
+
Contract C::Args[C::Num] => C::Num
|
171
|
+
def sum(*vals)
|
172
|
+
vals.inject(0) do |acc, val|
|
173
|
+
acc + val
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
Contract C::Args[C::Num], Proc => C::Num
|
178
|
+
def with_partial_sums(*vals, &blk)
|
179
|
+
sum = vals.inject(0) do |acc, val|
|
180
|
+
blk[acc]
|
181
|
+
acc + val
|
182
|
+
end
|
183
|
+
blk[sum]
|
184
|
+
end
|
185
|
+
|
186
|
+
Contract C::Args[C::Num], C::Func[C::Num => C::Num] => C::Num
|
187
|
+
def with_partial_sums_contracted(*vals, &blk)
|
188
|
+
sum = vals.inject(0) do |acc, val|
|
189
|
+
blk[acc]
|
190
|
+
acc + val
|
191
|
+
end
|
192
|
+
blk[sum]
|
193
|
+
end
|
194
|
+
|
195
|
+
# Important to use different arg types or it falsely passes
|
196
|
+
Contract C::Num, C::Args[String] => C::ArrayOf[String]
|
197
|
+
def arg_then_splat(n, *vals)
|
198
|
+
vals.map { |v| v * n }
|
199
|
+
end
|
200
|
+
|
201
|
+
Contract C::Num, Proc => nil
|
202
|
+
def double_with_proc(x, &blk)
|
203
|
+
blk.call(x * 2)
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
|
207
|
+
Contract C::Pos => nil
|
208
|
+
def pos_test(x)
|
209
|
+
end
|
210
|
+
|
211
|
+
Contract C::Neg => nil
|
212
|
+
def neg_test(x)
|
213
|
+
end
|
214
|
+
|
215
|
+
Contract C::Nat => nil
|
216
|
+
def nat_test(x)
|
217
|
+
end
|
218
|
+
|
219
|
+
Contract C::Any => nil
|
220
|
+
def show(x)
|
221
|
+
end
|
222
|
+
|
223
|
+
Contract C::None => nil
|
224
|
+
def fail_all(x)
|
225
|
+
end
|
226
|
+
|
227
|
+
Contract C::Or[C::Num, String] => nil
|
228
|
+
def num_or_string(x)
|
229
|
+
end
|
230
|
+
|
231
|
+
Contract C::Xor[C::RespondTo[:good], C::RespondTo[:bad]] => nil
|
232
|
+
def xor_test(x)
|
233
|
+
end
|
234
|
+
|
235
|
+
Contract C::And[A, C::RespondTo[:good]] => nil
|
236
|
+
def and_test(x)
|
237
|
+
end
|
238
|
+
|
239
|
+
Contract C::Enum[:a, :b, :c] => nil
|
240
|
+
def enum_test(x)
|
241
|
+
end
|
242
|
+
|
243
|
+
Contract C::RespondTo[:good] => nil
|
244
|
+
def responds_test(x)
|
245
|
+
end
|
246
|
+
|
247
|
+
Contract C::Send[:good] => nil
|
248
|
+
def send_test(x)
|
249
|
+
end
|
250
|
+
|
251
|
+
Contract C::Not[nil] => nil
|
252
|
+
def not_nil(x)
|
253
|
+
end
|
254
|
+
|
255
|
+
Contract C::ArrayOf[C::Num] => C::Num
|
256
|
+
def product(vals)
|
257
|
+
vals.inject(1) do |acc, x|
|
258
|
+
acc * x
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
Contract C::SetOf[C::Num] => C::Num
|
263
|
+
def product_from_set(vals)
|
264
|
+
vals.inject(1) do |acc, x|
|
265
|
+
acc * x
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
Contract C::RangeOf[C::Num] => C::Num
|
270
|
+
def first_in_range_num(r)
|
271
|
+
r.first
|
272
|
+
end
|
273
|
+
|
274
|
+
Contract C::RangeOf[Date] => Date
|
275
|
+
def first_in_range_date(r)
|
276
|
+
r.first
|
277
|
+
end
|
278
|
+
|
279
|
+
Contract C::DescendantOf[Enumerable] => nil
|
280
|
+
def enumerable_descendant_test(enum)
|
281
|
+
end
|
282
|
+
|
283
|
+
Contract C::Bool => nil
|
284
|
+
def bool_test(x)
|
285
|
+
end
|
286
|
+
|
287
|
+
Contract C::Num
|
288
|
+
def no_args
|
289
|
+
1
|
290
|
+
end
|
291
|
+
|
292
|
+
# This function has a contract which says it has no args,
|
293
|
+
# but the function does have args.
|
294
|
+
Contract nil => C::Num
|
295
|
+
def old_style_no_args
|
296
|
+
2
|
297
|
+
end
|
298
|
+
|
299
|
+
Contract C::ArrayOf[C::Num], C::Func[C::Num => C::Num] => C::ArrayOf[C::Num]
|
300
|
+
def map(arr, func)
|
301
|
+
ret = []
|
302
|
+
arr.each do |x|
|
303
|
+
ret << func[x]
|
304
|
+
end
|
305
|
+
ret
|
306
|
+
end
|
307
|
+
|
308
|
+
Contract C::ArrayOf[C::Any], Proc => C::ArrayOf[C::Any]
|
309
|
+
def tutorial_map(arr, func)
|
310
|
+
ret = []
|
311
|
+
arr.each do |x|
|
312
|
+
ret << func[x]
|
313
|
+
end
|
314
|
+
ret
|
315
|
+
end
|
316
|
+
|
317
|
+
# Need to test Func with weak contracts for other args
|
318
|
+
# and changing type from input to output otherwise it falsely passes!
|
319
|
+
Contract Array, C::Func[String => C::Num] => Array
|
320
|
+
def map_plain(arr, func)
|
321
|
+
arr.map do |x|
|
322
|
+
func[x]
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
Contract C::None => C::Func[String => C::Num]
|
327
|
+
def lambda_with_wrong_return
|
328
|
+
lambda { |x| x }
|
329
|
+
end
|
330
|
+
|
331
|
+
Contract C::None => C::Func[String => C::Num]
|
332
|
+
def lambda_with_correct_return
|
333
|
+
lambda { |x| x.length }
|
334
|
+
end
|
335
|
+
|
336
|
+
Contract C::Num => C::Num
|
337
|
+
def default_args(x = 1)
|
338
|
+
2
|
339
|
+
end
|
340
|
+
|
341
|
+
Contract C::Maybe[C::Num] => C::Maybe[C::Num]
|
342
|
+
def maybe_double x
|
343
|
+
if x.nil?
|
344
|
+
nil
|
345
|
+
else
|
346
|
+
x * 2
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
Contract C::HashOf[Symbol, C::Num] => C::Num
|
351
|
+
def gives_max_value(hash)
|
352
|
+
hash.values.max
|
353
|
+
end
|
354
|
+
|
355
|
+
Contract C::HashOf[Symbol => C::Num] => C::Num
|
356
|
+
def pretty_gives_max_value(hash)
|
357
|
+
hash.values.max
|
358
|
+
end
|
359
|
+
|
360
|
+
Contract EmptyCont => C::Any
|
361
|
+
def using_empty_contract(a)
|
362
|
+
a
|
363
|
+
end
|
364
|
+
|
365
|
+
Contract (1..10) => nil
|
366
|
+
def method_with_range_contract(x)
|
367
|
+
end
|
368
|
+
|
369
|
+
Contract String
|
370
|
+
def a_private_method
|
371
|
+
"works"
|
372
|
+
end
|
373
|
+
private :a_private_method
|
374
|
+
|
375
|
+
Contract String
|
376
|
+
def a_protected_method
|
377
|
+
"works"
|
378
|
+
end
|
379
|
+
protected :a_protected_method
|
380
|
+
|
381
|
+
private
|
382
|
+
|
383
|
+
Contract String
|
384
|
+
def a_really_private_method
|
385
|
+
"works for sure"
|
386
|
+
end
|
387
|
+
|
388
|
+
protected
|
389
|
+
|
390
|
+
Contract String
|
391
|
+
def a_really_protected_method
|
392
|
+
"works for sure"
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# for testing inheritance
|
397
|
+
class Parent
|
398
|
+
include Contracts::Core
|
399
|
+
|
400
|
+
Contract C::Num => C::Num
|
401
|
+
def double x
|
402
|
+
x * 2
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
class Child < Parent
|
407
|
+
end
|
408
|
+
|
409
|
+
class GenericExample
|
410
|
+
Contract Parent => Parent
|
411
|
+
def id_ a
|
412
|
+
a
|
413
|
+
end
|
414
|
+
|
415
|
+
Contract C::Exactly[Parent] => nil
|
416
|
+
def exactly_test(x)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
# for testing equality
|
421
|
+
class Foo
|
422
|
+
end
|
423
|
+
module Bar
|
424
|
+
end
|
425
|
+
Baz = 1
|
426
|
+
|
427
|
+
class GenericExample
|
428
|
+
Contract C::Eq[Foo] => C::Any
|
429
|
+
def eq_class_test(x)
|
430
|
+
end
|
431
|
+
|
432
|
+
Contract C::Eq[Bar] => C::Any
|
433
|
+
def eq_module_test(x)
|
434
|
+
end
|
435
|
+
|
436
|
+
Contract C::Eq[Baz] => C::Any
|
437
|
+
def eq_value_test(x)
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
# pattern matching example with possible deep contract violation
|
442
|
+
class PatternMatchingExample
|
443
|
+
include Contracts::Core
|
444
|
+
|
445
|
+
class Success
|
446
|
+
attr_accessor :request
|
447
|
+
def initialize request
|
448
|
+
@request = request
|
449
|
+
end
|
450
|
+
|
451
|
+
def ==(other)
|
452
|
+
request == other.request
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
class Failure
|
457
|
+
end
|
458
|
+
|
459
|
+
Response = C::Or[Success, Failure]
|
460
|
+
|
461
|
+
class StringWithHello
|
462
|
+
def self.valid?(string)
|
463
|
+
string.is_a?(String) && !!string.match(/hello/i)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
Contract Success => Response
|
468
|
+
def process_request(status)
|
469
|
+
Success.new(decorated_request(status.request))
|
470
|
+
end
|
471
|
+
|
472
|
+
Contract Failure => Response
|
473
|
+
def process_request(status)
|
474
|
+
Failure.new
|
475
|
+
end
|
476
|
+
|
477
|
+
Contract StringWithHello => String
|
478
|
+
def decorated_request(request)
|
479
|
+
request + "!"
|
480
|
+
end
|
481
|
+
|
482
|
+
Contract C::Num, String => String
|
483
|
+
def do_stuff(number, string)
|
484
|
+
"foo"
|
485
|
+
end
|
486
|
+
|
487
|
+
Contract C::Num, String, C::Num => String
|
488
|
+
def do_stuff(number, string, other_number)
|
489
|
+
"bar"
|
490
|
+
end
|
491
|
+
|
492
|
+
Contract C::Num => C::Num
|
493
|
+
def double x
|
494
|
+
"bad"
|
495
|
+
end
|
496
|
+
|
497
|
+
Contract String => String
|
498
|
+
def double x
|
499
|
+
x * 2
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
# invariant example (silliest implementation ever)
|
504
|
+
class MyBirthday
|
505
|
+
include Contracts::Core
|
506
|
+
include Contracts::Invariants
|
507
|
+
|
508
|
+
invariant(:day) { 1 <= day && day <= 31 }
|
509
|
+
invariant(:month) { 1 <= month && month <= 12 }
|
510
|
+
|
511
|
+
attr_accessor :day, :month
|
512
|
+
def initialize(day, month)
|
513
|
+
@day = day
|
514
|
+
@month = month
|
515
|
+
end
|
516
|
+
|
517
|
+
Contract C::None => Fixnum
|
518
|
+
def silly_next_day!
|
519
|
+
self.day += 1
|
520
|
+
end
|
521
|
+
|
522
|
+
Contract C::None => Fixnum
|
523
|
+
def silly_next_month!
|
524
|
+
self.month += 1
|
525
|
+
end
|
526
|
+
|
527
|
+
Contract C::None => Fixnum
|
528
|
+
def clever_next_day!
|
529
|
+
return clever_next_month! if day == 31
|
530
|
+
self.day += 1
|
531
|
+
end
|
532
|
+
|
533
|
+
Contract C::None => Fixnum
|
534
|
+
def clever_next_month!
|
535
|
+
return next_year! if month == 12
|
536
|
+
self.month += 1
|
537
|
+
self.day = 1
|
538
|
+
end
|
539
|
+
|
540
|
+
Contract C::None => Fixnum
|
541
|
+
def next_year!
|
542
|
+
self.month = 1
|
543
|
+
self.day = 1
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
class SingletonClassExample
|
548
|
+
# This turned out to be required line here to make singleton classes
|
549
|
+
# work properly under all platforms. Not sure if it worth trying to
|
550
|
+
# do something with it.
|
551
|
+
include Contracts::Core
|
552
|
+
|
553
|
+
class << self
|
554
|
+
Contract String => String
|
555
|
+
def hoge(str)
|
556
|
+
"super#{str}"
|
557
|
+
end
|
558
|
+
|
559
|
+
Contract C::Num, C::Num => C::Num
|
560
|
+
def add(a, b)
|
561
|
+
a + b
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
with_enabled_no_contracts do
|
567
|
+
class NoContractsSimpleExample
|
568
|
+
include Contracts::Core
|
569
|
+
|
570
|
+
Contract String => nil
|
571
|
+
def some_method(x)
|
572
|
+
nil
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
class NoContractsInvariantsExample
|
577
|
+
include Contracts::Core
|
578
|
+
include Contracts::Invariants
|
579
|
+
|
580
|
+
attr_accessor :day
|
581
|
+
|
582
|
+
invariant(:day_rule) { 1 <= day && day <= 7 }
|
583
|
+
|
584
|
+
Contract C::None => nil
|
585
|
+
def next_day
|
586
|
+
self.day += 1
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
class NoContractsPatternMatchingExample
|
591
|
+
include Contracts::Core
|
592
|
+
|
593
|
+
Contract 200, String => String
|
594
|
+
def on_response(status, body)
|
595
|
+
body + "!"
|
596
|
+
end
|
597
|
+
|
598
|
+
Contract Fixnum, String => String
|
599
|
+
def on_response(status, body)
|
600
|
+
"error #{status}: #{body}"
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
module ModuleExample
|
606
|
+
include Contracts::Core
|
607
|
+
|
608
|
+
Contract C::Num, C::Num => C::Num
|
609
|
+
def plus(a, b)
|
610
|
+
a + b
|
611
|
+
end
|
612
|
+
|
613
|
+
Contract String => String
|
614
|
+
def self.hoge(str)
|
615
|
+
"super#{str}"
|
616
|
+
end
|
617
|
+
|
618
|
+
class << self
|
619
|
+
Contract String => nil
|
620
|
+
def eat(food)
|
621
|
+
# yummy
|
622
|
+
nil
|
623
|
+
end
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
class KlassWithModuleExample
|
628
|
+
include ModuleExample
|
629
|
+
end
|
630
|
+
|
631
|
+
class SingletonInheritanceExample
|
632
|
+
include Contracts::Core
|
633
|
+
|
634
|
+
Contract C::Any => C::Any
|
635
|
+
def self.a_contracted_self
|
636
|
+
self
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
class SingletonInheritanceExampleSubclass < SingletonInheritanceExample
|
641
|
+
class << self
|
642
|
+
Contract Integer => Integer
|
643
|
+
def num(int)
|
644
|
+
int
|
645
|
+
end
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
class BareOptionalContractUsed
|
650
|
+
include Contracts::Core
|
651
|
+
|
652
|
+
Contract C::Num, C::Optional[C::Num] => nil
|
653
|
+
def something(a, b)
|
654
|
+
nil
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
module ModuleContractExample
|
659
|
+
include Contracts::Core
|
660
|
+
|
661
|
+
module AModule
|
662
|
+
end
|
663
|
+
|
664
|
+
module AnotherModule
|
665
|
+
end
|
666
|
+
|
667
|
+
module InheritedModule
|
668
|
+
include AModule
|
669
|
+
end
|
670
|
+
|
671
|
+
class AClassWithModule
|
672
|
+
include AModule
|
673
|
+
end
|
674
|
+
|
675
|
+
class AClassWithoutModule
|
676
|
+
end
|
677
|
+
|
678
|
+
class AClassWithAnotherModule
|
679
|
+
include AnotherModule
|
680
|
+
end
|
681
|
+
|
682
|
+
class AClassWithInheritedModule
|
683
|
+
include InheritedModule
|
684
|
+
end
|
685
|
+
|
686
|
+
class AClassWithBothModules
|
687
|
+
include AModule
|
688
|
+
include AnotherModule
|
689
|
+
end
|
690
|
+
|
691
|
+
Contract AModule => Symbol
|
692
|
+
def self.hello(thing)
|
693
|
+
:world
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
module ModuleWithContracts
|
698
|
+
def self.included(base)
|
699
|
+
base.extend ClassMethods
|
700
|
+
end
|
701
|
+
|
702
|
+
module ClassMethods
|
703
|
+
include Contracts::Core
|
704
|
+
|
705
|
+
Contract C::None => String
|
706
|
+
def foo
|
707
|
+
"bar"
|
708
|
+
end
|
709
|
+
end
|
710
|
+
end
|