contracts 0.12.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/CHANGELOG.markdown +6 -0
- data/TUTORIAL.md +27 -25
- data/features/builtin_contracts/args.feature +80 -1
- data/features/builtin_contracts/int.feature +93 -0
- data/features/builtin_contracts/nat_pos.feature +119 -0
- data/lib/contracts/builtin_contracts.rb +15 -1
- data/lib/contracts/decorators.rb +4 -0
- data/lib/contracts/engine/eigenclass.rb +4 -0
- data/lib/contracts/engine/target.rb +2 -0
- data/lib/contracts/version.rb +1 -1
- data/spec/contracts_spec.rb +8 -26
- data/spec/fixtures/fixtures.rb +6 -0
- data/spec/override_validators_spec.rb +1 -1
- data/spec/validators_spec.rb +1 -1
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
NDFhYTJmODVkY2FlNWE3ZGI2ZjcxM2JiODMyODBlZjM4MmRjZDkwZQ==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e31d0dc3c6a107d0255ab5af8229749e6ce825e6
|
4
|
+
data.tar.gz: 915617473470b5450ee855605380d7d6552d49a8
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ZDAwMmNhZWVkMTMxMDZkZTUwOGMwN2VhYjBlOTg2MGFhNmEzNjM1N2Q0NTJi
|
11
|
-
YzMzN2Y1ZTBiMWIwZDgyYWM0MmM5N2IyMjQ3ZWVmZGMwYzQxZDE=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZTI2OWM5ZTNmYjFmMTI1NjU5ODUwNzM4MTg3NDBjYjkxM2RkZDJiYTBjNTcy
|
14
|
-
ZGY4NzYzZWU2NmM3Njc2ZmFlZjAzYzBkZDdhYTQ3ZmViNDYyMDY5Yzc2N2M3
|
15
|
-
Y2U5NTczYjdiODZjZjAxMzNiOGVjYTkyNzlhY2UyYmM3NDQzOWQ=
|
6
|
+
metadata.gz: 73c20ffc1467c52de7a44f91cf1711049c2db2f47aaf3b8faf517a0ba05030aa891c3748a0287ed6d5be799f96d3eb9b92eb8ea950041b0ff459e63262388a56
|
7
|
+
data.tar.gz: 0343d43d8437dcb11255392d53cb57e06119eb0dbd7f20880c0e0646d40b1abf9888e6b1fd5450464eed6ec4635444f7817b87228b1560eca81f0e4997a79eee
|
data/CHANGELOG.markdown
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## v0.13.0
|
2
|
+
|
3
|
+
- Enhancement: Add support for Ruby 2.3 - [Oleksii Fedorov](https://github.com/waterlink) [#216](https://github.com/egonSchiele/contracts.ruby/pull/216)
|
4
|
+
- Enhancement: Added Int, Nat and NatPos builtin contracts - [Simon George](https://github.com/sfcgeorge) [#212](https://github.com/egonSchiele/contracts.ruby/pull/212)
|
5
|
+
- Bugfix: Allow contracts on singleton of subclass - [Oleksii Federov](https://github.com/waterlink) [#211](https://github.com/egonSchiele/contracts.ruby/pull/211)
|
6
|
+
|
1
7
|
## v0.12.0
|
2
8
|
|
3
9
|
- Feature: add `Regexp` validator - [Gert Goet](https://github.com/eval) [#196](https://github.com/egonSchiele/contracts.ruby/pull/196)
|
data/TUTORIAL.md
CHANGED
@@ -68,42 +68,44 @@ This can be useful if you're in a REPL and want to figure out how a function sho
|
|
68
68
|
contracts.ruby comes with a lot of built-in contracts, including the following:
|
69
69
|
|
70
70
|
* Basic types
|
71
|
-
* [`Num`](http://www.rubydoc.info/gems/contracts/Contracts/Num) – checks that the argument is `Numeric`
|
72
|
-
* [`Pos`](http://www.rubydoc.info/gems/contracts/Contracts/Pos) – checks that the argument is a positive number
|
73
|
-
* [`Neg`](http://www.rubydoc.info/gems/contracts/Contracts/Neg) – checks that the argument is a negative number
|
74
|
-
* [`
|
75
|
-
* [`
|
76
|
-
* [`
|
77
|
-
* [`
|
71
|
+
* [`Num`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Num) – checks that the argument is `Numeric`
|
72
|
+
* [`Pos`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Pos) – checks that the argument is a positive number
|
73
|
+
* [`Neg`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Neg) – checks that the argument is a negative number
|
74
|
+
* [`Int`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Int) – checks that the argument is an integer
|
75
|
+
* [`Nat`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Nat) – checks that the argument is a natural number (>= 0)
|
76
|
+
* [`NatPos`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/NatPos) – checks that the argument is a positive natural number (> 0)
|
77
|
+
* [`Bool`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Bool) – checks that the argument is `true` or `false`
|
78
|
+
* [`Any`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Any) – Passes for any argument. Use when the argument has no constraints.
|
79
|
+
* [`None`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/None) – Fails for any argument. Use when the method takes no arguments.
|
78
80
|
|
79
81
|
* Logical combinations
|
80
|
-
* [`Maybe`](http://www.rubydoc.info/gems/contracts/Contracts/Maybe) – specifies that a value _may be_ nil, e.g. `Maybe[String]` (equivalent to `Or[String,nil]`)
|
81
|
-
* [`Or`](http://www.rubydoc.info/gems/contracts/Contracts/Or) – passes if any of the given contracts pass, e.g. `Or[Fixnum, Float]`
|
82
|
-
* [`Xor`](http://www.rubydoc.info/gems/contracts/Contracts/Xor) – passes if exactly one of the given contracts pass, e.g. `Xor[Fixnum, Float]`
|
83
|
-
* [`And`](http://www.rubydoc.info/gems/contracts/Contracts/And) – passes if all contracts pass, e.g. `And[Nat, -> (n) { n.even? }]`
|
84
|
-
* [`Not`](http://www.rubydoc.info/gems/contracts/Contracts/Not) – passes if all contracts fail for the given argument, e.g. `Not[nil]`
|
82
|
+
* [`Maybe`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Maybe) – specifies that a value _may be_ nil, e.g. `Maybe[String]` (equivalent to `Or[String,nil]`)
|
83
|
+
* [`Or`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Or) – passes if any of the given contracts pass, e.g. `Or[Fixnum, Float]`
|
84
|
+
* [`Xor`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Xor) – passes if exactly one of the given contracts pass, e.g. `Xor[Fixnum, Float]`
|
85
|
+
* [`And`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/And) – passes if all contracts pass, e.g. `And[Nat, -> (n) { n.even? }]`
|
86
|
+
* [`Not`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Not) – passes if all contracts fail for the given argument, e.g. `Not[nil]`
|
85
87
|
|
86
88
|
* Collections
|
87
|
-
* [`ArrayOf`](http://www.rubydoc.info/gems/contracts/Contracts/ArrayOf) – checks that the argument is an array, and all elements pass the given contract, e.g. `ArrayOf[Num]`
|
88
|
-
* [`SetOf`](http://www.rubydoc.info/gems/contracts/Contracts/SetOf) – checks that the argument is a set, and all elements pass the given contract, e.g. `SetOf[Num]`
|
89
|
-
* [`HashOf`](http://www.rubydoc.info/gems/contracts/Contracts/HashOf) – checks that the argument is a hash, and all keys and values pass the given contract, e.g. `HashOf[Symbol => String]` or `HashOf[Symbol,String]`
|
90
|
-
* [`RangeOf`](http://www.rubydoc.info/gems/contracts/Contracts/RangeOf) – checks that the argument is a range whose elements (#first and #last) pass the given contract, e.g. `RangeOf[Date]`
|
91
|
-
* [`Enum`](http://www.rubydoc.info/gems/contracts/Contracts/Enum) – checks that the argument is part of a given collection of objects, e.g. `Enum[:a, :b, :c]`
|
89
|
+
* [`ArrayOf`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/ArrayOf) – checks that the argument is an array, and all elements pass the given contract, e.g. `ArrayOf[Num]`
|
90
|
+
* [`SetOf`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/SetOf) – checks that the argument is a set, and all elements pass the given contract, e.g. `SetOf[Num]`
|
91
|
+
* [`HashOf`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/HashOf) – checks that the argument is a hash, and all keys and values pass the given contract, e.g. `HashOf[Symbol => String]` or `HashOf[Symbol,String]`
|
92
|
+
* [`RangeOf`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/RangeOf) – checks that the argument is a range whose elements (#first and #last) pass the given contract, e.g. `RangeOf[Date]`
|
93
|
+
* [`Enum`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Enum) – checks that the argument is part of a given collection of objects, e.g. `Enum[:a, :b, :c]`
|
92
94
|
|
93
95
|
* Keyword arguments
|
94
|
-
* [`KeywordArgs`](http://www.rubydoc.info/gems/contracts/Contracts/KeywordArgs) – checks that the argument is an options hash, and all required keyword arguments are present, and all values pass their respective contracts, e.g. `KeywordArgs[:number => Num, :description => Optional[String]]`
|
95
|
-
* [`Optional`](http://www.rubydoc.info/gems/contracts/Contracts/Optional) – checks that the keyword argument is either not present or pass the given contract, can not be used outside of `KeywordArgs` contract, e.g. `Optional[Num]`
|
96
|
+
* [`KeywordArgs`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/KeywordArgs) – checks that the argument is an options hash, and all required keyword arguments are present, and all values pass their respective contracts, e.g. `KeywordArgs[:number => Num, :description => Optional[String]]`
|
97
|
+
* [`Optional`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Optional) – checks that the keyword argument is either not present or pass the given contract, can not be used outside of `KeywordArgs` contract, e.g. `Optional[Num]`
|
96
98
|
|
97
99
|
* Duck typing
|
98
|
-
* [`RespondTo`](http://www.rubydoc.info/gems/contracts/Contracts/RespondTo) – checks that the argument responds to all of the given methods, e.g. `RespondTo[:password, :credit_card]`
|
99
|
-
* [`Send`](http://www.rubydoc.info/gems/contracts/Contracts/Send) – checks that all named methods return a truthy value, e.g. `Send[:valid?]`
|
100
|
+
* [`RespondTo`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/RespondTo) – checks that the argument responds to all of the given methods, e.g. `RespondTo[:password, :credit_card]`
|
101
|
+
* [`Send`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Send) – checks that all named methods return a truthy value, e.g. `Send[:valid?]`
|
100
102
|
|
101
103
|
* Miscellaneous
|
102
|
-
* [`Exactly`](http://www.rubydoc.info/gems/contracts/Contracts/Exactly) – checks that the argument has the given type, not accepting sub-classes, e.g. `Exactly[Numeric]`.
|
103
|
-
* [`Eq`](http://www.rubydoc.info/gems/contracts/Contracts/Eq) – checks that the argument is precisely equal to the given value, e.g. `Eq[String]` matches the class `String` and not a string instance.
|
104
|
-
* [`Func`](http://www.rubydoc.info/gems/contracts/Contracts/Func) – specifies the contract for a proc/lambda e.g. `Contract ArrayOf[Num], Func[Num => Num] => ArrayOf[Num]`. See section "Contracts On Functions".
|
104
|
+
* [`Exactly`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Exactly) – checks that the argument has the given type, not accepting sub-classes, e.g. `Exactly[Numeric]`.
|
105
|
+
* [`Eq`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Eq) – checks that the argument is precisely equal to the given value, e.g. `Eq[String]` matches the class `String` and not a string instance.
|
106
|
+
* [`Func`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Func) – specifies the contract for a proc/lambda e.g. `Contract ArrayOf[Num], Func[Num => Num] => ArrayOf[Num]`. See section "Contracts On Functions".
|
105
107
|
|
106
|
-
To see all the built-in contracts and their full descriptions, check out the [RDoc](http://rubydoc.info/gems/contracts/Contracts).
|
108
|
+
To see all the built-in contracts and their full descriptions, check out the [RDoc](http://rubydoc.info/gems/contracts/Contracts/Builtin).
|
107
109
|
|
108
110
|
It is recommended to use shortcut for referring builtin contracts:
|
109
111
|
|
@@ -1 +1,80 @@
|
|
1
|
-
Feature: Args
|
1
|
+
Feature: Args
|
2
|
+
|
3
|
+
Used for `*args` (variadic functions). Takes contract and uses it to validate
|
4
|
+
every element passed in through `*args`.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
Contract C::Args[C::Num] => C::Bool
|
8
|
+
def example(*args)
|
9
|
+
```
|
10
|
+
|
11
|
+
This example contract will validate all arguments passed through `*args` to
|
12
|
+
accept only numbers.
|
13
|
+
|
14
|
+
Background:
|
15
|
+
Given a file named "args_usage.rb" with:
|
16
|
+
"""ruby
|
17
|
+
require "contracts"
|
18
|
+
C = Contracts
|
19
|
+
|
20
|
+
class Example
|
21
|
+
include Contracts::Core
|
22
|
+
|
23
|
+
Contract C::Args[C::Num] => C::Bool
|
24
|
+
def only_nums(*args)
|
25
|
+
args.inspect
|
26
|
+
end
|
27
|
+
end
|
28
|
+
"""
|
29
|
+
|
30
|
+
Scenario: Accepts no arguments
|
31
|
+
Given a file named "accepts_no_arguments.rb" with:
|
32
|
+
"""ruby
|
33
|
+
require "./args_usage"
|
34
|
+
puts Example.new.only_nums
|
35
|
+
"""
|
36
|
+
When I run `ruby accepts_no_arguments.rb`
|
37
|
+
Then the output should contain:
|
38
|
+
"""
|
39
|
+
[]
|
40
|
+
"""
|
41
|
+
|
42
|
+
Scenario: Accepts one valid argument
|
43
|
+
Given a file named "accepts_one_argument.rb" with:
|
44
|
+
"""ruby
|
45
|
+
require "./args_usage"
|
46
|
+
puts Example.new.only_nums(42)
|
47
|
+
"""
|
48
|
+
When I run `ruby accepts_one_argument.rb`
|
49
|
+
Then the output should contain:
|
50
|
+
"""
|
51
|
+
[42]
|
52
|
+
"""
|
53
|
+
|
54
|
+
Scenario: Accepts many valid arguments
|
55
|
+
Given a file named "accepts_many_arguments.rb" with:
|
56
|
+
"""ruby
|
57
|
+
require "./args_usage"
|
58
|
+
puts Example.new.only_nums(42, 45, 17, 24)
|
59
|
+
"""
|
60
|
+
When I run `ruby accepts_many_arguments.rb`
|
61
|
+
Then the output should contain:
|
62
|
+
"""
|
63
|
+
[42, 45, 17, 24]
|
64
|
+
"""
|
65
|
+
|
66
|
+
Scenario: Rejects invalid argument
|
67
|
+
Given a file named "rejects_invalid_argument.rb" with:
|
68
|
+
"""ruby
|
69
|
+
require "./args_usage"
|
70
|
+
puts Example.new.only_nums(42, "foo", 17, 24)
|
71
|
+
"""
|
72
|
+
When I run `ruby rejects_invalid_argument.rb`
|
73
|
+
Then the output should contain:
|
74
|
+
"""
|
75
|
+
: Contract violation for argument 1 of 4: (ParamContractError)
|
76
|
+
Expected: (Args[Contracts::Builtin::Num]),
|
77
|
+
Actual: "foo"
|
78
|
+
Value guarded in: Example::only_nums
|
79
|
+
With Contract: Args => Bool
|
80
|
+
"""
|
@@ -0,0 +1,93 @@
|
|
1
|
+
Feature: Int
|
2
|
+
|
3
|
+
Checks that an argument is an integer.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
Contract C::Int => C::Int
|
7
|
+
```
|
8
|
+
|
9
|
+
Background:
|
10
|
+
Given a file named "int_usage.rb" with:
|
11
|
+
"""ruby
|
12
|
+
require "contracts"
|
13
|
+
C = Contracts
|
14
|
+
|
15
|
+
class Integr
|
16
|
+
include Contracts::Core
|
17
|
+
|
18
|
+
Contract C::Int => C::Int
|
19
|
+
def prev(number)
|
20
|
+
number - 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
"""
|
24
|
+
|
25
|
+
Scenario: Accepts positive integers
|
26
|
+
Given a file named "accepts_positive_integers.rb" with:
|
27
|
+
"""ruby
|
28
|
+
require "./int_usage"
|
29
|
+
puts Integr.new.prev(7)
|
30
|
+
"""
|
31
|
+
When I run `ruby accepts_positive_integers.rb`
|
32
|
+
Then output should contain:
|
33
|
+
"""
|
34
|
+
6
|
35
|
+
"""
|
36
|
+
|
37
|
+
Scenario: Accepts zero
|
38
|
+
Given a file named "accepts_zero.rb" with:
|
39
|
+
"""ruby
|
40
|
+
require "./int_usage"
|
41
|
+
puts Integr.new.prev(1)
|
42
|
+
"""
|
43
|
+
When I run `ruby accepts_zero.rb`
|
44
|
+
Then output should contain:
|
45
|
+
"""
|
46
|
+
0
|
47
|
+
"""
|
48
|
+
|
49
|
+
Scenario: Accepts negative integers
|
50
|
+
Given a file named "accepts_negative_integers.rb" with:
|
51
|
+
"""ruby
|
52
|
+
require "./int_usage"
|
53
|
+
puts Integr.new.prev(-1)
|
54
|
+
"""
|
55
|
+
When I run `ruby accepts_negative_integers.rb`
|
56
|
+
Then output should contain:
|
57
|
+
"""
|
58
|
+
-2
|
59
|
+
"""
|
60
|
+
|
61
|
+
Scenario: Rejects floats
|
62
|
+
Given a file named "rejects_floats.rb" with:
|
63
|
+
"""ruby
|
64
|
+
require "./int_usage"
|
65
|
+
puts Integr.new.prev(3.43)
|
66
|
+
"""
|
67
|
+
When I run `ruby rejects_floats.rb`
|
68
|
+
Then output should contain:
|
69
|
+
"""
|
70
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
71
|
+
Expected: Int,
|
72
|
+
Actual: 3.43
|
73
|
+
Value guarded in: Integr::prev
|
74
|
+
With Contract: Int => Int
|
75
|
+
"""
|
76
|
+
And output should contain "int_usage.rb:8"
|
77
|
+
|
78
|
+
Scenario: Rejects other values
|
79
|
+
Given a file named "rejects_others.rb" with:
|
80
|
+
"""ruby
|
81
|
+
require "./int_usage"
|
82
|
+
puts Integr.new.prev("foo")
|
83
|
+
"""
|
84
|
+
When I run `ruby rejects_others.rb`
|
85
|
+
Then output should contain:
|
86
|
+
"""
|
87
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
88
|
+
Expected: Int,
|
89
|
+
Actual: "foo"
|
90
|
+
Value guarded in: Integr::prev
|
91
|
+
With Contract: Int => Int
|
92
|
+
"""
|
93
|
+
And output should contain "int_usage.rb:8"
|
@@ -0,0 +1,119 @@
|
|
1
|
+
Feature: NatPos
|
2
|
+
|
3
|
+
Checks that an argument is a positive natural number.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
Contract C::NatPos => C::NatPos
|
7
|
+
```
|
8
|
+
|
9
|
+
Background:
|
10
|
+
Given a file named "nat_pos_usage.rb" with:
|
11
|
+
"""ruby
|
12
|
+
require "contracts"
|
13
|
+
C = Contracts
|
14
|
+
|
15
|
+
class NaturalPositive
|
16
|
+
include Contracts::Core
|
17
|
+
|
18
|
+
Contract C::NatPos => C::NatPos
|
19
|
+
def prev(number)
|
20
|
+
number - 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
"""
|
24
|
+
|
25
|
+
Scenario: Accepts positive integers
|
26
|
+
Given a file named "accepts_positive_integers.rb" with:
|
27
|
+
"""ruby
|
28
|
+
require "./nat_pos_usage"
|
29
|
+
puts NaturalPositive.new.prev(7)
|
30
|
+
"""
|
31
|
+
When I run `ruby accepts_positive_integers.rb`
|
32
|
+
Then output should contain:
|
33
|
+
"""
|
34
|
+
6
|
35
|
+
"""
|
36
|
+
|
37
|
+
Scenario: Rejects zero
|
38
|
+
Given a file named "rejects_zero.rb" with:
|
39
|
+
"""ruby
|
40
|
+
require "./nat_pos_usage"
|
41
|
+
puts NaturalPositive.new.prev(0)
|
42
|
+
"""
|
43
|
+
When I run `ruby rejects_zero.rb`
|
44
|
+
Then output should contain:
|
45
|
+
"""
|
46
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
47
|
+
Expected: NatPos,
|
48
|
+
Actual: 0
|
49
|
+
Value guarded in: NaturalPositive::prev
|
50
|
+
With Contract: NatPos => NatPos
|
51
|
+
"""
|
52
|
+
|
53
|
+
Scenario: Rejects negative integers
|
54
|
+
Given a file named "rejects_negative_integers.rb" with:
|
55
|
+
"""ruby
|
56
|
+
require "./nat_pos_usage"
|
57
|
+
puts NaturalPositive.new.prev(-1)
|
58
|
+
"""
|
59
|
+
When I run `ruby rejects_negative_integers.rb`
|
60
|
+
Then output should contain:
|
61
|
+
"""
|
62
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
63
|
+
Expected: NatPos,
|
64
|
+
Actual: -1
|
65
|
+
Value guarded in: NaturalPositive::prev
|
66
|
+
With Contract: NatPos => NatPos
|
67
|
+
"""
|
68
|
+
And output should contain "nat_pos_usage.rb:8"
|
69
|
+
|
70
|
+
Scenario: Rejects negative integers as a return value
|
71
|
+
Given a file named "rejects_negative_integers.rb" with:
|
72
|
+
"""ruby
|
73
|
+
require "./nat_pos_usage"
|
74
|
+
puts NaturalPositive.new.prev(1)
|
75
|
+
"""
|
76
|
+
When I run `ruby rejects_negative_integers.rb`
|
77
|
+
Then output should contain:
|
78
|
+
"""
|
79
|
+
: Contract violation for return value: (ReturnContractError)
|
80
|
+
Expected: NatPos,
|
81
|
+
Actual: 0
|
82
|
+
Value guarded in: NaturalPositive::prev
|
83
|
+
With Contract: NatPos => NatPos
|
84
|
+
"""
|
85
|
+
And output should contain "nat_pos_usage.rb:8"
|
86
|
+
|
87
|
+
Scenario: Rejects floats
|
88
|
+
Given a file named "rejects_floats.rb" with:
|
89
|
+
"""ruby
|
90
|
+
require "./nat_pos_usage"
|
91
|
+
puts NaturalPositive.new.prev(3.43)
|
92
|
+
"""
|
93
|
+
When I run `ruby rejects_floats.rb`
|
94
|
+
Then output should contain:
|
95
|
+
"""
|
96
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
97
|
+
Expected: NatPos,
|
98
|
+
Actual: 3.43
|
99
|
+
Value guarded in: NaturalPositive::prev
|
100
|
+
With Contract: NatPos => NatPos
|
101
|
+
"""
|
102
|
+
And output should contain "nat_pos_usage.rb:8"
|
103
|
+
|
104
|
+
Scenario: Rejects other values
|
105
|
+
Given a file named "rejects_others.rb" with:
|
106
|
+
"""ruby
|
107
|
+
require "./nat_pos_usage"
|
108
|
+
puts NaturalPositive.new.prev("foo")
|
109
|
+
"""
|
110
|
+
When I run `ruby rejects_others.rb`
|
111
|
+
Then output should contain:
|
112
|
+
"""
|
113
|
+
: Contract violation for argument 1 of 1: (ParamContractError)
|
114
|
+
Expected: NatPos,
|
115
|
+
Actual: "foo"
|
116
|
+
Value guarded in: NaturalPositive::prev
|
117
|
+
With Contract: NatPos => NatPos
|
118
|
+
"""
|
119
|
+
And output should contain "nat_pos_usage.rb:8"
|
@@ -41,13 +41,27 @@ module Contracts
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
# Check that an argument is
|
44
|
+
# Check that an argument is an +Integer+.
|
45
|
+
class Int
|
46
|
+
def self.valid? val
|
47
|
+
val && val.is_a?(Integer)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Check that an argument is a natural number (includes zero).
|
45
52
|
class Nat
|
46
53
|
def self.valid? val
|
47
54
|
val && val.is_a?(Integer) && val >= 0
|
48
55
|
end
|
49
56
|
end
|
50
57
|
|
58
|
+
# Check that an argument is a positive natural number (excludes zero).
|
59
|
+
class NatPos
|
60
|
+
def self.valid? val
|
61
|
+
val && val.is_a?(Integer) && val > 0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
51
65
|
# Passes for any argument.
|
52
66
|
class Any
|
53
67
|
def self.valid? val
|
data/lib/contracts/decorators.rb
CHANGED
@@ -18,6 +18,10 @@ module Contracts
|
|
18
18
|
return Engine.fetch_from(eigenclass) if Engine.applied?(eigenclass)
|
19
19
|
|
20
20
|
Target.new(eigenclass).apply(Eigenclass)
|
21
|
+
eigenclass.extend(MethodDecorators)
|
22
|
+
# FIXME; this should detect what user uses `include Contracts` or
|
23
|
+
# `include Contracts;;Core`
|
24
|
+
eigenclass.send(:include, Contracts)
|
21
25
|
Engine.fetch_from(owner).set_eigenclass_owner
|
22
26
|
Engine.fetch_from(eigenclass)
|
23
27
|
end
|
data/lib/contracts/version.rb
CHANGED
data/spec/contracts_spec.rb
CHANGED
@@ -141,32 +141,6 @@ RSpec.describe "Contracts:" do
|
|
141
141
|
end.to raise_error(ContractError, /Expected: String/)
|
142
142
|
end
|
143
143
|
|
144
|
-
context "when owner class does not include Contracts" do
|
145
|
-
let(:error) do
|
146
|
-
# NOTE Unable to support this user-friendly error for ruby
|
147
|
-
# 1.8.7 and jruby 1.8, 1.9 it has much less support for
|
148
|
-
# singleton inheritance hierarchy
|
149
|
-
if Contracts::Support.eigenclass_hierarchy_supported?
|
150
|
-
[Contracts::ContractsNotIncluded, Contracts::ContractsNotIncluded::DEFAULT_MESSAGE]
|
151
|
-
else
|
152
|
-
[NoMethodError, /undefined method `Contract'/]
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
it "fails with descriptive error" do
|
157
|
-
expect do
|
158
|
-
Class.new(GenericExample) do
|
159
|
-
class << self
|
160
|
-
Contract String => String
|
161
|
-
def hoge(name)
|
162
|
-
"super#{name}"
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end.to raise_error(*error)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
144
|
describe "builtin contracts usage" do
|
171
145
|
it "allows to use builtin contracts without namespacing and redundant Contracts inclusion" do
|
172
146
|
expect do
|
@@ -176,6 +150,14 @@ RSpec.describe "Contracts:" do
|
|
176
150
|
end
|
177
151
|
end
|
178
152
|
|
153
|
+
describe "usage in the singleton class of a subclass" do
|
154
|
+
subject { SingletonInheritanceExampleSubclass }
|
155
|
+
|
156
|
+
it "should work with a valid contract on a singleton method" do
|
157
|
+
expect(subject.num(1)).to eq(1)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
179
161
|
describe "no contracts feature" do
|
180
162
|
it "disables normal contract checks" do
|
181
163
|
object = NoContractsSimpleExample.new
|
data/spec/fixtures/fixtures.rb
CHANGED
@@ -621,6 +621,12 @@ class SingletonInheritanceExample
|
|
621
621
|
end
|
622
622
|
|
623
623
|
class SingletonInheritanceExampleSubclass < SingletonInheritanceExample
|
624
|
+
class << self
|
625
|
+
Contract Integer => Integer
|
626
|
+
def num(int)
|
627
|
+
int
|
628
|
+
end
|
629
|
+
end
|
624
630
|
end
|
625
631
|
|
626
632
|
class BareOptionalContractUsed
|
data/spec/validators_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contracts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aditya Bhargava
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: This library provides contracts for Ruby. Contracts let you clearly express
|
14
14
|
how your code behaves, and free you from writing tons of boilerplate, defensive
|
@@ -18,10 +18,10 @@ executables: []
|
|
18
18
|
extensions: []
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
|
-
- .gitignore
|
22
|
-
- .rspec
|
23
|
-
- .rubocop.yml
|
24
|
-
- .travis.yml
|
21
|
+
- ".gitignore"
|
22
|
+
- ".rspec"
|
23
|
+
- ".rubocop.yml"
|
24
|
+
- ".travis.yml"
|
25
25
|
- CHANGELOG.markdown
|
26
26
|
- Gemfile
|
27
27
|
- README.md
|
@@ -49,9 +49,11 @@ files:
|
|
49
49
|
- features/builtin_contracts/exactly.feature
|
50
50
|
- features/builtin_contracts/func.feature
|
51
51
|
- features/builtin_contracts/hash_of.feature
|
52
|
+
- features/builtin_contracts/int.feature
|
52
53
|
- features/builtin_contracts/keyword_args.feature
|
53
54
|
- features/builtin_contracts/maybe.feature
|
54
55
|
- features/builtin_contracts/nat.feature
|
56
|
+
- features/builtin_contracts/nat_pos.feature
|
55
57
|
- features/builtin_contracts/neg.feature
|
56
58
|
- features/builtin_contracts/none.feature
|
57
59
|
- features/builtin_contracts/not.feature
|
@@ -107,17 +109,17 @@ require_paths:
|
|
107
109
|
- lib
|
108
110
|
required_ruby_version: !ruby/object:Gem::Requirement
|
109
111
|
requirements:
|
110
|
-
- -
|
112
|
+
- - ">="
|
111
113
|
- !ruby/object:Gem::Version
|
112
114
|
version: '0'
|
113
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
116
|
requirements:
|
115
|
-
- -
|
117
|
+
- - ">="
|
116
118
|
- !ruby/object:Gem::Version
|
117
119
|
version: '0'
|
118
120
|
requirements: []
|
119
121
|
rubyforge_project:
|
120
|
-
rubygems_version: 2.4.
|
122
|
+
rubygems_version: 2.4.6
|
121
123
|
signing_key:
|
122
124
|
specification_version: 4
|
123
125
|
summary: Contracts for Ruby.
|