contracts 0.11.0 → 0.12.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 +13 -5
- data/CHANGELOG.markdown +8 -0
- data/Gemfile +3 -0
- data/README.md +14 -10
- data/TUTORIAL.md +34 -1
- data/benchmarks/io.rb +3 -3
- data/cucumber.yml +1 -0
- data/features/README.md +17 -0
- data/features/basics/functype.feature +71 -0
- data/features/basics/simple_example.feature +210 -0
- data/features/builtin_contracts/README.md +22 -0
- data/features/builtin_contracts/and.feature +103 -0
- data/features/builtin_contracts/any.feature +44 -0
- data/features/builtin_contracts/args.feature +1 -0
- data/features/builtin_contracts/array_of.feature +1 -0
- data/features/builtin_contracts/bool.feature +64 -0
- data/features/builtin_contracts/enum.feature +1 -0
- data/features/builtin_contracts/eq.feature +1 -0
- data/features/builtin_contracts/exactly.feature +1 -0
- data/features/builtin_contracts/func.feature +1 -0
- data/features/builtin_contracts/hash_of.feature +1 -0
- data/features/builtin_contracts/keyword_args.feature +1 -0
- data/features/builtin_contracts/maybe.feature +1 -0
- data/features/builtin_contracts/nat.feature +115 -0
- data/features/builtin_contracts/neg.feature +115 -0
- data/features/builtin_contracts/none.feature +145 -0
- data/features/builtin_contracts/not.feature +1 -0
- data/features/builtin_contracts/num.feature +64 -0
- data/features/builtin_contracts/or.feature +83 -0
- data/features/builtin_contracts/pos.feature +116 -0
- data/features/builtin_contracts/range_of.feature +1 -0
- data/features/builtin_contracts/respond_to.feature +78 -0
- data/features/builtin_contracts/send.feature +147 -0
- data/features/builtin_contracts/set_of.feature +1 -0
- data/features/builtin_contracts/xor.feature +99 -0
- data/features/support/env.rb +6 -0
- data/lib/contracts.rb +1 -1
- data/lib/contracts/builtin_contracts.rb +356 -351
- data/lib/contracts/core.rb +11 -2
- data/lib/contracts/formatters.rb +2 -2
- data/lib/contracts/validators.rb +6 -0
- data/lib/contracts/version.rb +1 -1
- data/script/cucumber +5 -0
- data/script/docs-release +3 -0
- data/script/docs-staging +3 -0
- data/spec/builtin_contracts_spec.rb +1 -1
- data/spec/contracts_spec.rb +29 -0
- data/spec/fixtures/fixtures.rb +12 -0
- data/spec/validators_spec.rb +25 -3
- metadata +42 -9
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MzAzMDEzNDY4MjVjYWM2Njk2ZWViYTI0MTQ4MTI5ZGZiMGZhOTNhNA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NDFhYTJmODVkY2FlNWE3ZGI2ZjcxM2JiODMyODBlZjM4MmRjZDkwZQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZjNmODA2MDM5NGI0MjVjMzI0MDNlYmRjZWMzOTUxNmFhYmE0NWU1OGIwNzEz
|
10
|
+
ZDAwMmNhZWVkMTMxMDZkZTUwOGMwN2VhYjBlOTg2MGFhNmEzNjM1N2Q0NTJi
|
11
|
+
YzMzN2Y1ZTBiMWIwZDgyYWM0MmM5N2IyMjQ3ZWVmZGMwYzQxZDE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZTI2OWM5ZTNmYjFmMTI1NjU5ODUwNzM4MTg3NDBjYjkxM2RkZDJiYTBjNTcy
|
14
|
+
ZGY4NzYzZWU2NmM3Njc2ZmFlZjAzYzBkZDdhYTQ3ZmViNDYyMDY5Yzc2N2M3
|
15
|
+
Y2U5NTczYjdiODZjZjAxMzNiOGVjYTkyNzlhY2UyYmM3NDQzOWQ=
|
data/CHANGELOG.markdown
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
+
## v0.12.0
|
2
|
+
|
3
|
+
- Feature: add `Regexp` validator - [Gert Goet](https://github.com/eval) [#196](https://github.com/egonSchiele/contracts.ruby/pull/196)
|
4
|
+
- Docs: bootstrap cucumber/aruba/relish setup - [Oleksii Fedorov](https://github.com/waterlink) [#195](https://github.com/egonSchiele/contracts.ruby/pull/195)
|
5
|
+
- Bugfix: allow to `extend` module, that has `Contracts` or `Contracts::Core` included without harming current module/class `Contracts` functionality, see: [#176](https://github.com/egonSchiele/contracts.ruby/issues/176) - [Oleksii Fedorov](https://github.com/waterlink) [#198](https://github.com/egonSchiele/contracts.ruby/pull/198)
|
6
|
+
- Enhancement: add `include Contracts::Builtin` to allow users to use builtin contracts without `Contracts::` prefix together with `include Contracts::Core` - [PikachuEXE](https://github.com/PikachuEXE) [#199](https://github.com/egonSchiele/contracts.ruby/pull/199)
|
7
|
+
|
1
8
|
## v0.11.0
|
9
|
+
|
2
10
|
- Enhancement: add `include Contracts::Core` that doesn't pollute the namespace as much as `include Contracts` - [Oleksii Federov](https://github.com/waterlink) [#185](https://github.com/egonSchiele/contracts.ruby/pull/185)
|
3
11
|
- Bugfix: fail if a non-hash is provided to a `HashOf` contract - [Abe Voelker](https://github.com/abevoelker) [#190](https://github.com/egonSchiele/contracts.ruby/pull/190)
|
4
12
|
- Bugfix: bugfix for using varargs and `Maybe[Proc]` together - [Adit Bhargava](https://github.com/egonSchiele) [#188](https://github.com/egonSchiele/contracts.ruby/pull/188)
|
data/Gemfile
CHANGED
@@ -4,10 +4,13 @@ gemspec
|
|
4
4
|
|
5
5
|
group :test do
|
6
6
|
gem "rspec"
|
7
|
+
gem "aruba"
|
8
|
+
gem "cucumber", "~> 1.3.20"
|
7
9
|
gem "rubocop", "~> 0.29.1", :platform => [:ruby_20, :ruby_21, :ruby_22]
|
8
10
|
end
|
9
11
|
|
10
12
|
group :development do
|
13
|
+
gem "relish"
|
11
14
|
gem "method_profiler"
|
12
15
|
gem "ruby-prof"
|
13
16
|
gem "rake"
|
data/README.md
CHANGED
@@ -23,24 +23,28 @@ This says that double expects a number and returns a number. Here's the full cod
|
|
23
23
|
|
24
24
|
```ruby
|
25
25
|
require 'contracts'
|
26
|
-
include Contracts
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
class Example
|
28
|
+
include Contracts::Core
|
29
|
+
include Contracts::Builtin
|
30
|
+
|
31
|
+
Contract Num => Num
|
32
|
+
def double(x)
|
33
|
+
x * 2
|
34
|
+
end
|
31
35
|
end
|
32
36
|
|
33
|
-
puts double("oops")
|
37
|
+
puts Example.new.double("oops")
|
34
38
|
```
|
35
39
|
|
36
40
|
Save this in a file and run it. Notice we are calling `double` with `"oops"`, which is not a number. The contract fails with a detailed error message:
|
37
41
|
|
38
|
-
|
39
|
-
Expected:
|
42
|
+
ParamContractError: Contract violation for argument 1 of 1:
|
43
|
+
Expected: Num,
|
40
44
|
Actual: "oops"
|
41
|
-
Value guarded in:
|
42
|
-
With Contract:
|
43
|
-
At: main.rb:
|
45
|
+
Value guarded in: Example::double
|
46
|
+
With Contract: Num => Num
|
47
|
+
At: main.rb:8
|
44
48
|
...stack trace...
|
45
49
|
|
46
50
|
Instead of throwing an exception, you could log it, print a clean error message for your user...whatever you want. contracts.ruby is here to help you handle bugs better, not to get in your way.
|
data/TUTORIAL.md
CHANGED
@@ -63,7 +63,7 @@ This can be useful if you're in a REPL and want to figure out how a function sho
|
|
63
63
|
|
64
64
|
## Built-in Contracts
|
65
65
|
|
66
|
-
`Num` is one of the built-in contracts that contracts.ruby comes with. The built-in contracts are in the `Contracts` namespace. The easiest way to use them is to include the `Contracts` module in your class/module.
|
66
|
+
`Num` is one of the built-in contracts that contracts.ruby comes with. The built-in contracts are in the `Contracts` namespace. The easiest way to use them is to include the `Contracts::Builtin` module in your class/module.
|
67
67
|
|
68
68
|
contracts.ruby comes with a lot of built-in contracts, including the following:
|
69
69
|
|
@@ -120,6 +120,22 @@ with while typing and anything that does not conflict with libraries you use.
|
|
120
120
|
|
121
121
|
All examples after this point assume you have chosen a shortcut as `C::`.
|
122
122
|
|
123
|
+
If you are sure, that builtin contracts will not nameclash with your own code
|
124
|
+
and libraries you may use, then you can include all builtin contracts in your
|
125
|
+
class/module:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
class Example
|
129
|
+
include Contracts::Core
|
130
|
+
include Contracts::Builtin
|
131
|
+
|
132
|
+
Contract Maybe[Num], Or[Float, String] => Bool
|
133
|
+
def complicated_algorithm(a, b)
|
134
|
+
# ...
|
135
|
+
end
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
123
139
|
## More Examples
|
124
140
|
|
125
141
|
### Hello, World
|
@@ -271,6 +287,22 @@ give_largest_value(a: 1, b: 2, c: 3) # returns 3
|
|
271
287
|
give_largest_value("a" => 1, 2 => 2, c: 3)
|
272
288
|
```
|
273
289
|
|
290
|
+
### Contracts On Strings
|
291
|
+
|
292
|
+
When you want a contract to match not just any string (i.e. `Contract String => nil`), you can use regular expressions:
|
293
|
+
```ruby
|
294
|
+
Contract /World|Mars/i => nil
|
295
|
+
def greet(name)
|
296
|
+
puts "Hello #{name}!"
|
297
|
+
end
|
298
|
+
```
|
299
|
+
|
300
|
+
Using logical combinations you can combine existing definitions, instead of writing 1 big regular expression:
|
301
|
+
```ruby
|
302
|
+
Contract C::And[default_mail_regexp, /#{AppConfig.domain}\z/] => nil
|
303
|
+
def send_admin_invite(email)
|
304
|
+
```
|
305
|
+
|
274
306
|
### Contracts On Keyword Arguments
|
275
307
|
|
276
308
|
ruby 2.0+, but can be used for normal hashes too, when keyword arguments are
|
@@ -562,6 +594,7 @@ Possible validator overrides:
|
|
562
594
|
- `override_validator(Array)` - e.g. `[C::Num, String]`,
|
563
595
|
- `override_validator(Hash)` - e.g. `{ :a => C::Num, :b => String }`,
|
564
596
|
- `override_validator(Range)` - e.g. `(1..10)`,
|
597
|
+
- `override_validator(Regexp)` - e.g. `/foo/`,
|
565
598
|
- `override_validator(Contracts::Args)` - e.g. `C::Args[C::Num]`,
|
566
599
|
- `override_validator(Contracts::Func)` - e.g. `C::Func[C::Num => C::Num]`,
|
567
600
|
- `override_validator(:valid)` - allows to override how contracts that respond to `:valid?` are handled,
|
data/benchmarks/io.rb
CHANGED
@@ -8,15 +8,15 @@ require "open-uri"
|
|
8
8
|
include Contracts
|
9
9
|
|
10
10
|
def download url
|
11
|
-
open("
|
11
|
+
open("http://www.#{url}/").read
|
12
12
|
end
|
13
13
|
|
14
14
|
Contract String => String
|
15
15
|
def contracts_download url
|
16
|
-
open("
|
16
|
+
open("http://www.#{url}").read
|
17
17
|
end
|
18
18
|
|
19
|
-
@urls = %w{
|
19
|
+
@urls = %w{google.com bing.com}
|
20
20
|
|
21
21
|
def benchmark
|
22
22
|
Benchmark.bm 30 do |x|
|
data/cucumber.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
default: --require features
|
data/features/README.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
contracts.ruby brings code contracts to the Ruby language.
|
2
|
+
|
3
|
+
Example:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Example
|
7
|
+
include Contracts::Core
|
8
|
+
C = Contracts
|
9
|
+
|
10
|
+
Contract C::Num, C::Num => C::Num
|
11
|
+
def add(a, b)
|
12
|
+
a + b
|
13
|
+
end
|
14
|
+
end
|
15
|
+
```
|
16
|
+
|
17
|
+
This documentation is [open source](https://github.com/egonSchiele/contracts.ruby/tree/master/features). If you find it incomplete or confusing, please [submit an issue](https://github.com/egonSchiele/contracts.ruby/issues), or, better yet, [a pull request](https://github.com/egonSchiele/contracts.ruby).
|
@@ -0,0 +1,71 @@
|
|
1
|
+
Feature: Fetching contracted function type
|
2
|
+
|
3
|
+
You can use `functype(name)` method for that:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
functype(:add) # => "add :: Num, Num => Num"
|
7
|
+
```
|
8
|
+
|
9
|
+
Background:
|
10
|
+
Given a file named "example.rb" with:
|
11
|
+
"""ruby
|
12
|
+
require "contracts"
|
13
|
+
C = Contracts
|
14
|
+
|
15
|
+
class Example
|
16
|
+
include Contracts::Core
|
17
|
+
|
18
|
+
Contract C::Num, C::Num => C::Num
|
19
|
+
def add(a, b)
|
20
|
+
a + b
|
21
|
+
end
|
22
|
+
|
23
|
+
Contract String => String
|
24
|
+
def self.greeting(name)
|
25
|
+
"Hello, #{name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
Contract C::Num => C::Num
|
30
|
+
def increment(number)
|
31
|
+
number + 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
"""
|
36
|
+
|
37
|
+
Scenario: functype on instance method
|
38
|
+
Given a file named "instance_method_functype.rb" with:
|
39
|
+
"""ruby
|
40
|
+
require "./example"
|
41
|
+
puts Example.new.functype(:add)
|
42
|
+
"""
|
43
|
+
When I run `ruby instance_method_functype.rb`
|
44
|
+
Then the output should contain:
|
45
|
+
"""
|
46
|
+
add :: Num, Num => Num
|
47
|
+
"""
|
48
|
+
|
49
|
+
Scenario: functype on class method
|
50
|
+
Given a file named "class_method_functype.rb" with:
|
51
|
+
"""ruby
|
52
|
+
require "./example"
|
53
|
+
puts Example.functype(:greeting)
|
54
|
+
"""
|
55
|
+
When I run `ruby class_method_functype.rb`
|
56
|
+
Then the output should contain:
|
57
|
+
"""
|
58
|
+
greeting :: String => String
|
59
|
+
"""
|
60
|
+
|
61
|
+
Scenario: functype on singleton method
|
62
|
+
Given a file named "singleton_method_functype.rb" with:
|
63
|
+
"""ruby
|
64
|
+
require "./example"
|
65
|
+
puts Example.functype(:increment)
|
66
|
+
"""
|
67
|
+
When I run `ruby singleton_method_functype.rb`
|
68
|
+
Then the output should contain:
|
69
|
+
"""
|
70
|
+
increment :: Num => Num
|
71
|
+
"""
|
@@ -0,0 +1,210 @@
|
|
1
|
+
Feature: Simple examples and Contract violations
|
2
|
+
|
3
|
+
Contracts.ruby allows specification of contracts on per-method basis, where
|
4
|
+
method arguments and return value will be validated upon method call.
|
5
|
+
|
6
|
+
Example:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
Contract C::Num, C::Num => C::Num
|
10
|
+
def add(a, b)
|
11
|
+
a + b
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
Here `Contract arg_contracts... => return_contract` defines list of argument
|
16
|
+
contracts `args_contracts...` as `C::Num, C::Num` (i.e.: both arguments
|
17
|
+
should be numbers) and return value contract `return_contract` as `C::Num`
|
18
|
+
(i.e.: return value should be a number too).
|
19
|
+
|
20
|
+
`Contract arg_contracts... => return_contract` affects next defined instance,
|
21
|
+
class or singleton method, meaning that all of these work:
|
22
|
+
|
23
|
+
- [Instance method](#instance-method),
|
24
|
+
|
25
|
+
- [Class method](#class-method),
|
26
|
+
|
27
|
+
- [Singleton method](#singleton-method).
|
28
|
+
|
29
|
+
Whenever invalid argument is passed to a contracted method, corresponding
|
30
|
+
`ContractError` will be raised. That happens right after bad value got into
|
31
|
+
system protected by contracts and prevents error propagation: first
|
32
|
+
non-contracts library frame in exception's backtrace is a culprit for passing
|
33
|
+
an invalid argument - you do not need to verify 20-30 frames to find a
|
34
|
+
culprit! Example of such error: [instance method contract
|
35
|
+
violation](#instance-method-contract-violation).
|
36
|
+
|
37
|
+
Whenever invalid return value is returned from a contracted method,
|
38
|
+
corresponding `ContractError` will be raised. That happens right after method
|
39
|
+
returned this value and prevents error propagation: `At: your_filename.rb:17`
|
40
|
+
part of error message points directly to a culprit method. Example of such
|
41
|
+
error: [return value contract
|
42
|
+
violation](#singleton-method-return-value-contract-violation).
|
43
|
+
|
44
|
+
Contract violation error consists of such parts:
|
45
|
+
- Violation type:
|
46
|
+
- `Contract violation for argument X of Y: (ParamContractError)`,
|
47
|
+
- `Contract violation for return value (ReturnContractError)`.
|
48
|
+
- Expected contract, example: `Expected: Num`.
|
49
|
+
- Actual value, example: `Actual: "foo"`.
|
50
|
+
- Location of violated contract, example: `Value guarded in: Example::add`.
|
51
|
+
- Full contract, example: `With Contract: Num, Num => Num`.
|
52
|
+
- Source code location of contracted method, example: `At: lib/your_library/some_class.rb:17`.
|
53
|
+
|
54
|
+
Scenario: Instance method
|
55
|
+
Given a file named "instance_method.rb" with:
|
56
|
+
"""ruby
|
57
|
+
require "contracts"
|
58
|
+
C = Contracts
|
59
|
+
|
60
|
+
class Example
|
61
|
+
include Contracts::Core
|
62
|
+
|
63
|
+
Contract C::Num, C::Num => C::Num
|
64
|
+
def add(a, b)
|
65
|
+
a + b
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
puts Example.new.add(2, 2)
|
70
|
+
"""
|
71
|
+
When I run `ruby instance_method.rb`
|
72
|
+
Then the output should contain:
|
73
|
+
"""
|
74
|
+
4
|
75
|
+
"""
|
76
|
+
|
77
|
+
Scenario: Instance method contract violation
|
78
|
+
Given a file named "instance_method_violation.rb" with:
|
79
|
+
"""ruby
|
80
|
+
require "contracts"
|
81
|
+
C = Contracts
|
82
|
+
|
83
|
+
class Example
|
84
|
+
include Contracts::Core
|
85
|
+
|
86
|
+
Contract C::Num, C::Num => C::Num
|
87
|
+
def add(a, b)
|
88
|
+
a + b
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
puts Example.new.add(2, "foo")
|
93
|
+
"""
|
94
|
+
When I run `ruby instance_method_violation.rb`
|
95
|
+
Then the output should contain:
|
96
|
+
"""
|
97
|
+
: Contract violation for argument 2 of 2: (ParamContractError)
|
98
|
+
Expected: Num,
|
99
|
+
Actual: "foo"
|
100
|
+
Value guarded in: Example::add
|
101
|
+
With Contract: Num, Num => Num
|
102
|
+
At: instance_method_violation.rb:8
|
103
|
+
"""
|
104
|
+
|
105
|
+
Scenario: Class method
|
106
|
+
Given a file named "class_method.rb" with:
|
107
|
+
"""ruby
|
108
|
+
require "contracts"
|
109
|
+
C = Contracts
|
110
|
+
|
111
|
+
class Example
|
112
|
+
include Contracts::Core
|
113
|
+
|
114
|
+
Contract C::Num, C::Num => C::Num
|
115
|
+
def self.add(a, b)
|
116
|
+
a + b
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
puts Example.add(2, 2)
|
121
|
+
"""
|
122
|
+
When I run `ruby class_method.rb`
|
123
|
+
Then the output should contain:
|
124
|
+
"""
|
125
|
+
4
|
126
|
+
"""
|
127
|
+
|
128
|
+
Scenario: Class method contract violation
|
129
|
+
Given a file named "class_method_violation.rb" with:
|
130
|
+
"""ruby
|
131
|
+
require "contracts"
|
132
|
+
C = Contracts
|
133
|
+
|
134
|
+
class Example
|
135
|
+
include Contracts::Core
|
136
|
+
|
137
|
+
Contract C::Num, C::Num => C::Num
|
138
|
+
def self.add(a, b)
|
139
|
+
a + b
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
puts Example.add(:foo, 2)
|
144
|
+
"""
|
145
|
+
When I run `ruby class_method_violation.rb`
|
146
|
+
Then the output should contain:
|
147
|
+
"""
|
148
|
+
: Contract violation for argument 1 of 2: (ParamContractError)
|
149
|
+
Expected: Num,
|
150
|
+
Actual: :foo
|
151
|
+
Value guarded in: Example::add
|
152
|
+
With Contract: Num, Num => Num
|
153
|
+
At: class_method_violation.rb:8
|
154
|
+
"""
|
155
|
+
|
156
|
+
Scenario: Singleton method
|
157
|
+
Given a file named "singleton_method.rb" with:
|
158
|
+
"""ruby
|
159
|
+
require "contracts"
|
160
|
+
C = Contracts
|
161
|
+
|
162
|
+
class Example
|
163
|
+
include Contracts::Core
|
164
|
+
|
165
|
+
class << self
|
166
|
+
Contract C::Num, C::Num => C::Num
|
167
|
+
def add(a, b)
|
168
|
+
a + b
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
puts Example.add(2, 2)
|
174
|
+
"""
|
175
|
+
When I run `ruby singleton_method.rb`
|
176
|
+
Then the output should contain:
|
177
|
+
"""
|
178
|
+
4
|
179
|
+
"""
|
180
|
+
|
181
|
+
Scenario: Singleton method return value contract violation
|
182
|
+
Given a file named "singleton_method_violation.rb" with:
|
183
|
+
"""ruby
|
184
|
+
require "contracts"
|
185
|
+
C = Contracts
|
186
|
+
|
187
|
+
class Example
|
188
|
+
include Contracts::Core
|
189
|
+
|
190
|
+
class << self
|
191
|
+
Contract C::Num, C::Num => C::Num
|
192
|
+
def add(a, b)
|
193
|
+
# notice here non-number is returned
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
puts Example.add(2, 2)
|
200
|
+
"""
|
201
|
+
When I run `ruby singleton_method_violation.rb`
|
202
|
+
Then the output should contain:
|
203
|
+
"""
|
204
|
+
: Contract violation for return value: (ReturnContractError)
|
205
|
+
Expected: Num,
|
206
|
+
Actual: nil
|
207
|
+
Value guarded in: Example::add
|
208
|
+
With Contract: Num, Num => Num
|
209
|
+
At: singleton_method_violation.rb:9
|
210
|
+
"""
|