contracts 0.17 → 0.17.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbf4bff58a593998c5902920fff3c0a808ca5eb97ec9cad3646df370435421c7
4
- data.tar.gz: 02e5cffce164a25ae463b22e9f8b85c810d50c9c2875d09a3e83751afebac012
3
+ metadata.gz: 6d5a8e7593b70e99ae8c0f56e6bf4484b884ecfbb740a418f27a78321ca856c9
4
+ data.tar.gz: c62fb8724ebfe3aea0dc2ff4ae8f9af119192d84d57244076ae83d60a5d26c03
5
5
  SHA512:
6
- metadata.gz: c6911f4383b0140c40bc08c838a3044897c3c9ac3c577fae4b3806879cbfed12f044f8ee1336305b1599c10af1bfb5aabe9ae2e8f1f9ffd94774d4de789e8402
7
- data.tar.gz: 25c8ecfe04e846ba55d87b33a43e0a38422440d0a963066ac67217edd8ebc396cbb2309b50704ea6f4dc4609bacbfb3fb3b501ef0a40210fe1877df5c6fbb7f8
6
+ metadata.gz: ea0815ee43b56200ab8465c112f5144989a0ac12c1a9e463ec7bc61ab14b1eeae5ef226ffed7961770b59d57fcfac3527445620cafaa55675337d289beedac4b
7
+ data.tar.gz: 1895e5c8786d024696e05be452c749f1f830e538b348d30fc9deb165f92a3f87697659b5fef784a65e4eda5f4306ed3c38a7d8622ef72fd76bd5cd38e41f3b1f
@@ -6,11 +6,13 @@ on:
6
6
  - master
7
7
  paths-ignore:
8
8
  - 'README.md'
9
+ - 'CHANGELOG.markdown'
9
10
  push:
10
11
  branches:
11
12
  - master
12
13
  paths-ignore:
13
14
  - 'README.md'
15
+ - 'CHANGELOG.markdown'
14
16
 
15
17
  jobs:
16
18
  rubocop:
@@ -22,11 +24,11 @@ jobs:
22
24
  os:
23
25
  - ubuntu
24
26
  ruby:
25
- - "3.0"
27
+ - "3.3"
26
28
  runs-on: ${{ matrix.os }}-latest
27
29
  steps:
28
30
  - name: Checkout
29
- uses: actions/checkout@v2
31
+ uses: actions/checkout@v4
30
32
  - name: Setup Ruby
31
33
  uses: ruby/setup-ruby@v1
32
34
  with:
@@ -6,11 +6,13 @@ on:
6
6
  - master
7
7
  paths-ignore:
8
8
  - 'README.md'
9
+ - 'CHANGELOG.markdown'
9
10
  push:
10
11
  branches:
11
12
  - master
12
13
  paths-ignore:
13
14
  - 'README.md'
15
+ - 'CHANGELOG.markdown'
14
16
 
15
17
  jobs:
16
18
  unit_tests:
@@ -22,16 +24,16 @@ jobs:
22
24
  os:
23
25
  - ubuntu
24
26
  ruby:
27
+ - "3.3"
28
+ - "3.2"
29
+ - "3.1"
25
30
  - "3.0"
26
- test_command: ["bundle exec rspec && bundle exec cucumber"]
27
- include:
28
- - os: ubuntu
29
- ruby: "3.0"
30
- test_command: "bundle exec rspec"
31
+ test_command:
32
+ - "bundle exec rspec && bundle exec cucumber"
31
33
  runs-on: ${{ matrix.os }}-latest
32
34
  steps:
33
35
  - name: Checkout
34
- uses: actions/checkout@v2
36
+ uses: actions/checkout@v4
35
37
  - name: Setup Ruby
36
38
  uses: ruby/setup-ruby@v1
37
39
  with:
data/.rubocop.yml CHANGED
@@ -64,7 +64,7 @@ Style/Documentation:
64
64
  Layout/LineLength:
65
65
  Enabled: false
66
66
 
67
- # triggered by Contract ({ :name => String, :age => Fixnum }) => nil
67
+ # triggered by Contract ({ :name => String, :age => Integer }) => nil
68
68
  Lint/ParenthesesAsGroupedExpression:
69
69
  Enabled: false
70
70
 
data/CHANGELOG.markdown CHANGED
@@ -1,4 +1,18 @@
1
1
 
