contracts 0.13.0 → 0.17
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 +5 -5
- data/.github/workflows/code_style_checks.yaml +36 -0
- data/.github/workflows/tests.yaml +41 -0
- data/CHANGELOG.markdown +54 -7
- data/Gemfile +9 -5
- data/LICENSE +23 -0
- data/README.md +14 -6
- data/Rakefile +5 -6
- data/TUTORIAL.md +28 -1
- data/contracts.gemspec +9 -1
- data/dependabot.yml +20 -0
- data/features/basics/pretty-print.feature +241 -0
- data/features/support/env.rb +2 -0
- data/lib/contracts.rb +64 -19
- data/lib/contracts/attrs.rb +26 -0
- data/lib/contracts/builtin_contracts.rb +85 -7
- data/lib/contracts/call_with.rb +50 -28
- data/lib/contracts/core.rb +3 -3
- data/lib/contracts/decorators.rb +6 -2
- data/lib/contracts/engine.rb +2 -0
- data/lib/contracts/engine/base.rb +4 -3
- data/lib/contracts/engine/eigenclass.rb +3 -2
- data/lib/contracts/engine/target.rb +2 -0
- data/lib/contracts/errors.rb +3 -0
- data/lib/contracts/formatters.rb +17 -11
- data/lib/contracts/invariants.rb +8 -4
- data/lib/contracts/method_handler.rb +30 -28
- data/lib/contracts/method_reference.rb +4 -2
- data/lib/contracts/support.rb +14 -10
- data/lib/contracts/validators.rb +6 -2
- data/lib/contracts/version.rb +3 -1
- data/spec/attrs_spec.rb +119 -0
- data/spec/builtin_contracts_spec.rb +155 -97
- data/spec/contracts_spec.rb +54 -12
- data/spec/fixtures/fixtures.rb +49 -2
- data/spec/methods_spec.rb +54 -0
- data/spec/override_validators_spec.rb +3 -3
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +17 -2
- data/spec/ruby_version_specific/contracts_spec_2.1.rb +1 -1
- data/spec/validators_spec.rb +1 -1
- metadata +22 -10
- data/script/cucumber +0 -5
data/spec/contracts_spec.rb
CHANGED
@@ -7,7 +7,7 @@ RSpec.describe "Contracts:" do
|
|
7
7
|
it "should fail for insufficient arguments" do
|
8
8
|
expect do
|
9
9
|
@o.hello
|
10
|
-
end.to raise_error
|
10
|
+
end.to raise_error ArgumentError
|
11
11
|
end
|
12
12
|
|
13
13
|
it "should fail for insufficient contracts" do
|
@@ -32,7 +32,7 @@ RSpec.describe "Contracts:" do
|
|
32
32
|
1
|
33
33
|
end
|
34
34
|
end
|
35
|
-
end.to raise_error
|
35
|
+
end.to raise_error NameError
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -349,19 +349,19 @@ RSpec.describe "Contracts:" do
|
|
349
349
|
|
350
350
|
describe "Hashes" do
|
351
351
|
it "should pass for exact correct input" do
|
352
|
-
expect { @o.person(:name => "calvin", :age => 10) }.to_not raise_error
|
352
|
+
expect { @o.person({ :name => "calvin", :age => 10 }) }.to_not raise_error
|
353
353
|
end
|
354
354
|
|
355
355
|
it "should pass even if some keys don't have contracts" do
|
356
|
-
expect { @o.person(:name => "calvin", :age => 10, :foo => "bar") }.to_not raise_error
|
356
|
+
expect { @o.person({ :name => "calvin", :age => 10, :foo => "bar" }) }.to_not raise_error
|
357
357
|
end
|
358
358
|
|
359
359
|
it "should fail if a key with a contract on it isn't provided" do
|
360
|
-
expect { @o.person(:name => "calvin") }.to raise_error(ContractError)
|
360
|
+
expect { @o.person({ :name => "calvin" }) }.to raise_error(ContractError)
|
361
361
|
end
|
362
362
|
|
363
363
|
it "should fail for incorrect input" do
|
364
|
-
expect { @o.person(:name => 50, :age => 10) }.to raise_error(ContractError)
|
364
|
+
expect { @o.person({ :name => 50, :age => 10 }) }.to raise_error(ContractError)
|
365
365
|
end
|
366
366
|
end
|
367
367
|
|
@@ -612,16 +612,19 @@ RSpec.describe "Contracts:" do
|
|
612
612
|
|
613
613
|
it "should contain to_s representation within a Hash contract" do
|
614
614
|
expect do
|
615
|
-
@o.hash_complex_contracts(:rigged => "bad")
|
615
|
+
@o.hash_complex_contracts({ :rigged => "bad" })
|
616
616
|
end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass"))
|
617
617
|
end
|
618
618
|
|
619
619
|
it "should contain to_s representation within a nested Hash contract" do
|
620
620
|
expect do
|
621
|
-
@o.nested_hash_complex_contracts(
|
622
|
-
|
623
|
-
|
624
|
-
|
621
|
+
@o.nested_hash_complex_contracts({
|
622
|
+
:rigged => true,
|
623
|
+
:contents => {
|
624
|
+
:kind => 0,
|
625
|
+
:total => 42,
|
626
|
+
},
|
627
|
+
})
|
625
628
|
end.to raise_error(ContractError, not_s(delim "String or Symbol"))
|
626
629
|
end
|
627
630
|
|
@@ -637,6 +640,28 @@ RSpec.describe "Contracts:" do
|
|
637
640
|
end.to raise_error(ContractError, not_s(delim "String or Symbol"))
|
638
641
|
end
|
639
642
|
|
643
|
+
it "should wrap and pretty print for long param contracts" do
|
644
|
+
expect do
|
645
|
+
@o.long_array_param_contracts(true)
|
646
|
+
end.to(
|
647
|
+
raise_error(
|
648
|
+
ParamContractError,
|
649
|
+
/\[\(String or Symbol\),\n \(String or Symbol\),/
|
650
|
+
)
|
651
|
+
)
|
652
|
+
end
|
653
|
+
|
654
|
+
it "should wrap and pretty print for long return contracts" do
|
655
|
+
expect do
|
656
|
+
@o.long_array_return_contracts
|
657
|
+
end.to(
|
658
|
+
raise_error(
|
659
|
+
ReturnContractError,
|
660
|
+
/\[\(String or Symbol\),\n \(String or Symbol\),/
|
661
|
+
)
|
662
|
+
)
|
663
|
+
end
|
664
|
+
|
640
665
|
it "should not contain Contracts:: module prefix" do
|
641
666
|
expect do
|
642
667
|
@o.double("bad")
|
@@ -696,7 +721,7 @@ RSpec.describe "Contracts:" do
|
|
696
721
|
it "should apply the contract to an inherited method" do
|
697
722
|
c = Child.new
|
698
723
|
expect { c.double(2) }.to_not raise_error
|
699
|
-
expect { c.double("asd") }.to raise_error
|
724
|
+
expect { c.double("asd") }.to raise_error ParamContractError
|
700
725
|
end
|
701
726
|
end
|
702
727
|
|
@@ -727,5 +752,22 @@ RSpec.describe "Contracts:" do
|
|
727
752
|
it "works correctly with methods with passing contracts" do
|
728
753
|
expect { klass.new.foo(42) }.to raise_error(ContractError, /Expected: String/)
|
729
754
|
end
|
755
|
+
|
756
|
+
# See the discussion on this issue:
|
757
|
+
# https://github.com/egonSchiele/contracts.ruby/issues/229
|
758
|
+
it "should not fail with 'undefined method 'Contract''" do
|
759
|
+
expect do
|
760
|
+
class ModuleThenContracts
|
761
|
+
include ModuleWithContracts
|
762
|
+
include Contracts::Core
|
763
|
+
|
764
|
+
# fails on this line
|
765
|
+
Contract C::Num => C::Num
|
766
|
+
def double(x)
|
767
|
+
x * 2
|
768
|
+
end
|
769
|
+
end
|
770
|
+
end.to_not raise_error
|
771
|
+
end
|
730
772
|
end
|
731
773
|
end
|
data/spec/fixtures/fixtures.rb
CHANGED
@@ -104,6 +104,10 @@ class GenericExample
|
|
104
104
|
def person(data)
|
105
105
|
end
|
106
106
|
|
107
|
+
Contract C::StrictHash[{ :name => String, :age => Fixnum }] => nil
|
108
|
+
def strict_person(data)
|
109
|
+
end
|
110
|
+
|
107
111
|
Contract ({ :rigged => C::Or[TrueClass, FalseClass] }) => nil
|
108
112
|
def hash_complex_contracts(data)
|
109
113
|
end
|
@@ -116,11 +120,11 @@ class GenericExample
|
|
116
120
|
end
|
117
121
|
|
118
122
|
Contract C::KeywordArgs[:name => String, :age => Fixnum] => nil
|
119
|
-
def person_keywordargs(
|
123
|
+
def person_keywordargs(name: "name", age: 10)
|
120
124
|
end
|
121
125
|
|
122
126
|
Contract C::KeywordArgs[:hash => C::HashOf[Symbol, C::Num]] => nil
|
123
|
-
def hash_keywordargs(
|
127
|
+
def hash_keywordargs(hash:)
|
124
128
|
end
|
125
129
|
|
126
130
|
Contract (/foo/) => nil
|
@@ -143,6 +147,30 @@ class GenericExample
|
|
143
147
|
def nested_array_complex_contracts(data)
|
144
148
|
end
|
145
149
|
|
150
|
+
Contract [
|
151
|
+
C::Or[String, Symbol],
|
152
|
+
C::Or[String, Symbol],
|
153
|
+
C::Or[String, Symbol],
|
154
|
+
C::Or[String, Symbol],
|
155
|
+
C::Or[String, Symbol],
|
156
|
+
C::Or[String, Symbol],
|
157
|
+
C::Or[String, Symbol]
|
158
|
+
] => nil
|
159
|
+
def long_array_param_contracts(data)
|
160
|
+
end
|
161
|
+
|
162
|
+
Contract C::None => [
|
163
|
+
C::Or[String, Symbol],
|
164
|
+
C::Or[String, Symbol],
|
165
|
+
C::Or[String, Symbol],
|
166
|
+
C::Or[String, Symbol],
|
167
|
+
C::Or[String, Symbol],
|
168
|
+
C::Or[String, Symbol],
|
169
|
+
C::Or[String, Symbol]
|
170
|
+
]
|
171
|
+
def long_array_return_contracts
|
172
|
+
end
|
173
|
+
|
146
174
|
Contract Proc => C::Any
|
147
175
|
def do_call(&block)
|
148
176
|
block.call
|
@@ -263,6 +291,10 @@ class GenericExample
|
|
263
291
|
r.first
|
264
292
|
end
|
265
293
|
|
294
|
+
Contract C::DescendantOf[Enumerable] => nil
|
295
|
+
def enumerable_descendant_test(enum)
|
296
|
+
end
|
297
|
+
|
266
298
|
Contract C::Bool => nil
|
267
299
|
def bool_test(x)
|
268
300
|
end
|
@@ -676,3 +708,18 @@ module ModuleContractExample
|
|
676
708
|
:world
|
677
709
|
end
|
678
710
|
end
|
711
|
+
|
712
|
+
module ModuleWithContracts
|
713
|
+
def self.included(base)
|
714
|
+
base.extend ClassMethods
|
715
|
+
end
|
716
|
+
|
717
|
+
module ClassMethods
|
718
|
+
include Contracts::Core
|
719
|
+
|
720
|
+
Contract C::None => String
|
721
|
+
def foo
|
722
|
+
"bar"
|
723
|
+
end
|
724
|
+
end
|
725
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
RSpec.describe "Contracts:" do
|
2
|
+
describe "method called with blocks" do
|
3
|
+
module FuncTest
|
4
|
+
include Contracts::Core
|
5
|
+
include Contracts::Builtin
|
6
|
+
|
7
|
+
Contract Func[Num=>Num] => nil
|
8
|
+
def foo(&blk)
|
9
|
+
_ = blk.call(2)
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
Contract Num, Func[Num=>Num] => nil
|
14
|
+
def foo2(a, &blk)
|
15
|
+
_ = blk.call(2)
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
Contract Func[Num=>Num] => nil
|
20
|
+
def bar(blk)
|
21
|
+
_ = blk.call(2)
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
Contract Num, Func[Num=>Num] => nil
|
26
|
+
def bar2(a, blk)
|
27
|
+
_ = blk.call(2)
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def obj
|
33
|
+
Object.new.tap do |o|
|
34
|
+
o.extend(FuncTest)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should enforce return value inside block with no other parameter" do
|
39
|
+
expect { obj.foo(&:to_s) }.to raise_error ReturnContractError
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should enforce return value inside block with other parameter" do
|
43
|
+
expect { obj.foo2(2) { |x| x.to_s } }.to raise_error ReturnContractError
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should enforce return value inside lambda with no other parameter" do
|
47
|
+
expect { obj.bar lambda { |x| x.to_s } }.to raise_error ReturnContractError
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should enforce return value inside lambda with other parameter" do
|
51
|
+
expect { obj.bar2(2, lambda { |x| x.to_s }) }.to raise_error ReturnContractError
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -30,15 +30,15 @@ RSpec.describe Contract do
|
|
30
30
|
obj = klass.new
|
31
31
|
|
32
32
|
expect do
|
33
|
-
obj.something(:a => 35, :b => "hello")
|
33
|
+
obj.something({ :a => 35, :b => "hello" })
|
34
34
|
end.to raise_error(ContractError)
|
35
35
|
|
36
36
|
expect do
|
37
|
-
obj.something(
|
37
|
+
obj.something({
|
38
38
|
:a => 35,
|
39
39
|
:b => "hello",
|
40
40
|
:it_is_a_hash => true
|
41
|
-
)
|
41
|
+
})
|
42
42
|
end.not_to raise_error
|
43
43
|
end
|
44
44
|
|
@@ -1,12 +1,17 @@
|
|
1
1
|
class GenericExample
|
2
|
-
Contract C::Args[String],
|
2
|
+
Contract C::Args[String], C::KeywordArgs[ repeat: C::Maybe[C::Num] ] => C::ArrayOf[String]
|
3
3
|
def splat_then_optional_named(*vals, repeat: 2)
|
4
4
|
vals.map { |v| v * repeat }
|
5
5
|
end
|
6
6
|
|
7
|
-
Contract
|
7
|
+
Contract C::KeywordArgs[ foo: C::Nat ] => nil
|
8
8
|
def nat_test_with_kwarg(foo: 10)
|
9
9
|
end
|
10
|
+
|
11
|
+
Contract C::KeywordArgs[name: C::Optional[String]], C::Func[String => String] => String
|
12
|
+
def keyword_args_hello(name: "Adit", &block)
|
13
|
+
"Hey, #{yield name}!"
|
14
|
+
end
|
10
15
|
end
|
11
16
|
|
12
17
|
RSpec.describe "Contracts:" do
|
@@ -37,4 +42,14 @@ RSpec.describe "Contracts:" do
|
|
37
42
|
expect { @o.nat_test_with_kwarg }.to raise_error(ContractError)
|
38
43
|
end
|
39
44
|
end
|
45
|
+
|
46
|
+
describe "keyword args with defaults, with a block" do
|
47
|
+
it "should work when both keyword args and a block is given" do
|
48
|
+
expect(@o.keyword_args_hello(name: "maggie", &:upcase)).to eq("Hey, MAGGIE!")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should work even when keyword args aren't given" do
|
52
|
+
expect(@o.keyword_args_hello(&:upcase)).to eq("Hey, ADIT!")
|
53
|
+
end
|
54
|
+
end
|
40
55
|
end
|
@@ -51,7 +51,7 @@ RSpec.describe "Contracts:" do
|
|
51
51
|
end.to raise_error(ContractError)
|
52
52
|
end
|
53
53
|
|
54
|
-
it "should fail when passed nil to an optional argument which contract
|
54
|
+
it "should fail when passed nil to an optional argument which contract shouldn't accept nil" do
|
55
55
|
expect do
|
56
56
|
@o.complicated("a", true, :b, :c, 2.0, e: (1..5), f: nil, g: :d) do |x|
|
57
57
|
x
|
data/spec/validators_spec.rb
CHANGED
@@ -34,7 +34,7 @@ RSpec.describe "Contract validators" do
|
|
34
34
|
|
35
35
|
describe "within a hash" do
|
36
36
|
it "should pass for a matching string" do
|
37
|
-
expect { o.hash_containing_foo(:host => "foo.example.org") }.to_not raise_error
|
37
|
+
expect { o.hash_containing_foo({ :host => "foo.example.org" }) }.to_not raise_error
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
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.17'
|
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: 2021-05-07 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,12 +18,15 @@ executables: []
|
|
18
18
|
extensions: []
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
|
+
- ".github/workflows/code_style_checks.yaml"
|
22
|
+
- ".github/workflows/tests.yaml"
|
21
23
|
- ".gitignore"
|
22
24
|
- ".rspec"
|
23
25
|
- ".rubocop.yml"
|
24
|
-
- ".
|
26
|
+
- ".rubocop_todo.yml"
|
25
27
|
- CHANGELOG.markdown
|
26
28
|
- Gemfile
|
29
|
+
- LICENSE
|
27
30
|
- README.md
|
28
31
|
- Rakefile
|
29
32
|
- TODO.markdown
|
@@ -35,8 +38,10 @@ files:
|
|
35
38
|
- benchmarks/wrap_test.rb
|
36
39
|
- contracts.gemspec
|
37
40
|
- cucumber.yml
|
41
|
+
- dependabot.yml
|
38
42
|
- features/README.md
|
39
43
|
- features/basics/functype.feature
|
44
|
+
- features/basics/pretty-print.feature
|
40
45
|
- features/basics/simple_example.feature
|
41
46
|
- features/builtin_contracts/README.md
|
42
47
|
- features/builtin_contracts/and.feature
|
@@ -67,6 +72,7 @@ files:
|
|
67
72
|
- features/builtin_contracts/xor.feature
|
68
73
|
- features/support/env.rb
|
69
74
|
- lib/contracts.rb
|
75
|
+
- lib/contracts/attrs.rb
|
70
76
|
- lib/contracts/builtin_contracts.rb
|
71
77
|
- lib/contracts/call_with.rb
|
72
78
|
- lib/contracts/core.rb
|
@@ -83,14 +89,15 @@ files:
|
|
83
89
|
- lib/contracts/support.rb
|
84
90
|
- lib/contracts/validators.rb
|
85
91
|
- lib/contracts/version.rb
|
86
|
-
- script/cucumber
|
87
92
|
- script/docs-release
|
88
93
|
- script/docs-staging
|
89
94
|
- script/rubocop.rb
|
95
|
+
- spec/attrs_spec.rb
|
90
96
|
- spec/builtin_contracts_spec.rb
|
91
97
|
- spec/contracts_spec.rb
|
92
98
|
- spec/fixtures/fixtures.rb
|
93
99
|
- spec/invariants_spec.rb
|
100
|
+
- spec/methods_spec.rb
|
94
101
|
- spec/module_spec.rb
|
95
102
|
- spec/override_validators_spec.rb
|
96
103
|
- spec/ruby_version_specific/contracts_spec_1.9.rb
|
@@ -100,10 +107,13 @@ files:
|
|
100
107
|
- spec/support.rb
|
101
108
|
- spec/support_spec.rb
|
102
109
|
- spec/validators_spec.rb
|
103
|
-
homepage:
|
104
|
-
licenses:
|
110
|
+
homepage: https://github.com/egonSchiele/contracts.ruby
|
111
|
+
licenses:
|
112
|
+
- BSD-2-Clause
|
105
113
|
metadata: {}
|
106
|
-
post_install_message:
|
114
|
+
post_install_message: "\n 0.16.x will be the supporting Ruby 2.x and be feature
|
115
|
+
frozen (only fixes will be released)\n For Ruby 3.x use 0.17.x or later (might
|
116
|
+
not be released yet)\n "
|
107
117
|
rdoc_options: []
|
108
118
|
require_paths:
|
109
119
|
- lib
|
@@ -111,15 +121,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
121
|
requirements:
|
112
122
|
- - ">="
|
113
123
|
- !ruby/object:Gem::Version
|
114
|
-
version: '0'
|
124
|
+
version: '3.0'
|
125
|
+
- - "<"
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '4'
|
115
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
129
|
requirements:
|
117
130
|
- - ">="
|
118
131
|
- !ruby/object:Gem::Version
|
119
132
|
version: '0'
|
120
133
|
requirements: []
|
121
|
-
|
122
|
-
rubygems_version: 2.4.6
|
134
|
+
rubygems_version: 3.0.3
|
123
135
|
signing_key:
|
124
136
|
specification_version: 4
|
125
137
|
summary: Contracts for Ruby.
|