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.
Files changed (42) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/code_style_checks.yaml +36 -0
  3. data/.github/workflows/tests.yaml +41 -0
  4. data/CHANGELOG.markdown +54 -7
  5. data/Gemfile +9 -5
  6. data/LICENSE +23 -0
  7. data/README.md +14 -6
  8. data/Rakefile +5 -6
  9. data/TUTORIAL.md +28 -1
  10. data/contracts.gemspec +9 -1
  11. data/dependabot.yml +20 -0
  12. data/features/basics/pretty-print.feature +241 -0
  13. data/features/support/env.rb +2 -0
  14. data/lib/contracts.rb +64 -19
  15. data/lib/contracts/attrs.rb +26 -0
  16. data/lib/contracts/builtin_contracts.rb +85 -7
  17. data/lib/contracts/call_with.rb +50 -28
  18. data/lib/contracts/core.rb +3 -3
  19. data/lib/contracts/decorators.rb +6 -2
  20. data/lib/contracts/engine.rb +2 -0
  21. data/lib/contracts/engine/base.rb +4 -3
  22. data/lib/contracts/engine/eigenclass.rb +3 -2
  23. data/lib/contracts/engine/target.rb +2 -0
  24. data/lib/contracts/errors.rb +3 -0
  25. data/lib/contracts/formatters.rb +17 -11
  26. data/lib/contracts/invariants.rb +8 -4
  27. data/lib/contracts/method_handler.rb +30 -28
  28. data/lib/contracts/method_reference.rb +4 -2
  29. data/lib/contracts/support.rb +14 -10
  30. data/lib/contracts/validators.rb +6 -2
  31. data/lib/contracts/version.rb +3 -1
  32. data/spec/attrs_spec.rb +119 -0
  33. data/spec/builtin_contracts_spec.rb +155 -97
  34. data/spec/contracts_spec.rb +54 -12
  35. data/spec/fixtures/fixtures.rb +49 -2
  36. data/spec/methods_spec.rb +54 -0
  37. data/spec/override_validators_spec.rb +3 -3
  38. data/spec/ruby_version_specific/contracts_spec_2.0.rb +17 -2
  39. data/spec/ruby_version_specific/contracts_spec_2.1.rb +1 -1
  40. data/spec/validators_spec.rb +1 -1
  41. metadata +22 -10
  42. data/script/cucumber +0 -5
@@ -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(:rigged => true,
622
- :contents => {
623
- :kind => 0,
624
- :total => 42 })
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
@@ -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(data)
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(data)
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], { repeat: C::Maybe[C::Num] } => C::ArrayOf[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 ({foo: C::Nat}) => nil
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 shouldnt accept nil" do
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
@@ -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.13.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: 2016-01-25 00:00:00.000000000 Z
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
- - ".travis.yml"
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: http://github.com/egonSchiele/contracts.ruby
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
- rubyforge_project:
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.
data/script/cucumber DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- if ruby -e 'exit(1) unless RUBY_VERSION.to_f >= 2.0'; then
4
- bundle exec cucumber
5
- fi