contracts 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
"""
|