contracts 0.9 → 0.10
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 +4 -4
- data/CHANGELOG.markdown +5 -2
- data/TUTORIAL.md +136 -21
- data/benchmarks/hash.rb +69 -0
- data/lib/contracts.rb +20 -156
- data/lib/contracts/builtin_contracts.rb +100 -3
- data/lib/contracts/call_with.rb +96 -0
- data/lib/contracts/decorators.rb +4 -194
- data/lib/contracts/engine.rb +26 -0
- data/lib/contracts/engine/base.rb +136 -0
- data/lib/contracts/engine/eigenclass.rb +46 -0
- data/lib/contracts/engine/target.rb +68 -0
- data/lib/contracts/method_handler.rb +195 -0
- data/lib/contracts/support.rb +45 -30
- data/lib/contracts/validators.rb +127 -0
- data/lib/contracts/version.rb +1 -1
- data/spec/builtin_contracts_spec.rb +78 -2
- data/spec/contracts_spec.rb +64 -1
- data/spec/fixtures/fixtures.rb +77 -5
- data/spec/override_validators_spec.rb +162 -0
- data/spec/ruby_version_specific/contracts_spec_2.1.rb +10 -2
- metadata +12 -6
- data/lib/contracts/eigenclass.rb +0 -38
- data/lib/contracts/modules.rb +0 -17
data/lib/contracts/support.rb
CHANGED
@@ -1,44 +1,59 @@
|
|
1
1
|
module Contracts
|
2
2
|
module Support
|
3
|
-
|
4
|
-
|
3
|
+
class << self
|
4
|
+
def method_position(method)
|
5
|
+
return method.method_position if method.is_a?(MethodReference)
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
if RUBY_VERSION =~ /^1\.8/
|
8
|
+
if method.respond_to?(:__file__)
|
9
|
+
method.__file__ + ":" + method.__line__.to_s
|
10
|
+
else
|
11
|
+
method.inspect
|
12
|
+
end
|
9
13
|
else
|
10
|
-
method.
|
14
|
+
file, line = method.source_location
|
15
|
+
file + ":" + line.to_s
|
11
16
|
end
|
12
|
-
else
|
13
|
-
file, line = method.source_location
|
14
|
-
file + ":" + line.to_s
|
15
17
|
end
|
16
|
-
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
def method_name(method)
|
20
|
+
method.is_a?(Proc) ? "Proc" : method.name
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
# Generates unique id, which can be used as a part of identifier
|
24
|
+
#
|
25
|
+
# Example:
|
26
|
+
# Contracts::Support.unique_id # => "i53u6tiw5hbo"
|
27
|
+
def unique_id
|
28
|
+
# Consider using SecureRandom.hex here, and benchmark which one is better
|
29
|
+
(Time.now.to_f * 1000).to_i.to_s(36) + rand(1_000_000).to_s(36)
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
32
|
+
def contract_id(contract)
|
33
|
+
contract.object_id
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
def eigenclass_hierarchy_supported?
|
37
|
+
return false if RUBY_PLATFORM == "java" && RUBY_VERSION.to_f < 2.0
|
38
|
+
RUBY_VERSION.to_f > 1.8
|
39
|
+
end
|
40
|
+
|
41
|
+
def eigenclass_of(target)
|
42
|
+
class << target; self; end
|
43
|
+
end
|
39
44
|
|
40
|
-
|
41
|
-
|
45
|
+
def eigenclass?(target)
|
46
|
+
module_eigenclass?(target) ||
|
47
|
+
target <= eigenclass_of(Object)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Module eigenclass can be detected by its ancestor chain
|
53
|
+
# containing a Module
|
54
|
+
def module_eigenclass?(target)
|
55
|
+
target < Module
|
56
|
+
end
|
42
57
|
end
|
43
58
|
end
|
44
59
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Contracts
|
2
|
+
module Validators
|
3
|
+
DEFAULT_VALIDATOR_STRATEGIES = {
|
4
|
+
# e.g. lambda {true}
|
5
|
+
Proc => lambda { |contract| contract },
|
6
|
+
|
7
|
+
# e.g. [Num, String]
|
8
|
+
# TODO: account for these errors too
|
9
|
+
Array => lambda do |contract|
|
10
|
+
lambda do |arg|
|
11
|
+
return false unless arg.is_a?(Array) && arg.length == contract.length
|
12
|
+
arg.zip(contract).all? do |_arg, _contract|
|
13
|
+
Contract.valid?(_arg, _contract)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end,
|
17
|
+
|
18
|
+
# e.g. { :a => Num, :b => String }
|
19
|
+
Hash => lambda do |contract|
|
20
|
+
lambda do |arg|
|
21
|
+
return false unless arg.is_a?(Hash)
|
22
|
+
contract.keys.all? do |k|
|
23
|
+
Contract.valid?(arg[k], contract[k])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end,
|
27
|
+
|
28
|
+
Contracts::Args => lambda do |contract|
|
29
|
+
lambda do |arg|
|
30
|
+
Contract.valid?(arg, contract.contract)
|
31
|
+
end
|
32
|
+
end,
|
33
|
+
|
34
|
+
Contracts::Func => lambda do |_|
|
35
|
+
lambda do |arg|
|
36
|
+
arg.is_a?(Method) || arg.is_a?(Proc)
|
37
|
+
end
|
38
|
+
end,
|
39
|
+
|
40
|
+
:valid => lambda do |contract|
|
41
|
+
lambda { |arg| contract.valid?(arg) }
|
42
|
+
end,
|
43
|
+
|
44
|
+
:class => lambda do |contract|
|
45
|
+
lambda { |arg| arg.is_a?(contract) }
|
46
|
+
end,
|
47
|
+
|
48
|
+
:default => lambda do |contract|
|
49
|
+
lambda { |arg| contract == arg }
|
50
|
+
end
|
51
|
+
}.freeze
|
52
|
+
|
53
|
+
# Allows to override validator with custom one.
|
54
|
+
# Example:
|
55
|
+
# Contract.override_validator(Array) do |contract|
|
56
|
+
# lambda do |arg|
|
57
|
+
# # .. implementation for Array contract ..
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# Contract.override_validator(:class) do |contract|
|
62
|
+
# lambda do |arg|
|
63
|
+
# arg.is_a?(contract) || arg.is_a?(RSpec::Mocks::Double)
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
def override_validator(name, &block)
|
67
|
+
validator_strategies[name] = block
|
68
|
+
end
|
69
|
+
|
70
|
+
# This is a little weird. For each contract
|
71
|
+
# we pre-make a proc to validate it so we
|
72
|
+
# don't have to go through this decision tree every time.
|
73
|
+
# Seems silly but it saves us a bunch of time (4.3sec vs 5.2sec)
|
74
|
+
def make_validator!(contract)
|
75
|
+
klass = contract.class
|
76
|
+
key = if validator_strategies.key?(klass)
|
77
|
+
klass
|
78
|
+
else
|
79
|
+
if contract.respond_to? :valid?
|
80
|
+
:valid
|
81
|
+
elsif klass == Class || klass == Module
|
82
|
+
:class
|
83
|
+
else
|
84
|
+
:default
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
validator_strategies[key].call(contract)
|
89
|
+
end
|
90
|
+
|
91
|
+
def make_validator(contract)
|
92
|
+
contract_id = Support.contract_id(contract)
|
93
|
+
|
94
|
+
if memoized_validators.key?(contract_id)
|
95
|
+
return memoized_validators[contract_id]
|
96
|
+
end
|
97
|
+
|
98
|
+
memoized_validators[contract_id] = make_validator!(contract)
|
99
|
+
end
|
100
|
+
|
101
|
+
# @private
|
102
|
+
def reset_validators
|
103
|
+
clean_memoized_validators
|
104
|
+
restore_validators
|
105
|
+
end
|
106
|
+
|
107
|
+
# @private
|
108
|
+
def validator_strategies
|
109
|
+
@_validator_strategies ||= restore_validators
|
110
|
+
end
|
111
|
+
|
112
|
+
# @private
|
113
|
+
def restore_validators
|
114
|
+
@_validator_strategies = DEFAULT_VALIDATOR_STRATEGIES.dup
|
115
|
+
end
|
116
|
+
|
117
|
+
# @private
|
118
|
+
def memoized_validators
|
119
|
+
@_memoized_validators ||= clean_memoized_validators
|
120
|
+
end
|
121
|
+
|
122
|
+
# @private
|
123
|
+
def clean_memoized_validators
|
124
|
+
@_memoized_validators = {}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/contracts/version.rb
CHANGED
@@ -12,14 +12,18 @@ RSpec.describe "Contracts:" do
|
|
12
12
|
expect { @o.double(2.2) }.to_not raise_error
|
13
13
|
end
|
14
14
|
|
15
|
-
it "should fail for
|
16
|
-
expect { @o.double(
|
15
|
+
it "should fail for nil and other data types" do
|
16
|
+
expect { @o.double(nil) }.to raise_error(ContractError)
|
17
|
+
expect { @o.double(:x) }.to raise_error(ContractError)
|
18
|
+
expect { @o.double("x") }.to raise_error(ContractError)
|
19
|
+
expect { @o.double(/x/) }.to raise_error(ContractError)
|
17
20
|
end
|
18
21
|
end
|
19
22
|
|
20
23
|
describe "Pos:" do
|
21
24
|
it "should pass for positive numbers" do
|
22
25
|
expect { @o.pos_test(1) }.to_not raise_error
|
26
|
+
expect { @o.pos_test(1.6) }.to_not raise_error
|
23
27
|
end
|
24
28
|
|
25
29
|
it "should fail for 0" do
|
@@ -28,12 +32,21 @@ RSpec.describe "Contracts:" do
|
|
28
32
|
|
29
33
|
it "should fail for negative numbers" do
|
30
34
|
expect { @o.pos_test(-1) }.to raise_error(ContractError)
|
35
|
+
expect { @o.pos_test(-1.6) }.to raise_error(ContractError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should fail for nil and other data types" do
|
39
|
+
expect { @o.pos_test(nil) }.to raise_error(ContractError)
|
40
|
+
expect { @o.pos_test(:x) }.to raise_error(ContractError)
|
41
|
+
expect { @o.pos_test("x") }.to raise_error(ContractError)
|
42
|
+
expect { @o.pos_test(/x/) }.to raise_error(ContractError)
|
31
43
|
end
|
32
44
|
end
|
33
45
|
|
34
46
|
describe "Neg:" do
|
35
47
|
it "should pass for negative numbers" do
|
36
48
|
expect { @o.neg_test(-1) }.to_not raise_error
|
49
|
+
expect { @o.neg_test(-1.6) }.to_not raise_error
|
37
50
|
end
|
38
51
|
|
39
52
|
it "should fail for 0" do
|
@@ -42,6 +55,14 @@ RSpec.describe "Contracts:" do
|
|
42
55
|
|
43
56
|
it "should fail for positive numbers" do
|
44
57
|
expect { @o.neg_test(1) }.to raise_error(ContractError)
|
58
|
+
expect { @o.neg_test(1.6) }.to raise_error(ContractError)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should fail for nil and other data types" do
|
62
|
+
expect { @o.neg_test(nil) }.to raise_error(ContractError)
|
63
|
+
expect { @o.neg_test(:x) }.to raise_error(ContractError)
|
64
|
+
expect { @o.neg_test("x") }.to raise_error(ContractError)
|
65
|
+
expect { @o.neg_test(/x/) }.to raise_error(ContractError)
|
45
66
|
end
|
46
67
|
end
|
47
68
|
|
@@ -60,6 +81,14 @@ RSpec.describe "Contracts:" do
|
|
60
81
|
|
61
82
|
it "should fail for negative numbers" do
|
62
83
|
expect { @o.nat_test(-1) }.to raise_error(ContractError)
|
84
|
+
expect { @o.nat_test(-1.6) }.to raise_error(ContractError)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should fail for nil and other data types" do
|
88
|
+
expect { @o.nat_test(nil) }.to raise_error(ContractError)
|
89
|
+
expect { @o.nat_test(:x) }.to raise_error(ContractError)
|
90
|
+
expect { @o.nat_test("x") }.to raise_error(ContractError)
|
91
|
+
expect { @o.nat_test(/x/) }.to raise_error(ContractError)
|
63
92
|
end
|
64
93
|
end
|
65
94
|
|
@@ -215,6 +244,45 @@ RSpec.describe "Contracts:" do
|
|
215
244
|
end
|
216
245
|
end
|
217
246
|
|
247
|
+
describe "RangeOf:" do
|
248
|
+
require "date"
|
249
|
+
it "should pass for a range of nums" do
|
250
|
+
expect { @o.first_in_range_num(3..10) }.to_not raise_error
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should pass for a range of dates" do
|
254
|
+
d1 = Date.today
|
255
|
+
d2 = d1 + 18
|
256
|
+
expect { @o.first_in_range_date(d1..d2) }.to_not raise_error
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should fail for a non-range" do
|
260
|
+
expect { @o.first_in_range_num("foo") }.to raise_error(ContractError)
|
261
|
+
expect { @o.first_in_range_num(:foo) }.to raise_error(ContractError)
|
262
|
+
expect { @o.first_in_range_num(5) }.to raise_error(ContractError)
|
263
|
+
expect { @o.first_in_range_num(nil) }.to raise_error(ContractError)
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should fail for a range with incorrect data type" do
|
267
|
+
expect { @o.first_in_range_num("a".."z") }.to raise_error(ContractError)
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should fail for a badly-defined range" do
|
271
|
+
# For some reason, Ruby 2.0.0 allows (date .. number) as a range.
|
272
|
+
# Perhaps other Ruby versions do too.
|
273
|
+
# Note that (date .. string) gives ArgumentError.
|
274
|
+
# This test guards against ranges with inconsistent data types.
|
275
|
+
begin
|
276
|
+
d1 = Date.today
|
277
|
+
expect { @o.first_in_range_date(d1..10).to raise_error(ContractError) }
|
278
|
+
expect { @o.first_in_range_num(d1..10).to raise_error(ContractError) }
|
279
|
+
rescue ArgumentError
|
280
|
+
# If Ruby doesn't like the range, we ignore the test.
|
281
|
+
:nop
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
218
286
|
describe "SetOf:" do
|
219
287
|
it "should pass for a set of nums" do
|
220
288
|
expect { @o.product_from_set(Set.new([1, 2, 3])) }.to_not raise_error
|
@@ -254,6 +322,14 @@ RSpec.describe "Contracts:" do
|
|
254
322
|
end
|
255
323
|
end
|
256
324
|
|
325
|
+
describe "Optional:" do
|
326
|
+
it "can't be used outside of KeywordArgs" do
|
327
|
+
expect do
|
328
|
+
BareOptionalContractUsed.new.something(3, 5)
|
329
|
+
end.to raise_error(ArgumentError, Contracts::Optional::UNABLE_TO_USE_OUTSIDE_OF_OPT_HASH)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
257
333
|
describe "HashOf:" do
|
258
334
|
it "doesn't allow to specify multiple key-value pairs with pretty syntax" do
|
259
335
|
expect do
|
data/spec/contracts_spec.rb
CHANGED
@@ -250,7 +250,6 @@ RSpec.describe "Contracts:" do
|
|
250
250
|
let(:mod) do
|
251
251
|
Module.new do
|
252
252
|
include Contracts
|
253
|
-
include Contracts::Modules
|
254
253
|
|
255
254
|
Contract String => String
|
256
255
|
def greeting(name)
|
@@ -404,6 +403,26 @@ RSpec.describe "Contracts:" do
|
|
404
403
|
@o.double_with_proc(4)
|
405
404
|
end.to raise_error(ContractError, /Actual: nil/)
|
406
405
|
end
|
406
|
+
|
407
|
+
it "should succeed for maybe proc with no proc" do
|
408
|
+
expect do
|
409
|
+
@o.maybe_call(5)
|
410
|
+
end.to_not raise_error
|
411
|
+
end
|
412
|
+
|
413
|
+
it "should succeed for maybe proc with proc" do
|
414
|
+
expect do
|
415
|
+
@o.maybe_call(5) do
|
416
|
+
2 + 2
|
417
|
+
end
|
418
|
+
end.to_not raise_error
|
419
|
+
end
|
420
|
+
|
421
|
+
it "should fail for maybe proc with invalid input" do
|
422
|
+
expect do
|
423
|
+
@o.maybe_call("bad")
|
424
|
+
end.to raise_error(ContractError)
|
425
|
+
end
|
407
426
|
end
|
408
427
|
|
409
428
|
describe "varargs" do
|
@@ -489,6 +508,18 @@ RSpec.describe "Contracts:" do
|
|
489
508
|
it "should fail for a function that doesn't pass the contract with weak other args" do
|
490
509
|
expect { @o.map_plain(["hello", "joe"], lambda { |_| nil }) }.to raise_error(ContractError)
|
491
510
|
end
|
511
|
+
|
512
|
+
it "should fail for a returned function that doesn't pass the contract" do
|
513
|
+
expect { @o.lambda_with_wrong_return.call("hello") }.to raise_error(ContractError)
|
514
|
+
end
|
515
|
+
|
516
|
+
it "should fail for a returned function that receives the wrong argument type" do
|
517
|
+
expect { @o.lambda_with_correct_return.call(123) }.to raise_error(ContractError)
|
518
|
+
end
|
519
|
+
|
520
|
+
it "should not fail for a returned function that passes the contract" do
|
521
|
+
expect { @o.lambda_with_correct_return.call("hello") }.to_not raise_error
|
522
|
+
end
|
492
523
|
end
|
493
524
|
|
494
525
|
describe "default args to functions" do
|
@@ -536,6 +567,38 @@ RSpec.describe "Contracts:" do
|
|
536
567
|
end
|
537
568
|
end
|
538
569
|
|
570
|
+
describe "module contracts" do
|
571
|
+
it "passes for instance of class including module" do
|
572
|
+
expect(
|
573
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithModule.new)
|
574
|
+
).to eq(:world)
|
575
|
+
end
|
576
|
+
|
577
|
+
it "passes for instance of class including inherited module" do
|
578
|
+
expect(
|
579
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithInheritedModule.new)
|
580
|
+
).to eq(:world)
|
581
|
+
end
|
582
|
+
|
583
|
+
it "does not pass for instance of class not including module" do
|
584
|
+
expect do
|
585
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithoutModule.new)
|
586
|
+
end.to raise_error(ContractError, /Expected: ModuleContractExample::AModule/)
|
587
|
+
end
|
588
|
+
|
589
|
+
it "does not pass for instance of class including another module" do
|
590
|
+
expect do
|
591
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithAnotherModule.new)
|
592
|
+
end.to raise_error(ContractError, /Expected: ModuleContractExample::AModule/)
|
593
|
+
end
|
594
|
+
|
595
|
+
it "passes for instance of class including both modules" do
|
596
|
+
expect(
|
597
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithBothModules.new)
|
598
|
+
).to eq(:world)
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
539
602
|
describe "Contracts to_s formatting in expected" do
|
540
603
|
def not_s(match)
|
541
604
|
Regexp.new "[^\"\']#{match}[^\"\']"
|
data/spec/fixtures/fixtures.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "date"
|
2
|
+
|
1
3
|
class A
|
2
4
|
include Contracts
|
3
5
|
|
@@ -120,8 +122,13 @@ class GenericExample
|
|
120
122
|
end
|
121
123
|
|
122
124
|
Contract Proc => Any
|
123
|
-
def do_call(&
|
124
|
-
|
125
|
+
def do_call(&block)
|
126
|
+
block.call
|
127
|
+
end
|
128
|
+
|
129
|
+
Contract Args[Num], Maybe[Proc] => Any
|
130
|
+
def maybe_call(*vals, &block)
|
131
|
+
block.call if block
|
125
132
|
end
|
126
133
|
|
127
134
|
Contract Args[Num] => Num
|
@@ -219,6 +226,16 @@ class GenericExample
|
|
219
226
|
end
|
220
227
|
end
|
221
228
|
|
229
|
+
Contract RangeOf[Num] => Num
|
230
|
+
def first_in_range_num(r)
|
231
|
+
r.first
|
232
|
+
end
|
233
|
+
|
234
|
+
Contract RangeOf[Date] => Date
|
235
|
+
def first_in_range_date(r)
|
236
|
+
r.first
|
237
|
+
end
|
238
|
+
|
222
239
|
Contract Bool => nil
|
223
240
|
def bool_test(x)
|
224
241
|
end
|
@@ -262,6 +279,16 @@ class GenericExample
|
|
262
279
|
end
|
263
280
|
end
|
264
281
|
|
282
|
+
Contract None => Func[String => Num]
|
283
|
+
def lambda_with_wrong_return
|
284
|
+
lambda { |x| x }
|
285
|
+
end
|
286
|
+
|
287
|
+
Contract None => Func[String => Num]
|
288
|
+
def lambda_with_correct_return
|
289
|
+
lambda { |x| x.length }
|
290
|
+
end
|
291
|
+
|
265
292
|
Contract Num => Num
|
266
293
|
def default_args(x = 1)
|
267
294
|
2
|
@@ -528,10 +555,7 @@ with_enabled_no_contracts do
|
|
528
555
|
end
|
529
556
|
|
530
557
|
module ModuleExample
|
531
|
-
# This inclusion is required to actually override `method_added`
|
532
|
-
# hooks for module.
|
533
558
|
include Contracts
|
534
|
-
include Contracts::Modules
|
535
559
|
|
536
560
|
Contract Num, Num => Num
|
537
561
|
def plus(a, b)
|
@@ -567,3 +591,51 @@ end
|
|
567
591
|
|
568
592
|
class SingletonInheritanceExampleSubclass < SingletonInheritanceExample
|
569
593
|
end
|
594
|
+
|
595
|
+
class BareOptionalContractUsed
|
596
|
+
include Contracts
|
597
|
+
|
598
|
+
Contract Num, Optional[Num] => nil
|
599
|
+
def something(a, b)
|
600
|
+
nil
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
module ModuleContractExample
|
605
|
+
include Contracts
|
606
|
+
|
607
|
+
module AModule
|
608
|
+
end
|
609
|
+
|
610
|
+
module AnotherModule
|
611
|
+
end
|
612
|
+
|
613
|
+
module InheritedModule
|
614
|
+
include AModule
|
615
|
+
end
|
616
|
+
|
617
|
+
class AClassWithModule
|
618
|
+
include AModule
|
619
|
+
end
|
620
|
+
|
621
|
+
class AClassWithoutModule
|
622
|
+
end
|
623
|
+
|
624
|
+
class AClassWithAnotherModule
|
625
|
+
include AnotherModule
|
626
|
+
end
|
627
|
+
|
628
|
+
class AClassWithInheritedModule
|
629
|
+
include InheritedModule
|
630
|
+
end
|
631
|
+
|
632
|
+
class AClassWithBothModules
|
633
|
+
include AModule
|
634
|
+
include AnotherModule
|
635
|
+
end
|
636
|
+
|
637
|
+
Contract AModule => Symbol
|
638
|
+
def self.hello(thing)
|
639
|
+
:world
|
640
|
+
end
|
641
|
+
end
|