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.
Files changed (50) hide show
  1. checksums.yaml +13 -5
  2. data/CHANGELOG.markdown +8 -0
  3. data/Gemfile +3 -0
  4. data/README.md +14 -10
  5. data/TUTORIAL.md +34 -1
  6. data/benchmarks/io.rb +3 -3
  7. data/cucumber.yml +1 -0
  8. data/features/README.md +17 -0
  9. data/features/basics/functype.feature +71 -0
  10. data/features/basics/simple_example.feature +210 -0
  11. data/features/builtin_contracts/README.md +22 -0
  12. data/features/builtin_contracts/and.feature +103 -0
  13. data/features/builtin_contracts/any.feature +44 -0
  14. data/features/builtin_contracts/args.feature +1 -0
  15. data/features/builtin_contracts/array_of.feature +1 -0
  16. data/features/builtin_contracts/bool.feature +64 -0
  17. data/features/builtin_contracts/enum.feature +1 -0
  18. data/features/builtin_contracts/eq.feature +1 -0
  19. data/features/builtin_contracts/exactly.feature +1 -0
  20. data/features/builtin_contracts/func.feature +1 -0
  21. data/features/builtin_contracts/hash_of.feature +1 -0
  22. data/features/builtin_contracts/keyword_args.feature +1 -0
  23. data/features/builtin_contracts/maybe.feature +1 -0
  24. data/features/builtin_contracts/nat.feature +115 -0
  25. data/features/builtin_contracts/neg.feature +115 -0
  26. data/features/builtin_contracts/none.feature +145 -0
  27. data/features/builtin_contracts/not.feature +1 -0
  28. data/features/builtin_contracts/num.feature +64 -0
  29. data/features/builtin_contracts/or.feature +83 -0
  30. data/features/builtin_contracts/pos.feature +116 -0
  31. data/features/builtin_contracts/range_of.feature +1 -0
  32. data/features/builtin_contracts/respond_to.feature +78 -0
  33. data/features/builtin_contracts/send.feature +147 -0
  34. data/features/builtin_contracts/set_of.feature +1 -0
  35. data/features/builtin_contracts/xor.feature +99 -0
  36. data/features/support/env.rb +6 -0
  37. data/lib/contracts.rb +1 -1
  38. data/lib/contracts/builtin_contracts.rb +356 -351
  39. data/lib/contracts/core.rb +11 -2
  40. data/lib/contracts/formatters.rb +2 -2
  41. data/lib/contracts/validators.rb +6 -0
  42. data/lib/contracts/version.rb +1 -1
  43. data/script/cucumber +5 -0
  44. data/script/docs-release +3 -0
  45. data/script/docs-staging +3 -0
  46. data/spec/builtin_contracts_spec.rb +1 -1
  47. data/spec/contracts_spec.rb +29 -0
  48. data/spec/fixtures/fixtures.rb +12 -0
  49. data/spec/validators_spec.rb +25 -3
  50. metadata +42 -9