2
+ ## [v0.17.1] - 2024-10-06
3
+
4
+ [v0.17.1]: https://github.com/egonSchiele/contracts.ruby/compare/v0.17...v0.17.1
5
+
6
+ - Bugfix: Fix keyword arguments contract when used with optional positional arguments - [PikachuEXE](https://github.com/PikachuEXE) [#305](https://github.com/egonSchiele/contracts.ruby/pull/305)
7
+ - Enhancement: Always load version.rb, suppress legacy deprecation warning - [Vlad Pisanov](https://github.com/vlad-pisanov) [#301](https://github.com/egonSchiele/contracts.ruby/pull/306)
8
+ - Enhancement: Update doc & spec about deprecated `Fixnum` to `Integer` - [PikachuEXE](https://github.com/PikachuEXE) [#301](https://github.com/egonSchiele/contracts.ruby/pull/301)
9
+
10
+ ## [v0.17] - 2021-09-28
11
+
12
+ [v0.17]: https://github.com/egonSchiele/contracts.ruby/compare/v0.16.1...v0.17
13
+
14
+ - Update implementation & spec to be 3.0 compatible **Support for Ruby 2 has been discontinued** - [PikachuEXE](https://github.com/PikachuEXE) [#295](https://github.com/egonSchiele/contracts.ruby/pull/295)
15
+
2
16
  ## [v0.16.1] - 2021-04-17
3
17
 
4
18
  [v0.16.1]: https://github.com/egonSchiele/contracts.ruby/compare/v0.16.0...v0.16.1
@@ -20,7 +34,7 @@
20
34
  [v0.15.0]: https://github.com/egonSchiele/contracts.ruby/compare/v0.14.0...v0.15.0
21
35
 
22
36
  - Bugfix: Func contract's return value isn't enforced with blocks - [Piotr Szmielew](https://github.com/esse) [#251](https://github.com/egonSchiele/contracts.ruby/pull/251)
23
- - Bugfx: Fix contracts used in AR-models - [Gert Goet](https://github.com/eval) [#237](https://github.com/egonSchiele/contracts.ruby/pull/237)
37
+ - Bugfix: Fix contracts used in AR-models - [Gert Goet](https://github.com/eval) [#237](https://github.com/egonSchiele/contracts.ruby/pull/237)
24
38
 
25
39
  ## [v0.14.0] - 2016-04-26
26
40
 
data/README.md CHANGED
@@ -1,11 +1,19 @@
1
1
  This project is looking for a new maintainer! [More details here](https://github.com/egonSchiele/contracts.ruby/issues/249)
2
2
 
3
- # contracts.ruby [![GitHub Build Status](https://img.shields.io/github/workflow/status/egonSchiele/contracts.ruby/Tests?style=flat-square)](https://github.com/egonSchiele/contracts.ruby/actions?query=workflow%3ATests) [![Join the chat at https://gitter.im/egonSchiele/contracts.ruby](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg)](https://gitter.im/egonSchiele/contracts.ruby)
3
+
4
+
5
+ # contracts.ruby [![GitHub Build Status](https://img.shields.io/github/actions/workflow/status/egonSchiele/contracts.ruby/tests.yaml?branch=master&style=flat-square)](https://github.com/egonSchiele/contracts.ruby/actions/workflows/tests.yaml) [![Join the chat at https://gitter.im/egonSchiele/contracts.ruby](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg)](https://gitter.im/egonSchiele/contracts.ruby)
4
6
 
5
7
  Contracts let you clearly – even beautifully – express how your code behaves, and free you from writing tons of boilerplate, defensive code.
6
8
 
7
9
  You can think of contracts as `assert` on steroids.
8
10
 
11
+ ## 0.17.x = Ruby 3.x only
12
+
13
+ 0.17.x only supports Ruby 3.x
14
+ Looking for Ruby 2.x support?
15
+ Use 0.16.x
16
+
9
17
  ## Installation
10
18
 
11
19
  gem install contracts
@@ -83,7 +91,7 @@ Using contracts.ruby results in very little slowdown. Check out [this blog post]
83
91
 
84
92
  **Q.** What Rubies can I use this with?
85
93
 
86
- **A.** It's been tested with `2.1`, `2.2`, `2.3`, `2.4`, `2.5`, `2.6` and `2.7`.
94
+ **A.** It's been tested with `3.0` and `3.1`. (In case this list becomes outdated see [`.github/workflows/tests.yaml`](/.github/workflows/tests.yaml))
87
95
 
88
96
  If you're using the library, please [let me know](https://github.com/egonSchiele) what project you're using it on :)
89
97
 
data/TUTORIAL.md CHANGED
@@ -80,8 +80,8 @@ contracts.ruby comes with a lot of built-in contracts, including the following:
80
80
 
81
81
  * Logical combinations
82
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]`
83
+ * [`Or`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Or) – passes if any of the given contracts pass, e.g. `Or[Integer, Float]`
84
+ * [`Xor`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Xor) – passes if exactly one of the given contracts pass, e.g. `Xor[Integer, Float]`
85
85
  * [`And`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/And) – passes if all contracts pass, e.g. `And[Nat, -> (n) { n.even? }]`
86
86
  * [`Not`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/Not) – passes if all contracts fail for the given argument, e.g. `Not[nil]`
87
87
 
@@ -89,7 +89,7 @@ contracts.ruby comes with a lot of built-in contracts, including the following:
89
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
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
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
- * [`StrictHash`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/StrictHash) – checks that the argument is a hash, and every key passed is present in the given contract, e.g. `StrictHash[{ :description => String, :number => Fixnum }]`
92
+ * [`StrictHash`](http://www.rubydoc.info/gems/contracts/Contracts/Builtin/StrictHash) – checks that the argument is a hash, and every key passed is present in the given contract, e.g. `StrictHash[{ :description => String, :number => Integer }]`
93
93
  * [`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]`
94
94
  * [`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]`
95
95
 
@@ -152,7 +152,7 @@ end
152
152
 
153
153
  You always need to specify a contract for the return value. In this example, `hello` doesn't return anything, so the contract is `nil`. Now you know that you can use a constant like `nil` as the end of a contract. Valid values for a contract are:
154
154
 
155
- - the name of a class (like `String` or `Fixnum`)
155
+ - the name of a class (like `String` or `Integer`)
156
156
  - a constant (like `nil` or `1`)
157
157
  - a `Proc` that takes a value and returns true or false to indicate whether the contract passed or not
158
158
  - a class that responds to the `valid?` class method (more on this later)
@@ -161,32 +161,32 @@ You always need to specify a contract for the return value. In this example, `he
161
161
  ### A Double Function
162
162
 
163
163
  ```ruby
164
- Contract C::Or[Fixnum, Float] => C::Or[Fixnum, Float]
164
+ Contract C::Or[Integer, Float] => C::Or[Integer, Float]
165
165
  def double(x)
166
166
  2 * x
167
167
  end
168
168
  ```
169
169
 
170
170
  Sometimes you want to be able to choose between a few contracts. `Or` takes a variable number of contracts and checks the argument against all of them. If it passes for any of the contracts, then the `Or` contract passes.
171
- This introduces some new syntax. One of the valid values for a contract is an instance of a class that responds to the `valid?` method. This is what `Or[Fixnum, Float]` is. The longer way to write it would have been:
171
+ This introduces some new syntax. One of the valid values for a contract is an instance of a class that responds to the `valid?` method. This is what `Or[Integer, Float]` is. The longer way to write it would have been:
172
172
 
173
173
  ```ruby
174
- Contract C::Or.new(Fixnum, Float) => C::Or.new(Fixnum, Float)
174
+ Contract C::Or.new(Integer, Float) => C::Or.new(Integer, Float)
175
175
  ```
176
176
 
177
177
  All the built-in contracts have overridden the square brackets (`[]`) to give the same functionality. So you could write
178
178
 
179
179
  ```ruby
180
- Contract C::Or[Fixnum, Float] => C::Or[Fixnum, Float]
180
+ Contract C::Or[Integer, Float] => C::Or[Integer, Float]
181
181
  ```
182
182
 
183
183
  or
184
184
 
185
185
  ```ruby
186
- Contract C::Or.new(Fixnum, Float) => C::Or.new(Fixnum, Float)
186
+ Contract C::Or.new(Integer, Float) => C::Or.new(Integer, Float)
187
187
  ```
188
188
 
189
- whichever you prefer. They both mean the same thing here: make a new instance of `Or` with `Fixnum` and `Float`. Use that instance to validate the argument.
189
+ whichever you prefer. They both mean the same thing here: make a new instance of `Or` with `Integer` and `Float`. Use that instance to validate the argument.
190
190
 
191
191
  ### A Product Function
192
192
 
@@ -455,7 +455,7 @@ Now you can use `Person` wherever you would have used `Or[Hash, nil]`. Your code
455
455
 
456
456
  Contracts are very easy to define. To re-iterate, there are 5 kinds of contracts:
457
457
 
458
- - the name of a class (like `String` or `Fixnum`)
458
+ - the name of a class (like `String` or `Integer`)
459
459
  - a constant (like `nil` or `1`)
460
460
  - a `Proc` that takes a value and returns true or false to indicate whether the contract passed or not
461
461
  - a class that responds to the `valid?` class method (more on this later)
@@ -511,7 +511,7 @@ The `Or` contract takes a sequence of contracts, and passes if any of them pass.
511
511
  This class inherits from `CallableClass`, which allows us to use `[]` when using the class:
512
512
 
513
513
  ```ruby
514
- Contract C::Or[Fixnum, Float] => C::Num
514
+ Contract C::Or[Integer, Float] => C::Num
515
515
  def double(x)
516
516
  2 * x
517
517
  end
@@ -520,7 +520,7 @@ end
520
520
  Without `CallableClass`, we would have to use `.new` instead:
521
521
 
522
522
  ```ruby
523
- Contract C::Or.new(Fixnum, Float) => C::Num
523
+ Contract C::Or.new(Integer, Float) => C::Num
524
524
  def double(x)
525
525
  # etc
526
526
  ```
@@ -723,7 +723,7 @@ class MyBirthday < Struct.new(:day, :month)
723
723
  invariant(:day) { 1 <= day && day <= 31 }
724
724
  invariant(:month) { 1 <= month && month <= 12 }
725
725
 
726
- Contract C::None => Fixnum
726
+ Contract C::None => Integer
727
727
  def silly_next_day!
728
728
  self.day += 1
729
729
  end
data/contracts.gemspec CHANGED
@@ -13,8 +13,4 @@ Gem::Specification.new do |s|
13
13
  s.homepage = "https://github.com/egonSchiele/contracts.ruby"
14
14
  s.license = "BSD-2-Clause"
15
15
  s.required_ruby_version = [">= 3.0", "< 4"]
16
- s.post_install_message = "
17
- 0.16.x will be the supporting Ruby 2.x and be feature frozen (only fixes will be released)
18
- For Ruby 3.x use 0.17.x or later (might not be released yet)
19
- "
20
16
  end
@@ -0,0 +1,133 @@
1
+ Feature: Method Overloading
2
+
3
+ You can use contracts for method overloading! This is commonly called "pattern matching" in functional programming languages.
4
+
5
+ ```ruby
6
+ Contract 1 => 1
7
+ def fact x
8
+ x
9
+ end
10
+
11
+ Contract C::Num => C::Num
12
+ def fact x
13
+ x * fact(x - 1)
14
+ end
15
+ ```
16
+
17
+ Background:
18
+ Given a file named "method_overloading_with_positional_args_usage.rb" with:
19
+ """ruby
20
+ require "contracts"
21
+ C = Contracts
22
+
23
+ class Example
24
+ include Contracts::Core
25
+
26
+ Contract 1 => 1
27
+ def fact(x)
28
+ x
29
+ end
30
+
31
+ Contract C::Num => C::Num
32
+ def fact(x)
33
+ x * fact(x - 1)
34
+ end
35
+ end
36
+ """
37
+
38
+ Given a file named "method_overloading_with_keyword_args_usage.rb" with:
39
+ """ruby
40
+ require "contracts"
41
+ C = Contracts
42
+
43
+ class Example
44
+ include Contracts::Core
45
+
46
+ Contract C::KeywordArgs[age: Integer, size: Symbol] => String
47
+ def speak(age:, size:)
48
+ "age: #{age} size: #{size}"
49
+ end
50
+
51
+ Contract C::KeywordArgs[sound: String] => String
52
+ def speak(sound:)
53
+ "sound: #{sound}"
54
+ end
55
+ end
56
+ """
57
+
58
+ Scenario: Positional Args Method 1
59
+ Given a file named "positional_args_method_1.rb" with:
60
+ """ruby
61
+ require "./method_overloading_with_positional_args_usage"
62
+ puts Example.new.fact(1)
63
+ """
64
+ When I run `ruby positional_args_method_1.rb`
65
+ Then the output should contain:
66
+ """
67
+ 1
68
+ """
69
+
70
+ Scenario: Positional Args Method 2
71
+ Given a file named "positional_args_method_2.rb" with:
72
+ """ruby
73
+ require "./method_overloading_with_positional_args_usage"
74
+ puts Example.new.fact(4)
75
+ """
76
+ When I run `ruby positional_args_method_2.rb`
77
+ Then the output should contain:
78
+ """
79
+ 24
80
+ """
81
+
82
+ Scenario: Keyword Args Method 1
83
+ Given a file named "keyword_args_method_1.rb" with:
84
+ """ruby
85
+ require "./method_overloading_with_keyword_args_usage"
86
+ puts Example.new.speak(age: 5, size: :large)
87
+ """
88
+ When I run `ruby keyword_args_method_1.rb`
89
+ Then the output should contain:
90
+ """
91
+ age: 5 size: large
92
+ """
93
+
94
+ Scenario: Keyword Args Method 2
95
+ Given a file named "keyword_args_method_2.rb" with:
96
+ """ruby
97
+ require "./method_overloading_with_keyword_args_usage"
98
+ puts Example.new.speak(sound: "woof")
99
+ """
100
+ When I run `ruby keyword_args_method_2.rb`
101
+ Then the output should contain:
102
+ """
103
+ sound: woof
104
+ """
105
+
106
+ Scenario: Incorrect Positional Args Method
107
+ Given a file named "incorrect_positional_args_method.rb" with:
108
+ """ruby
109
+ require "contracts"
110
+ C = Contracts
111
+
112
+ class Example
113
+ include Contracts::Core
114
+
115
+ # Notice that this method's contract is wider than the one below
116
+ # This would cause this method to be called every time but never the one below
117
+ Contract C::Num => C::Num
118
+ def fact(x)
119
+ x * fact(x - 1)
120
+ end
121
+
122
+ Contract 1 => 1
123
+ def fact(x)
124
+ x
125
+ end
126
+ end
127
+ puts Example.new.fact(4)
128
+ """
129
+ When I run `ruby incorrect_positional_args_method.rb`
130
+ Then the output should contain:
131
+ """
132
+ stack level too deep (SystemStackError)
133
+ """
@@ -0,0 +1,76 @@
1
+ Feature: KeywordArgs when used with optional positional arguments
2
+
3
+ Checks that the argument is an options hash, and all required keyword arguments are present, and all values pass their respective contracts
4
+
5
+ ```ruby
6
+ Contract Any, KeywordArgs[:number => Num, :description => Optional[String]] => Any
7
+ ```
8
+
9
+ Background:
10
+ Given a file named "keyword_args_with_optional_positional_args_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, C::KeywordArgs[b: C::Optional[String]] => Symbol
19
+ def foo(output, a = 'a', b: 'b')
20
+ p [a, b]
21
+ output
22
+ end
23
+ end
24
+ """
25
+
26
+ Scenario: Accepts arguments when only require arguments filled and valid
27
+ Given a file named "accepts_all_filled_valid_args.rb" with:
28
+ """ruby
29
+ require "./keyword_args_with_optional_positional_args_usage"
30
+ puts Example.new.foo(:output)
31
+ """
32
+ When I run `ruby accepts_all_filled_valid_args.rb`
33
+ Then output should contain:
34
+ """
35
+ ["a", "b"]
36
+ output
37
+ """
38
+
39
+ Scenario: Accepts arguments when all filled and valid
40
+ Given a file named "accepts_all_filled_valid_args.rb" with:
41
+ """ruby
42
+ require "./keyword_args_with_optional_positional_args_usage"
43
+ puts Example.new.foo(:output, 'c', b: 'd')
44
+ """
45
+ When I run `ruby accepts_all_filled_valid_args.rb`
46
+ Then output should contain:
47
+ """
48
+ ["c", "d"]
49
+ output
50
+ """
51
+
52
+ Scenario: Accepts arguments when only require arguments & optional keyword arguments filled and valid
53
+ Given a file named "accepts_all_filled_valid_args.rb" with:
54
+ """ruby
55
+ require "./keyword_args_with_optional_positional_args_usage"
56
+ puts Example.new.foo(:output, b: 'd')
57
+ """
58
+ When I run `ruby accepts_all_filled_valid_args.rb`
59
+ Then output should contain:
60
+ """
61
+ ["a", "d"]
62
+ output
63
+ """
64
+
65
+ Scenario: Accepts arguments when only require arguments & optional positional arguments filled and valid
66
+ Given a file named "accepts_all_filled_valid_args.rb" with:
67
+ """ruby
68
+ require "./keyword_args_with_optional_positional_args_usage"
69
+ puts Example.new.foo(:output, 'c')
70
+ """
71
+ When I run `ruby accepts_all_filled_valid_args.rb`
72
+ Then output should contain:
73
+ """
74
+ ["c", "b"]
75
+ output
76
+ """
@@ -26,7 +26,8 @@ Feature: None
26
26
  def autorescue
27
27
  yield
28
28
  rescue => e
29
- puts e.inspect
29
+ # Since ruby 3.2 the `#inspect` output becomes a bit different
30
+ puts e.inspect.gsub(/^#</, "").gsub(/Error:\"/, "Error: ")
30
31
  end
31
32
  """
32
33
  Given a file named "none_usage.rb" with:
@@ -42,6 +43,11 @@ Feature: None
42
43
  def self.a_symbol(*args)
43
44
  :a_symbol
44
45
  end
46
+
47
+ Contract C::None => Symbol
48
+ def a_symbol(*args)
49
+ :a_symbol
50
+ end
45
51
  end
46
52
  """
47
53
 
@@ -61,14 +67,14 @@ Feature: None
61
67
  Given a file named "anything.rb" with:
62
68
  """ruby
63
69
  require "./none_usage"
64
- autorescue { Example.a_symbol(nil) }
65
- autorescue { Example.a_symbol(12) }
66
- autorescue { Example.a_symbol(37.5) }
67
- autorescue { Example.a_symbol("foo") }
68
- autorescue { Example.a_symbol(:foo) }
69
- autorescue { Example.a_symbol({}) }
70
- autorescue { Example.a_symbol([]) }
71
- autorescue { Example.a_symbol(Object) }
70
+ autorescue { Example.new.a_symbol(nil) }
71
+ autorescue { Example.new.a_symbol(12) }
72
+ autorescue { Example.new.a_symbol(37.5) }
73
+ autorescue { Example.new.a_symbol("foo") }
74
+ autorescue { Example.new.a_symbol(:foo) }
75
+ autorescue { Example.new.a_symbol({}) }
76
+ autorescue { Example.new.a_symbol([]) }
77
+ autorescue { Example.new.a_symbol(Object) }
72
78
  """
73
79
  When I run `ruby anything.rb`
74
80
 
@@ -95,7 +95,7 @@ module Contracts
95
95
 
96
96
  # Takes a variable number of contracts.
97
97
  # The contract passes if any of the contracts pass.
98
- # Example: <tt>Or[Fixnum, Float]</tt>
98
+ # Example: <tt>Or[Integer, Float]</tt>
99
99
  class Or < CallableClass
100
100
  def initialize(*vals)
101
101
  super()
@@ -120,7 +120,7 @@ module Contracts
120
120
 
121
121
  # Takes a variable number of contracts.
122
122
  # The contract passes if exactly one of those contracts pass.
123
- # Example: <tt>Xor[Fixnum, Float]</tt>
123
+ # Example: <tt>Xor[Integer, Float]</tt>
124
124
  class Xor < CallableClass
125
125
  def initialize(*vals)
126
126
  super()
@@ -146,7 +146,7 @@ module Contracts
146
146
 
147
147
  # Takes a variable number of contracts.
148
148
  # The contract passes if all contracts pass.
149
- # Example: <tt>And[Fixnum, Float]</tt>
149
+ # Example: <tt>And[Integer, Float]</tt>
150
150
  class And < CallableClass
151
151
  def initialize(*vals)
152
152
  super()
@@ -12,8 +12,20 @@ module Contracts
12
12
  # Explicitly append blk=nil if nil != Proc contract violation anticipated
13
13
  nil_block_appended = maybe_append_block!(args, blk)
14
14
 
15
- # Explicitly append options={} if Hash contract is present
16
- kargs_appended = maybe_append_options!(args, kargs, blk)
15
+ if @kargs_validator && !@kargs_validator[kargs]
16
+ data = {
17
+ arg: kargs,
18
+ contract: kargs_contract,
19
+ class: klass,
20
+ method: method,
21
+ contracts: self,
22
+ arg_pos: :keyword,
23
+ total_args: args.size,
24
+ return_value: false,
25
+ }
26
+ return ParamContractError.new("as return value", data) if returns
27
+ return unless Contract.failure_callback(data)
28
+ end
17
29
 
18
30
  # Loop forward validating the arguments up to the splat (if there is one)
19
31
  (@args_contract_index || args.size).times do |i|
@@ -84,7 +96,6 @@ module Contracts
84
96
  # If we put the block into args for validating, restore the args
85
97
  # OR if we added a fake nil at the end because a block wasn't passed in.
86
98
  args.slice!(-1) if blk || nil_block_appended
87
- args.slice!(-1) if kargs_appended
88
99
  result = if method.respond_to?(:call)
89
100
  # proc, block, lambda, etc
90
101
  method.call(*args, **kargs, &blk)
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Lint/RedundantRequireStatement
4
+ # If removed, spec/module_spec fails
3
5
  require "pp"
6
+ # rubocop:enable Lint/RedundantRequireStatement
4
7
 
5
8
  module Contracts
6
9
  # A namespace for classes related to formatting.
@@ -174,8 +174,10 @@ https://github.com/egonSchiele/contracts.ruby/issues
174
174
 
175
175
  def validate_pattern_matching!
176
176
  new_args_contract = decorator.args_contracts
177
+ new_kargs_contract = decorator.kargs_contract
177
178
  matched = decorated_methods.select do |contract|
178
- contract.args_contracts == new_args_contract
179
+ contract.args_contracts == new_args_contract &&
180
+ contract.kargs_contract == new_kargs_contract
179
181
  end
180
182
 
181
183
  return if matched.empty?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Contracts
4
- VERSION = "0.17"
4
+ VERSION = "0.17.2"
5
5
  end
data/lib/contracts.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "contracts/version"
3
4
  require "contracts/attrs"
4
5
  require "contracts/builtin_contracts"
5
6
  require "contracts/decorators"
@@ -52,7 +53,7 @@ class Contract < Contracts::Decorator
52
53
  end
53
54
  end
54
55
 
55
- attr_reader :args_contracts, :ret_contract, :klass, :method
56
+ attr_reader :args_contracts, :kargs_contract, :ret_contract, :klass, :method
56
57
 
57
58
  def initialize(klass, method, *contracts)
58
59
  super(klass, method)
@@ -69,6 +70,9 @@ class Contract < Contracts::Decorator
69
70
 
70
71
  # internally we just convert that return value syntax back to an array
71
72
  @args_contracts = contracts[0, contracts.size - 1] + contracts[-1].keys
73
+ # Extract contract for keyword arguments
74
+ @kargs_contract = args_contracts.find { |c| c.is_a?(Contracts::Builtin::KeywordArgs) }
75
+ args_contracts.delete(kargs_contract) if kargs_contract
72
76
 
73
77
  @ret_contract = contracts[-1].values[0]
74
78
 
@@ -76,6 +80,8 @@ class Contract < Contracts::Decorator
76
80
  Contract.make_validator(contract)
77
81
  end
78
82
 
83
+ @kargs_validator = kargs_contract ? Contract.make_validator(kargs_contract) : nil
84
+
79
85
  @args_contract_index = args_contracts.index do |contract|
80
86
  contract.is_a? Contracts::Args
81
87
  end
@@ -93,16 +99,6 @@ class Contract < Contracts::Decorator
93
99
 
94
100
  # ====
95
101
 
96
- # == @has_options_contract
97
- last_contract = args_contracts.last
98
- penultimate_contract = args_contracts[-2]
99
- @has_options_contract = if @has_proc_contract
100
- penultimate_contract.is_a?(Contracts::Builtin::KeywordArgs)
101
- else
102
- last_contract.is_a?(Contracts::Builtin::KeywordArgs)
103
- end
104
- # ===
105
-
106
102
  @klass, @method = klass, method
107
103
  end
108
104
 
@@ -255,19 +251,6 @@ class Contract < Contracts::Decorator
255
251
  true
256
252
  end
257
253
 
258
- # Same thing for when we have named params but didn't pass any in.
259
- # returns true if it appended nil
260
- def maybe_append_options! args, kargs, blk
261
- return false unless @has_options_contract
262
-
263
- if @has_proc_contract && args_contracts[-2].is_a?(Contracts::Builtin::KeywordArgs)
264
- args.insert(-2, kargs)
265
- elsif args_contracts[-1].is_a?(Contracts::Builtin::KeywordArgs)
266
- args << kargs
267
- end
268
- true
269
- end
270
-
271
254
  # Used to determine type of failure exception this contract should raise in case of failure
272
255
  def failure_exception
273
256
  if pattern_match?
@@ -30,7 +30,7 @@ RSpec.describe "Contracts:" do
30
30
  end
31
31
 
32
32
  describe "Num:" do
33
- it "should pass for Fixnums" do
33
+ it "should pass for Integers" do
34
34
  passes { @o.double(2) }
35
35
  end
36
36
 
@@ -100,11 +100,11 @@ class GenericExample
100
100
  end
101
101
  end
102
102
 
103
- Contract ({ :name => String, :age => Fixnum }) => nil
103
+ Contract ({ :name => String, :age => Integer }) => nil
104
104
  def person(data)
105
105
  end
106
106
 
107
- Contract C::StrictHash[{ :name => String, :age => Fixnum }] => nil
107
+ Contract C::StrictHash[{ :name => String, :age => Integer }] => nil
108
108
  def strict_person(data)
109
109
  end
110
110
 
@@ -119,7 +119,7 @@ class GenericExample
119
119
  def nested_hash_complex_contracts(data)
120
120
  end
121
121
 
122
- Contract C::KeywordArgs[:name => String, :age => Fixnum] => nil
122
+ Contract C::KeywordArgs[:name => String, :age => Integer] => nil
123
123
  def person_keywordargs(name: "name", age: 10)
124
124
  end
125
125
 
@@ -529,30 +529,30 @@ class MyBirthday
529
529
  @month = month
530
530
  end
531
531
 
532
- Contract C::None => Fixnum
532
+ Contract C::None => Integer
533
533
  def silly_next_day!
534
534
  self.day += 1
535
535
  end
536
536
 
537
- Contract C::None => Fixnum
537
+ Contract C::None => Integer
538
538
  def silly_next_month!
539
539
  self.month += 1
540
540
  end
541
541
 
542
- Contract C::None => Fixnum
542
+ Contract C::None => Integer
543
543
  def clever_next_day!
544
544
  return clever_next_month! if day == 31
545
545
  self.day += 1
546
546
  end
547
547
 
548
- Contract C::None => Fixnum
548
+ Contract C::None => Integer
549
549
  def clever_next_month!
550
550
  return next_year! if month == 12
551
551
  self.month += 1
552
552
  self.day = 1
553
553
  end
554
554
 
555
- Contract C::None => Fixnum
555
+ Contract C::None => Integer
556
556
  def next_year!
557
557
  self.month = 1
558
558
  self.day = 1
@@ -610,7 +610,7 @@ with_enabled_no_contracts do
610
610
  body + "!"
611
611
  end
612
612
 
613
- Contract Fixnum, String => String
613
+ Contract Integer, String => String
614
614
  def on_response(status, body)
615
615
  "error #{status}: #{body}"
616
616
  end
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.17'
4
+ version: 0.17.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aditya Bhargava
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-07 00:00:00.000000000 Z
11
+ date: 2024-10-15 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
@@ -40,6 +40,7 @@ files:
40
40
  - cucumber.yml
41
41
  - dependabot.yml
42
42
  - features/README.md
43
+ - features/advanced/pattern_matching.feature
43
44
  - features/basics/functype.feature
44
45
  - features/basics/pretty-print.feature
45
46
  - features/basics/simple_example.feature
@@ -56,6 +57,7 @@ files:
56
57
  - features/builtin_contracts/hash_of.feature
57
58
  - features/builtin_contracts/int.feature
58
59
  - features/builtin_contracts/keyword_args.feature
60
+ - features/builtin_contracts/keyword_args_with_optional_positional_args.feature
59
61
  - features/builtin_contracts/maybe.feature
60
62
  - features/builtin_contracts/nat.feature
61
63
  - features/builtin_contracts/nat_pos.feature
@@ -111,9 +113,7 @@ homepage: https://github.com/egonSchiele/contracts.ruby
111
113
  licenses:
112
114
  - BSD-2-Clause
113
115
  metadata: {}
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 "
116
+ post_install_message:
117
117
  rdoc_options: []
118
118
  require_paths:
119
119
  - lib
@@ -131,8 +131,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
131
  - !ruby/object:Gem::Version
132
132
  version: '0'
133
133
  requirements: []
134
- rubygems_version: 3.0.3
135
- signing_key:
134
+ rubygems_version: 3.4.10
135
+ signing_key:
136
136
  specification_version: 4
137
137
  summary: Contracts for Ruby.
138
138
  test_files: []