@@ -0,0 +1,22 @@
1
+ To use builtin contracts you can refer them with `Contracts::*`:
2
+
3
+ ```ruby
4
+ Contract Contracts::Num => Contracts::Maybe(Contracts::Num)
5
+ ```
6
+
7
+ It is recommended to use a short alias for `Contracts`, for example `C`:
8
+
9
+ ```ruby
10
+ C = Contracts
11
+
12
+ Contract C::Num => C::Maybe(C::Num)
13
+ ```
14
+
15
+ It is possible to `include Contracts` and refer them without namespace, but
16
+ this is deprecated and not recommended.
17
+
18
+ *NOTE: in the future it will be possible to do `include Contracts::Builtin`
19
+ instead.*
20
+
21
+ *NOTE: all contracts marked as (TODO) have their documentaion `.feature` file
22
+ as stub. Contributions to those are warmly welcome!*
@@ -0,0 +1,103 @@
1
+ Feature: And
2
+
3
+ Takes a variable number of contracts. The contract passes if all of the
4
+ contracts pass.
5
+
6
+ ```ruby
7
+ Contract C::And[Float, C::Neg] => String
8
+ ```
9
+
10
+ This example will validate first argument of a method and accept only
11
+ negative `Float`.
12
+
13
+ Background:
14
+ Given a file named "and_usage.rb" with:
15
+ """ruby
16
+ require "contracts"
17
+ C = Contracts
18
+
19
+ class Example
20
+ include Contracts::Core
21
+
22
+ Contract C::And[Float, C::Neg] => String
23
+ def fneg_string(number)
24
+ number.to_i.to_s
25
+ end
26
+ end
27
+ """
28
+
29
+ Scenario: Accepts negative float
30
+ Given a file named "accepts_negative_float.rb" with:
31
+ """ruby
32
+ require "./and_usage"
33
+ puts Example.new.fneg_string(-3.7)
34
+ """
35
+ When I run `ruby accepts_negative_float.rb`
36
+ Then output should contain:
37
+ """
38
+ -3
39
+ """
40
+
41
+ Scenario: Rejects positive float
42
+ Given a file named "rejects_positive_float.rb" with:
43
+ """ruby
44
+ require "./and_usage"
45
+ puts Example.new.fneg_string(7.5)
46
+ """
47
+ When I run `ruby rejects_positive_float.rb`
48
+ Then output should contain:
49
+ """
50
+ : Contract violation for argument 1 of 1: (ParamContractError)
51
+ Expected: (Float and Neg),
52
+ Actual: 7.5
53
+ Value guarded in: Example::fneg_string
54
+ With Contract: And => String
55
+ """
56
+
57
+ Scenario: Rejects negative integer
58
+ Given a file named "rejects_negative_integer.rb" with:
59
+ """ruby
60
+ require "./and_usage"
61
+ puts Example.new.fneg_string(-5)
62
+ """
63
+ When I run `ruby rejects_negative_integer.rb`
64
+ Then output should contain:
65
+ """
66
+ : Contract violation for argument 1 of 1: (ParamContractError)
67
+ Expected: (Float and Neg),
68
+ Actual: -5
69
+ Value guarded in: Example::fneg_string
70
+ With Contract: And => String
71
+ """
72
+
73
+ Scenario: Rejects positive integer
74
+ Given a file named "rejects_positive_integer.rb" with:
75
+ """ruby
76
+ require "./and_usage"
77
+ puts Example.new.fneg_string(5)
78
+ """
79
+ When I run `ruby rejects_positive_integer.rb`
80
+ Then output should contain:
81
+ """
82
+ : Contract violation for argument 1 of 1: (ParamContractError)
83
+ Expected: (Float and Neg),
84
+ Actual: 5
85
+ Value guarded in: Example::fneg_string
86
+ With Contract: And => String
87
+ """
88
+
89
+ Scenario: Rejects others
90
+ Given a file named "rejects_others.rb" with:
91
+ """ruby
92
+ require "./and_usage"
93
+ puts Example.new.fneg_string(:foo)
94
+ """
95
+ When I run `ruby rejects_others.rb`
96
+ Then output should contain:
97
+ """
98
+ : Contract violation for argument 1 of 1: (ParamContractError)
99
+ Expected: (Float and Neg),
100
+ Actual: :foo
101
+ Value guarded in: Example::fneg_string
102
+ With Contract: And => String
103
+ """
@@ -0,0 +1,44 @@
1
+ Feature: Any
2
+
3
+ Passes for any argument.
4
+
5
+ ```ruby
6
+ Contract C::Any => String
7
+ ```
8
+
9
+ Scenario: Accepts any argument
10
+ Given a file named "any_usage.rb" with:
11
+ """ruby
12
+ require "contracts"
13
+ C = Contracts
14
+
15
+ class Example
16
+ include Contracts::Core
17
+
18
+ Contract C::Any => String
19
+ def self.stringify(x)
20
+ x.inspect
21
+ end
22
+ end
23
+
24
+ puts Example.stringify(25)
25
+ puts Example.stringify(37.59)
26
+ puts Example.stringify("foo")
27
+ puts Example.stringify(:foo)
28
+ puts Example.stringify(nil)
29
+ puts Example.stringify(Object)
30
+ """
31
+ When I run `ruby any_usage.rb`
32
+ Then output should contain:
33
+ """
34
+ 25
35
+ 37.59
36
+ "foo"
37
+ :foo
38
+ nil
39
+ Object
40
+ """
41
+ And output should not contain:
42
+ """
43
+ Contract violation for
44
+ """
@@ -0,0 +1 @@
1
+ Feature: Args (TODO)
@@ -0,0 +1 @@
1
+ Feature: ArrayOf (TODO)
@@ -0,0 +1,64 @@
1
+ Feature: Bool
2
+
3
+ Checks that the argument is a `true` or `false`.
4
+
5
+ ```ruby
6
+ Contract String => C::Bool
7
+ ```
8
+
9
+ Background:
10
+ Given a file named "bool_usage.rb" with:
11
+ """ruby
12
+ require "contracts"
13
+ C = Contracts
14
+
15
+ class Example
16
+ include Contracts::Core
17
+
18
+ Contract String => C::Bool
19
+ def self.strong?(password)
20
+ return if password == ""
21
+ password.length > 22
22
+ end
23
+ end
24
+ """
25
+
26
+ Scenario: Accepts `true`
27
+ Given a file named "true.rb" with:
28
+ """ruby
29
+ require "./bool_usage"
30
+ puts Example.strong?("verystrongandLon774gPassword!ForYouHere")
31
+ """
32
+ When I run `ruby true.rb`
33
+ Then output should contain:
34
+ """
35
+ true
36
+ """
37
+
38
+ Scenario: Accepts `false`
39
+ Given a file named "false.rb" with:
40
+ """ruby
41
+ require "./bool_usage"
42
+ puts Example.strong?("welcome")
43
+ """
44
+ When I run `ruby false.rb`
45
+ Then output should contain:
46
+ """
47
+ false
48
+ """
49
+
50
+ Scenario: Rejects everything else
51
+ Given a file named "nil.rb" with:
52
+ """ruby
53
+ require "./bool_usage"
54
+ puts Example.strong?("")
55
+ """
56
+ When I run `ruby nil.rb`
57
+ Then output should contain:
58
+ """
59
+ : Contract violation for return value: (ReturnContractError)
60
+ Expected: Bool,
61
+ Actual: nil
62
+ Value guarded in: Example::strong?
63
+ With Contract: String => Bool
64
+ """
@@ -0,0 +1 @@
1
+ Feature: Enum (TODO)
@@ -0,0 +1 @@
1
+ Feature: Eq (TODO)
@@ -0,0 +1 @@
1
+ Feature: Exactly (TODO)
@@ -0,0 +1 @@
1
+ Feature: Func (TODO)
@@ -0,0 +1 @@
1
+ Feature: HashOf (TODO)
@@ -0,0 +1 @@
1
+ Feature: KeywordArgs (TODO)
@@ -0,0 +1 @@
1
+ Feature: Maybe (TODO)
@@ -0,0 +1,115 @@
1
+ Feature: Nat
2
+
3
+ Checks that an argument is a natural number.
4
+
5
+ ```ruby
6
+ Contract C::Nat => C::Nat
7
+ ```
8
+
9
+ Background:
10
+ Given a file named "nat_usage.rb" with:
11
+ """ruby
12
+ require "contracts"
13
+ C = Contracts
14
+
15
+ class Natural
16
+ include Contracts::Core
17
+
18
+ Contract C::Nat => C::Nat
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_usage"
29
+ puts Natural.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 "./nat_usage"
41
+ puts Natural.new.prev(1)
42
+ """
43
+ When I run `ruby accepts_zero.rb`
44
+ Then output should contain:
45
+ """
46
+ 0
47
+ """
48
+
49
+ Scenario: Rejects negative integers
50
+ Given a file named "rejects_negative_integers.rb" with:
51
+ """ruby
52
+ require "./nat_usage"
53
+ puts Natural.new.prev(-1)
54
+ """
55
+ When I run `ruby rejects_negative_integers.rb`
56
+ Then output should contain:
57
+ """
58
+ : Contract violation for argument 1 of 1: (ParamContractError)
59
+ Expected: Nat,
60
+ Actual: -1
61
+ Value guarded in: Natural::prev
62
+ With Contract: Nat => Nat
63
+ """
64
+ And output should contain "nat_usage.rb:8"
65
+
66
+ Scenario: Rejects negative integers as a return value
67
+ Given a file named "rejects_negative_integers.rb" with:
68
+ """ruby
69
+ require "./nat_usage"
70
+ puts Natural.new.prev(0)
71
+ """
72
+ When I run `ruby rejects_negative_integers.rb`
73
+ Then output should contain:
74
+ """
75
+ : Contract violation for return value: (ReturnContractError)
76
+ Expected: Nat,
77
+ Actual: -1
78
+ Value guarded in: Natural::prev
79
+ With Contract: Nat => Nat
80
+ """
81
+ And output should contain "nat_usage.rb:8"
82
+
83
+ Scenario: Rejects floats
84
+ Given a file named "rejects_floats.rb" with:
85
+ """ruby
86
+ require "./nat_usage"
87
+ puts Natural.new.prev(3.43)
88
+ """
89
+ When I run `ruby rejects_floats.rb`
90
+ Then output should contain:
91
+ """
92
+ : Contract violation for argument 1 of 1: (ParamContractError)
93
+ Expected: Nat,
94
+ Actual: 3.43
95
+ Value guarded in: Natural::prev
96
+ With Contract: Nat => Nat
97
+ """
98
+ And output should contain "nat_usage.rb:8"
99
+
100
+ Scenario: Rejects other values
101
+ Given a file named "rejects_others.rb" with:
102
+ """ruby
103
+ require "./nat_usage"
104
+ puts Natural.new.prev("foo")
105
+ """
106
+ When I run `ruby rejects_others.rb`
107
+ Then output should contain:
108
+ """
109
+ : Contract violation for argument 1 of 1: (ParamContractError)
110
+ Expected: Nat,
111
+ Actual: "foo"
112
+ Value guarded in: Natural::prev
113
+ With Contract: Nat => Nat
114
+ """
115
+ And output should contain "nat_usage.rb:8"
@@ -0,0 +1,115 @@
1
+ Feature: Neg
2
+
3
+ Checks that an argument is negative `Numeric`.
4
+
5
+ ```ruby
6
+ Contract C::Neg => C::Neg
7
+ ```
8
+
9
+ Background:
10
+ Given a file named "pos_usage.rb" with:
11
+ """ruby
12
+ require "contracts"
13
+ C = Contracts
14
+
15
+ class Example
16
+ include Contracts::Core
17
+
18
+ Contract C::Neg => C::Neg
19
+ def double_expense(amount)
20
+ amount * 2
21
+ end
22
+ end
23
+ """
24
+
25
+ Scenario: Accepts negative integers
26
+ Given a file named "accepts_negative_integers.rb" with:
27
+ """ruby
28
+ require "./pos_usage"
29
+ puts Example.new.double_expense(-50)
30
+ """
31
+ When I run `ruby accepts_negative_integers.rb`
32
+ Then output should contain:
33
+ """
34
+ -100
35
+ """
36
+
37
+ Scenario: Accepts negative floats
38
+ Given a file named "accepts_negative_floats.rb" with:
39
+ """ruby
40
+ require "./pos_usage"
41
+ puts Example.new.double_expense(-37.99)
42
+ """
43
+ When I run `ruby accepts_negative_floats.rb`
44
+ Then output should contain:
45
+ """
46
+ -75.98
47
+ """
48
+
49
+ Scenario: Rejects positive integers
50
+ Given a file named "rejects_positive_integers.rb" with:
51
+ """ruby
52
+ require "./pos_usage"
53
+ puts Example.new.double_expense(50)
54
+ """
55
+ When I run `ruby rejects_positive_integers.rb`
56
+ Then output should contain:
57
+ """
58
+ : Contract violation for argument 1 of 1: (ParamContractError)
59
+ Expected: Neg,
60
+ Actual: 50
61
+ Value guarded in: Example::double_expense
62
+ With Contract: Neg => Neg
63
+ """
64
+ And output should contain "pos_usage.rb:8"
65
+
66
+ Scenario: Rejects positive floats
67
+ Given a file named "rejects_positive_floats.rb" with:
68
+ """ruby
69
+ require "./pos_usage"
70
+ puts Example.new.double_expense(42.50)
71
+ """
72
+ When I run `ruby rejects_positive_floats.rb`
73
+ Then output should contain:
74
+ """
75
+ : Contract violation for argument 1 of 1: (ParamContractError)
76
+ Expected: Neg,
77
+ Actual: 42.5
78
+ Value guarded in: Example::double_expense
79
+ With Contract: Neg => Neg
80
+ """
81
+ And output should contain "pos_usage.rb:8"
82
+
83
+ Scenario: Rejects zero
84
+ Given a file named "rejects_zero.rb" with:
85
+ """ruby
86
+ require "./pos_usage"
87
+ puts Example.new.double_expense(0)
88
+ """
89
+ When I run `ruby rejects_zero.rb`
90
+ Then output should contain:
91
+ """
92
+ : Contract violation for argument 1 of 1: (ParamContractError)
93
+ Expected: Neg,
94
+ Actual: 0
95
+ Value guarded in: Example::double_expense
96
+ With Contract: Neg => Neg
97
+ """
98
+ And output should contain "pos_usage.rb:8"
99
+
100
+ Scenario: Rejects other values
101
+ Given a file named "rejects_others.rb" with:
102
+ """ruby
103
+ require "./pos_usage"
104
+ puts Example.new.double_expense("foo")
105
+ """
106
+ When I run `ruby rejects_others.rb`
107
+ Then output should contain:
108
+ """
109
+ : Contract violation for argument 1 of 1: (ParamContractError)
110
+ Expected: Neg,
111
+ Actual: "foo"
112
+ Value guarded in: Example::double_expense
113
+ With Contract: Neg => Neg
114
+ """
115
+ And output should contain "pos_usage.rb:8"