contracts 0.16.1 → 0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/code_style_checks.yaml +1 -1
- data/.github/workflows/tests.yaml +2 -8
- data/CHANGELOG.markdown +2 -0
- data/Gemfile +8 -4
- data/Rakefile +2 -0
- data/contracts.gemspec +3 -0
- data/features/support/env.rb +2 -0
- data/lib/contracts.rb +32 -23
- data/lib/contracts/attrs.rb +2 -0
- data/lib/contracts/builtin_contracts.rb +40 -7
- data/lib/contracts/call_with.rb +40 -29
- data/lib/contracts/core.rb +3 -1
- data/lib/contracts/decorators.rb +5 -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 +14 -10
- data/lib/contracts/invariants.rb +8 -4
- data/lib/contracts/method_handler.rb +19 -9
- data/lib/contracts/method_reference.rb +4 -2
- data/lib/contracts/support.rb +4 -2
- data/lib/contracts/validators.rb +6 -2
- data/lib/contracts/version.rb +3 -1
- data/spec/builtin_contracts_spec.rb +8 -12
- data/spec/contracts_spec.rb +12 -9
- data/spec/fixtures/fixtures.rb +2 -7
- data/spec/override_validators_spec.rb +3 -3
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +2 -2
- data/spec/validators_spec.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbf4bff58a593998c5902920fff3c0a808ca5eb97ec9cad3646df370435421c7
|
4
|
+
data.tar.gz: 02e5cffce164a25ae463b22e9f8b85c810d50c9c2875d09a3e83751afebac012
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6911f4383b0140c40bc08c838a3044897c3c9ac3c577fae4b3806879cbfed12f044f8ee1336305b1599c10af1bfb5aabe9ae2e8f1f9ffd94774d4de789e8402
|
7
|
+
data.tar.gz: 25c8ecfe04e846ba55d87b33a43e0a38422440d0a963066ac67217edd8ebc396cbb2309b50704ea6f4dc4609bacbfb3fb3b501ef0a40210fe1877df5c6fbb7f8
|
@@ -22,17 +22,11 @@ jobs:
|
|
22
22
|
os:
|
23
23
|
- ubuntu
|
24
24
|
ruby:
|
25
|
-
- "
|
26
|
-
- "2.2"
|
27
|
-
- "2.3"
|
28
|
-
- "2.4"
|
29
|
-
- "2.5"
|
30
|
-
- "2.6"
|
31
|
-
- "2.7"
|
25
|
+
- "3.0"
|
32
26
|
test_command: ["bundle exec rspec && bundle exec cucumber"]
|
33
27
|
include:
|
34
28
|
- os: ubuntu
|
35
|
-
ruby: "
|
29
|
+
ruby: "3.0"
|
36
30
|
test_command: "bundle exec rspec"
|
37
31
|
runs-on: ${{ matrix.os }}-latest
|
38
32
|
steps:
|
data/CHANGELOG.markdown
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
|
2
2
|
## [v0.16.1] - 2021-04-17
|
3
3
|
|
4
|
+
[v0.16.1]: https://github.com/egonSchiele/contracts.ruby/compare/v0.16.0...v0.16.1
|
5
|
+
|
4
6
|
- Enhancement: Pretty-print contracts in error messages - [Corey Farwell](https://github.com/frewsxcv) [#289](https://github.com/egonSchiele/contracts.ruby/pull/289)
|
5
7
|
- Bugfix: Fix `attr_accessor_with_contract` with multiple attribute names input - [Kevin Yeh](https://github.com/kyeah) [#259](https://github.com/egonSchiele/contracts.ruby/pull/259)
|
6
8
|
- Bugfix: Fix "stack level too deep" in CI builds - [md-work](https://github.com/md-work) [#283](https://github.com/egonSchiele/contracts.ruby/pull/283)
|
data/Gemfile
CHANGED
@@ -1,17 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source "https://rubygems.org"
|
2
4
|
|
3
5
|
gemspec
|
4
6
|
|
5
7
|
group :test do
|
6
|
-
gem "rspec"
|
7
8
|
gem "aruba"
|
8
9
|
gem "cucumber", "~> 1.3.20"
|
9
|
-
gem "
|
10
|
+
gem "rspec"
|
11
|
+
|
12
|
+
gem "rubocop", ">= 1.0.0"
|
13
|
+
gem "rubocop-performance", ">= 1.0.0"
|
10
14
|
end
|
11
15
|
|
12
16
|
group :development do
|
13
|
-
gem "relish"
|
14
17
|
gem "method_profiler"
|
15
|
-
gem "ruby-prof"
|
16
18
|
gem "rake"
|
19
|
+
gem "relish"
|
20
|
+
gem "ruby-prof"
|
17
21
|
end
|
data/Rakefile
CHANGED
data/contracts.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require File.expand_path(File.join(__FILE__, "../lib/contracts/version"))
|
2
4
|
|
3
5
|
Gem::Specification.new do |s|
|
@@ -10,6 +12,7 @@ Gem::Specification.new do |s|
|
|
10
12
|
s.files = `git ls-files`.split("\n")
|
11
13
|
s.homepage = "https://github.com/egonSchiele/contracts.ruby"
|
12
14
|
s.license = "BSD-2-Clause"
|
15
|
+
s.required_ruby_version = [">= 3.0", "< 4"]
|
13
16
|
s.post_install_message = "
|
14
17
|
0.16.x will be the supporting Ruby 2.x and be feature frozen (only fixes will be released)
|
15
18
|
For Ruby 3.x use 0.17.x or later (might not be released yet)
|
data/features/support/env.rb
CHANGED
data/lib/contracts.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "contracts/attrs"
|
2
4
|
require "contracts/builtin_contracts"
|
3
5
|
require "contracts/decorators"
|
@@ -51,7 +53,9 @@ class Contract < Contracts::Decorator
|
|
51
53
|
end
|
52
54
|
|
53
55
|
attr_reader :args_contracts, :ret_contract, :klass, :method
|
56
|
+
|
54
57
|
def initialize(klass, method, *contracts)
|
58
|
+
super(klass, method)
|
55
59
|
unless contracts.last.is_a?(Hash)
|
56
60
|
unless contracts.one?
|
57
61
|
fail %{
|
@@ -93,17 +97,17 @@ class Contract < Contracts::Decorator
|
|
93
97
|
last_contract = args_contracts.last
|
94
98
|
penultimate_contract = args_contracts[-2]
|
95
99
|
@has_options_contract = if @has_proc_contract
|
96
|
-
penultimate_contract.is_a?(
|
100
|
+
penultimate_contract.is_a?(Contracts::Builtin::KeywordArgs)
|
97
101
|
else
|
98
|
-
last_contract.is_a?(
|
102
|
+
last_contract.is_a?(Contracts::Builtin::KeywordArgs)
|
99
103
|
end
|
100
104
|
# ===
|
101
105
|
|
102
106
|
@klass, @method = klass, method
|
103
107
|
end
|
104
108
|
|
105
|
-
def pretty_contract
|
106
|
-
|
109
|
+
def pretty_contract contract
|
110
|
+
contract.is_a?(Class) ? contract.name : contract.class.name
|
107
111
|
end
|
108
112
|
|
109
113
|
def to_s
|
@@ -130,15 +134,15 @@ class Contract < Contracts::Decorator
|
|
130
134
|
expected_prefix = "Expected: "
|
131
135
|
expected_value = Contracts::Support.indent_string(
|
132
136
|
Contracts::Formatters::Expected.new(data[:contract]).contract.pretty_inspect,
|
133
|
-
expected_prefix.length
|
137
|
+
expected_prefix.length,
|
134
138
|
).strip
|
135
|
-
expected_line = expected_prefix
|
139
|
+
expected_line = "#{expected_prefix}#{expected_value},"
|
136
140
|
|
137
141
|
# Actual
|
138
142
|
actual_prefix = "Actual: "
|
139
143
|
actual_value = Contracts::Support.indent_string(
|
140
144
|
data[:arg].pretty_inspect,
|
141
|
-
actual_prefix.length
|
145
|
+
actual_prefix.length,
|
142
146
|
).strip
|
143
147
|
actual_line = actual_prefix + actual_value
|
144
148
|
|
@@ -157,16 +161,19 @@ class Contract < Contracts::Decorator
|
|
157
161
|
position_value = Contracts::Support.method_position(data[:method])
|
158
162
|
position_line = position_prefix + position_value
|
159
163
|
|
160
|
-
|
161
|
-
|
164
|
+
[
|
165
|
+
header,
|
162
166
|
Contracts::Support.indent_string(
|
163
|
-
[
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
167
|
+
[
|
168
|
+
expected_line,
|
169
|
+
actual_line,
|
170
|
+
value_line,
|
171
|
+
contract_line,
|
172
|
+
position_line,
|
173
|
+
].join("\n"),
|
174
|
+
indent_amount,
|
175
|
+
),
|
176
|
+
].join("\n")
|
170
177
|
end
|
171
178
|
|
172
179
|
# Callback for when a contract fails. By default it raises
|
@@ -182,7 +189,7 @@ class Contract < Contracts::Decorator
|
|
182
189
|
# puts failure_msg(data)
|
183
190
|
# exit
|
184
191
|
# end
|
185
|
-
def self.failure_callback(data, use_pattern_matching
|
192
|
+
def self.failure_callback(data, use_pattern_matching: true)
|
186
193
|
if data[:contracts].pattern_match? && use_pattern_matching
|
187
194
|
return DEFAULT_FAILURE_CALLBACK.call(data)
|
188
195
|
end
|
@@ -242,19 +249,21 @@ class Contract < Contracts::Decorator
|
|
242
249
|
# returns true if it appended nil
|
243
250
|
def maybe_append_block! args, blk
|
244
251
|
return false unless @has_proc_contract && !blk &&
|
245
|
-
|
252
|
+
(@args_contract_index || args.size < args_contracts.size)
|
253
|
+
|
246
254
|
args << nil
|
247
255
|
true
|
248
256
|
end
|
249
257
|
|
250
258
|
# Same thing for when we have named params but didn't pass any in.
|
251
259
|
# returns true if it appended nil
|
252
|
-
def maybe_append_options! args, blk
|
260
|
+
def maybe_append_options! args, kargs, blk
|
253
261
|
return false unless @has_options_contract
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
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
|
258
267
|
end
|
259
268
|
true
|
260
269
|
end
|
data/lib/contracts/attrs.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "contracts/formatters"
|
2
4
|
require "set"
|
3
5
|
|
@@ -30,35 +32,35 @@ module Contracts
|
|
30
32
|
# Check that an argument is a positive number.
|
31
33
|
class Pos
|
32
34
|
def self.valid? val
|
33
|
-
val
|
35
|
+
val.is_a?(Numeric) && val.positive?
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
39
|
# Check that an argument is a negative number.
|
38
40
|
class Neg
|
39
41
|
def self.valid? val
|
40
|
-
val
|
42
|
+
val.is_a?(Numeric) && val.negative?
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
44
46
|
# Check that an argument is an +Integer+.
|
45
47
|
class Int
|
46
48
|
def self.valid? val
|
47
|
-
val
|
49
|
+
val.is_a?(Integer)
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
51
53
|
# Check that an argument is a natural number (includes zero).
|
52
54
|
class Nat
|
53
55
|
def self.valid? val
|
54
|
-
val
|
56
|
+
val.is_a?(Integer) && val >= 0
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
60
|
# Check that an argument is a positive natural number (excludes zero).
|
59
61
|
class NatPos
|
60
62
|
def self.valid? val
|
61
|
-
val
|
63
|
+
val.is_a?(Integer) && val.positive?
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
@@ -96,6 +98,7 @@ module Contracts
|
|
96
98
|
# Example: <tt>Or[Fixnum, Float]</tt>
|
97
99
|
class Or < CallableClass
|
98
100
|
def initialize(*vals)
|
101
|
+
super()
|
99
102
|
@vals = vals
|
100
103
|
end
|
101
104
|
|
@@ -107,9 +110,11 @@ module Contracts
|
|
107
110
|
end
|
108
111
|
|
109
112
|
def to_s
|
113
|
+
# rubocop:disable Style/StringConcatenation
|
110
114
|
@vals[0, @vals.size-1].map do |x|
|
111
115
|
InspectWrapper.create(x)
|
112
116
|
end.join(", ") + " or " + InspectWrapper.create(@vals[-1]).to_s
|
117
|
+
# rubocop:enable Style/StringConcatenation
|
113
118
|
end
|
114
119
|
end
|
115
120
|
|
@@ -118,6 +123,7 @@ module Contracts
|
|
118
123
|
# Example: <tt>Xor[Fixnum, Float]</tt>
|
119
124
|
class Xor < CallableClass
|
120
125
|
def initialize(*vals)
|
126
|
+
super()
|
121
127
|
@vals = vals
|
122
128
|
end
|
123
129
|
|
@@ -130,9 +136,11 @@ module Contracts
|
|
130
136
|
end
|
131
137
|
|
132
138
|
def to_s
|
139
|
+
# rubocop:disable Style/StringConcatenation
|
133
140
|
@vals[0, @vals.size-1].map do |x|
|
134
141
|
InspectWrapper.create(x)
|
135
142
|
end.join(", ") + " xor " + InspectWrapper.create(@vals[-1]).to_s
|
143
|
+
# rubocop:enable Style/StringConcatenation
|
136
144
|
end
|
137
145
|
end
|
138
146
|
|
@@ -141,6 +149,7 @@ module Contracts
|
|
141
149
|
# Example: <tt>And[Fixnum, Float]</tt>
|
142
150
|
class And < CallableClass
|
143
151
|
def initialize(*vals)
|
152
|
+
super()
|
144
153
|
@vals = vals
|
145
154
|
end
|
146
155
|
|
@@ -152,9 +161,11 @@ module Contracts
|
|
152
161
|
end
|
153
162
|
|
154
163
|
def to_s
|
164
|
+
# rubocop:disable Style/StringConcatenation
|
155
165
|
@vals[0, @vals.size-1].map do |x|
|
156
166
|
InspectWrapper.create(x)
|
157
167
|
end.join(", ") + " and " + InspectWrapper.create(@vals[-1]).to_s
|
168
|
+
# rubocop:enable Style/StringConcatenation
|
158
169
|
end
|
159
170
|
end
|
160
171
|
|
@@ -164,6 +175,7 @@ module Contracts
|
|
164
175
|
# Example: <tt>RespondTo[:password, :credit_card]</tt>
|
165
176
|
class RespondTo < CallableClass
|
166
177
|
def initialize(*meths)
|
178
|
+
super()
|
167
179
|
@meths = meths
|
168
180
|
end
|
169
181
|
|
@@ -185,6 +197,7 @@ module Contracts
|
|
185
197
|
# Example: <tt>Send[:valid?]</tt>
|
186
198
|
class Send < CallableClass
|
187
199
|
def initialize(*meths)
|
200
|
+
super()
|
188
201
|
@meths = meths
|
189
202
|
end
|
190
203
|
|
@@ -204,11 +217,12 @@ module Contracts
|
|
204
217
|
# Example: <tt>Exactly[Numeric]</tt>
|
205
218
|
class Exactly < CallableClass
|
206
219
|
def initialize(cls)
|
220
|
+
super()
|
207
221
|
@cls = cls
|
208
222
|
end
|
209
223
|
|
210
224
|
def valid?(val)
|
211
|
-
val.
|
225
|
+
val.instance_of?(@cls)
|
212
226
|
end
|
213
227
|
|
214
228
|
def to_s
|
@@ -222,6 +236,7 @@ module Contracts
|
|
222
236
|
# Example: <tt>Enum[:a, :b, :c]</tt>?
|
223
237
|
class Enum < CallableClass
|
224
238
|
def initialize(*vals)
|
239
|
+
super()
|
225
240
|
@vals = vals
|
226
241
|
end
|
227
242
|
|
@@ -235,6 +250,7 @@ module Contracts
|
|
235
250
|
# Example: <tt>Eq[Class]</tt>
|
236
251
|
class Eq < CallableClass
|
237
252
|
def initialize(value)
|
253
|
+
super()
|
238
254
|
@value = value
|
239
255
|
end
|
240
256
|
|
@@ -252,6 +268,7 @@ module Contracts
|
|
252
268
|
# Example: <tt>Not[nil]</tt>
|
253
269
|
class Not < CallableClass
|
254
270
|
def initialize(*vals)
|
271
|
+
super()
|
255
272
|
@vals = vals
|
256
273
|
end
|
257
274
|
|
@@ -275,12 +292,14 @@ module Contracts
|
|
275
292
|
# Example: <tt>CollectionOf[Array, Num]</tt>
|
276
293
|
class CollectionOf < CallableClass
|
277
294
|
def initialize(collection_class, contract)
|
295
|
+
super()
|
278
296
|
@collection_class = collection_class
|
279
297
|
@contract = contract
|
280
298
|
end
|
281
299
|
|
282
300
|
def valid?(vals)
|
283
301
|
return false unless vals.is_a?(@collection_class)
|
302
|
+
|
284
303
|
vals.all? do |val|
|
285
304
|
res, _ = Contract.valid?(val, @contract)
|
286
305
|
res
|
@@ -298,7 +317,7 @@ module Contracts
|
|
298
317
|
end
|
299
318
|
|
300
319
|
def new(contract)
|
301
|
-
@before_new
|
320
|
+
@before_new&.call
|
302
321
|
CollectionOf.new(@collection_class, contract)
|
303
322
|
end
|
304
323
|
|
@@ -324,7 +343,9 @@ module Contracts
|
|
324
343
|
# Example: <tt>Args[Or[String, Num]]</tt>
|
325
344
|
class Args < CallableClass
|
326
345
|
attr_reader :contract
|
346
|
+
|
327
347
|
def initialize(contract)
|
348
|
+
super()
|
328
349
|
@contract = contract
|
329
350
|
end
|
330
351
|
|
@@ -343,6 +364,7 @@ module Contracts
|
|
343
364
|
# Example: <tt>RangeOf[Nat]</tt>, <tt>RangeOf[Date]</tt>, ...
|
344
365
|
class RangeOf < CallableClass
|
345
366
|
def initialize(contract)
|
367
|
+
super()
|
346
368
|
@contract = contract
|
347
369
|
end
|
348
370
|
|
@@ -364,6 +386,7 @@ module Contracts
|
|
364
386
|
INVALID_KEY_VALUE_PAIR = "You should provide only one key-value pair to HashOf contract"
|
365
387
|
|
366
388
|
def initialize(key, value = nil)
|
389
|
+
super()
|
367
390
|
if value
|
368
391
|
@key = key
|
369
392
|
@value = value
|
@@ -376,6 +399,7 @@ module Contracts
|
|
376
399
|
|
377
400
|
def valid?(hash)
|
378
401
|
return false unless hash.is_a?(Hash)
|
402
|
+
|
379
403
|
keys_match = hash.keys.map { |k| Contract.valid?(k, @key) }.all?
|
380
404
|
vals_match = hash.values.map { |v| Contract.valid?(v, @value) }.all?
|
381
405
|
|
@@ -400,6 +424,7 @@ module Contracts
|
|
400
424
|
attr_reader :contract_hash
|
401
425
|
|
402
426
|
def initialize(contract_hash)
|
427
|
+
super()
|
403
428
|
@contract_hash = contract_hash
|
404
429
|
end
|
405
430
|
|
@@ -417,12 +442,14 @@ module Contracts
|
|
417
442
|
# Example: <tt>KeywordArgs[ e: Range, f: Optional[Num] ]</tt>
|
418
443
|
class KeywordArgs < CallableClass
|
419
444
|
def initialize(options)
|
445
|
+
super()
|
420
446
|
@options = options
|
421
447
|
end
|
422
448
|
|
423
449
|
def valid?(hash)
|
424
450
|
return false unless hash.is_a?(Hash)
|
425
451
|
return false unless hash.keys - options.keys == []
|
452
|
+
|
426
453
|
options.all? do |key, contract|
|
427
454
|
Optional._valid?(hash, key, contract)
|
428
455
|
end
|
@@ -445,6 +472,7 @@ module Contracts
|
|
445
472
|
# Example: <tt>DescendantOf[ e: Range, f: Optional[Num] ]</tt>
|
446
473
|
class DescendantOf < CallableClass
|
447
474
|
def initialize(parent_class)
|
475
|
+
super()
|
448
476
|
@parent_class = parent_class
|
449
477
|
end
|
450
478
|
|
@@ -473,11 +501,13 @@ module Contracts
|
|
473
501
|
|
474
502
|
def self._valid?(hash, key, contract)
|
475
503
|
return Contract.valid?(hash[key], contract) unless contract.is_a?(Optional)
|
504
|
+
|
476
505
|
contract.within_opt_hash!
|
477
506
|
!hash.key?(key) || Contract.valid?(hash[key], contract)
|
478
507
|
end
|
479
508
|
|
480
509
|
def initialize(contract)
|
510
|
+
super()
|
481
511
|
@contract = contract
|
482
512
|
@within_opt_hash = false
|
483
513
|
end
|
@@ -506,6 +536,7 @@ module Contracts
|
|
506
536
|
|
507
537
|
def ensure_within_opt_hash
|
508
538
|
return if within_opt_hash
|
539
|
+
|
509
540
|
fail ArgumentError, UNABLE_TO_USE_OUTSIDE_OF_OPT_HASH
|
510
541
|
end
|
511
542
|
|
@@ -531,7 +562,9 @@ module Contracts
|
|
531
562
|
# Example: <tt>Func[Num => Num] # the function should take a number and return a number</tt>
|
532
563
|
class Func < CallableClass
|
533
564
|
attr_reader :contracts
|
565
|
+
|
534
566
|
def initialize(*contracts)
|
567
|
+
super()
|
535
568
|
@contracts = contracts
|
536
569
|
end
|
537
570
|
end
|
data/lib/contracts/call_with.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module CallWith
|
3
|
-
def call_with(this, *args, &blk)
|
4
|
-
call_with_inner(false, this, *args, &blk)
|
5
|
+
def call_with(this, *args, **kargs, &blk)
|
6
|
+
call_with_inner(false, this, *args, **kargs, &blk)
|
5
7
|
end
|
6
8
|
|
7
|
-
def call_with_inner(returns, this, *args, &blk)
|
9
|
+
def call_with_inner(returns, this, *args, **kargs, &blk)
|
8
10
|
args << blk if blk
|
9
11
|
|
10
12
|
# Explicitly append blk=nil if nil != Proc contract violation anticipated
|
11
13
|
nil_block_appended = maybe_append_block!(args, blk)
|
12
14
|
|
13
15
|
# Explicitly append options={} if Hash contract is present
|
14
|
-
maybe_append_options!(args, blk)
|
16
|
+
kargs_appended = maybe_append_options!(args, kargs, blk)
|
15
17
|
|
16
18
|
# Loop forward validating the arguments up to the splat (if there is one)
|
17
19
|
(@args_contract_index || args.size).times do |i|
|
@@ -20,14 +22,16 @@ module Contracts
|
|
20
22
|
validator = @args_validators[i]
|
21
23
|
|
22
24
|
unless validator && validator[arg]
|
23
|
-
data = {
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
data = {
|
26
|
+
arg: arg,
|
27
|
+
contract: contract,
|
28
|
+
class: klass,
|
29
|
+
method: method,
|
30
|
+
contracts: self,
|
31
|
+
arg_pos: i+1,
|
32
|
+
total_args: args.size,
|
33
|
+
return_value: false,
|
34
|
+
}
|
31
35
|
return ParamContractError.new("as return value", data) if returns
|
32
36
|
return unless Contract.failure_callback(data)
|
33
37
|
end
|
@@ -57,14 +61,18 @@ module Contracts
|
|
57
61
|
validator = @args_validators[args_contracts.size - 1 - j]
|
58
62
|
|
59
63
|
unless validator && validator[arg]
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
# rubocop:disable Style/SoleNestedConditional
|
65
|
+
return unless Contract.failure_callback({
|
66
|
+
:arg => arg,
|
67
|
+
:contract => contract,
|
68
|
+
:class => klass,
|
69
|
+
:method => method,
|
70
|
+
:contracts => self,
|
71
|
+
:arg_pos => i - 1,
|
72
|
+
:total_args => args.size,
|
73
|
+
:return_value => false,
|
74
|
+
})
|
75
|
+
# rubocop:enable Style/SoleNestedConditional
|
68
76
|
end
|
69
77
|
|
70
78
|
if contract.is_a?(Contracts::Func)
|
@@ -76,24 +84,27 @@ module Contracts
|
|
76
84
|
# If we put the block into args for validating, restore the args
|
77
85
|
# OR if we added a fake nil at the end because a block wasn't passed in.
|
78
86
|
args.slice!(-1) if blk || nil_block_appended
|
87
|
+
args.slice!(-1) if kargs_appended
|
79
88
|
result = if method.respond_to?(:call)
|
80
89
|
# proc, block, lambda, etc
|
81
|
-
method.call(*args, &blk)
|
90
|
+
method.call(*args, **kargs, &blk)
|
82
91
|
else
|
83
92
|
# original method name reference
|
84
93
|
# Don't reassign blk, else Travis CI shows "stack level too deep".
|
85
94
|
target_blk = blk
|
86
|
-
target_blk = lambda { |*params| blk.call(*params) } if blk
|
87
|
-
method.send_to(this, *args, &target_blk)
|
95
|
+
target_blk = lambda { |*params| blk.call(*params) } if blk.is_a?(Contract)
|
96
|
+
method.send_to(this, *args, **kargs, &target_blk)
|
88
97
|
end
|
89
98
|
|
90
99
|
unless @ret_validator[result]
|
91
|
-
Contract.failure_callback(
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
100
|
+
Contract.failure_callback({
|
101
|
+
arg: result,
|
102
|
+
contract: ret_contract,
|
103
|
+
class: klass,
|
104
|
+
method: method,
|
105
|
+
contracts: self,
|
106
|
+
return_value: true,
|
107
|
+
})
|
97
108
|
end
|
98
109
|
|
99
110
|
this.verify_invariants!(method) if this.respond_to?(:verify_invariants!)
|
data/lib/contracts/core.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module Core
|
3
5
|
def self.included(base)
|
@@ -25,7 +27,7 @@ module Contracts
|
|
25
27
|
# NOTE: Workaround for `defined?(super)` bug in ruby 1.9.2
|
26
28
|
# source: http://stackoverflow.com/a/11181685
|
27
29
|
# bug: https://bugs.ruby-lang.org/issues/6644
|
28
|
-
base.class_eval <<-RUBY
|
30
|
+
base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
29
31
|
# TODO: deprecate
|
30
32
|
# Required when contracts are included in global scope
|
31
33
|
def Contract(*args)
|
data/lib/contracts/decorators.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module MethodDecorators
|
3
5
|
def self.extended(klass)
|
@@ -25,6 +27,7 @@ module Contracts
|
|
25
27
|
class << self; attr_accessor :decorators; end
|
26
28
|
|
27
29
|
def self.inherited(klass)
|
30
|
+
super
|
28
31
|
name = klass.name.gsub(/^./) { |m| m.downcase }
|
29
32
|
|
30
33
|
return if name =~ /^[^A-Za-z_]/ || name =~ /[^0-9A-Za-z_]/
|
@@ -33,11 +36,11 @@ module Contracts
|
|
33
36
|
# make a new method that is the name of your decorator.
|
34
37
|
# that method accepts random args and a block.
|
35
38
|
# inside, `decorate` is called with those params.
|
36
|
-
MethodDecorators.module_eval <<-
|
39
|
+
MethodDecorators.module_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
37
40
|
def #{klass}(*args, &blk)
|
38
41
|
::Contracts::Engine.fetch_from(self).decorate(#{klass}, *args, &blk)
|
39
42
|
end
|
40
|
-
|
43
|
+
RUBY_EVAL
|
41
44
|
end
|
42
45
|
|
43
46
|
def initialize(klass, method)
|
data/lib/contracts/engine.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module Engine
|
3
5
|
# Contracts engine
|
@@ -90,7 +92,7 @@ module Contracts
|
|
90
92
|
def nearest_decorated_ancestor
|
91
93
|
current = klass
|
92
94
|
current_engine = self
|
93
|
-
ancestors = current.ancestors[1
|
95
|
+
ancestors = current.ancestors[1..]
|
94
96
|
|
95
97
|
while current && current_engine && !current_engine.decorated_methods?
|
96
98
|
current = ancestors.shift
|
@@ -109,8 +111,7 @@ module Contracts
|
|
109
111
|
end
|
110
112
|
|
111
113
|
# No-op because it is safe to add decorators to normal classes
|
112
|
-
def validate
|
113
|
-
end
|
114
|
+
def validate!; end
|
114
115
|
|
115
116
|
def pop_decorators
|
116
117
|
decorators.tap { clear_decorators }
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module Engine
|
3
5
|
# Special case of contracts engine for eigenclasses
|
@@ -27,8 +29,7 @@ module Contracts
|
|
27
29
|
end
|
28
30
|
|
29
31
|
# No-op for eigenclasses
|
30
|
-
def set_eigenclass_owner
|
31
|
-
end
|
32
|
+
def set_eigenclass_owner; end
|
32
33
|
|
33
34
|
# Fetches just eigenclasses decorators
|
34
35
|
def all_decorators
|
data/lib/contracts/errors.rb
CHANGED
data/lib/contracts/formatters.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pp"
|
2
4
|
|
3
5
|
module Contracts
|
@@ -7,18 +9,19 @@ module Contracts
|
|
7
9
|
class Expected
|
8
10
|
# @param full [Boolean] if false only unique `to_s` values will be output,
|
9
11
|
# non unique values become empty string.
|
10
|
-
def initialize(contract, full
|
12
|
+
def initialize(contract, full: true)
|
11
13
|
@contract, @full = contract, full
|
12
14
|
end
|
13
15
|
|
14
16
|
# Formats any type of Contract.
|
15
17
|
def contract(contract = @contract)
|
16
|
-
|
18
|
+
case contract
|
19
|
+
when Hash
|
17
20
|
hash_contract(contract)
|
18
|
-
|
21
|
+
when Array
|
19
22
|
array_contract(contract)
|
20
23
|
else
|
21
|
-
InspectWrapper.create(contract, @full)
|
24
|
+
InspectWrapper.create(contract, full: @full)
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
@@ -26,14 +29,14 @@ module Contracts
|
|
26
29
|
def hash_contract(hash)
|
27
30
|
@full = true # Complex values output completely, overriding @full
|
28
31
|
hash.inject({}) do |repr, (k, v)|
|
29
|
-
repr.merge(k => InspectWrapper.create(contract(v), @full))
|
32
|
+
repr.merge(k => InspectWrapper.create(contract(v), full: @full))
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
36
|
# Formats Array contracts.
|
34
37
|
def array_contract(array)
|
35
38
|
@full = true
|
36
|
-
array.map { |v| InspectWrapper.create(contract(v), @full) }
|
39
|
+
array.map { |v| InspectWrapper.create(contract(v), full: @full) }
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
@@ -42,8 +45,8 @@ module Contracts
|
|
42
45
|
module InspectWrapper
|
43
46
|
# InspectWrapper is a factory, will never be an instance
|
44
47
|
# @return [ClassInspectWrapper, ObjectInspectWrapper]
|
45
|
-
def self.create(value, full
|
46
|
-
if value.
|
48
|
+
def self.create(value, full: true)
|
49
|
+
if value.instance_of?(Class)
|
47
50
|
ClassInspectWrapper
|
48
51
|
else
|
49
52
|
ObjectInspectWrapper
|
@@ -66,6 +69,7 @@ module Contracts
|
|
66
69
|
return @value.inspect if empty_val?
|
67
70
|
return @value.to_s if plain?
|
68
71
|
return delim(@value.to_s) if useful_to_s?
|
72
|
+
|
69
73
|
useful_inspect
|
70
74
|
end
|
71
75
|
|
@@ -96,7 +100,7 @@ module Contracts
|
|
96
100
|
end
|
97
101
|
|
98
102
|
def useful_to_s?
|
99
|
-
# Useless to_s value or no custom to_s
|
103
|
+
# Useless to_s value or no custom to_s behaviour defined
|
100
104
|
!empty_to_s? && custom_to_s?
|
101
105
|
end
|
102
106
|
|
@@ -125,7 +129,7 @@ module Contracts
|
|
125
129
|
include InspectWrapper
|
126
130
|
|
127
131
|
def custom_to_s?
|
128
|
-
!@value.to_s.match(
|
132
|
+
!@value.to_s.match(/#<\w+:.+>/)
|
129
133
|
end
|
130
134
|
|
131
135
|
def useful_inspect
|
data/lib/contracts/invariants.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module Invariants
|
3
5
|
def self.included(base)
|
@@ -46,10 +48,12 @@ module Contracts
|
|
46
48
|
def check_on(target, method)
|
47
49
|
return if target.instance_eval(&@condition)
|
48
50
|
|
49
|
-
self.class.failure_callback(
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
self.class.failure_callback({
|
52
|
+
expected: expected,
|
53
|
+
actual: false,
|
54
|
+
target: target,
|
55
|
+
method: method,
|
56
|
+
})
|
53
57
|
end
|
54
58
|
|
55
59
|
def self.failure_callback(data)
|
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
# Handles class and instance methods addition
|
3
5
|
# Represents single such method
|
4
6
|
class MethodHandler
|
5
7
|
METHOD_REFERENCE_FACTORY = {
|
6
8
|
:class_methods => SingletonMethodReference,
|
7
|
-
:instance_methods => MethodReference
|
9
|
+
:instance_methods => MethodReference,
|
8
10
|
}
|
9
11
|
|
10
12
|
RAW_METHOD_STRATEGY = {
|
11
13
|
:class_methods => lambda { |target, name| target.method(name) },
|
12
|
-
:instance_methods => lambda { |target, name| target.instance_method(name) }
|
14
|
+
:instance_methods => lambda { |target, name| target.instance_method(name) },
|
13
15
|
}
|
14
16
|
|
15
17
|
# Creates new instance of MethodHandler
|
@@ -78,11 +80,13 @@ module Contracts
|
|
78
80
|
|
79
81
|
def pattern_matching?
|
80
82
|
return @_pattern_matching if defined?(@_pattern_matching)
|
83
|
+
|
81
84
|
@_pattern_matching = decorated_methods.any? { |x| x.method != method_reference }
|
82
85
|
end
|
83
86
|
|
84
87
|
def mark_pattern_matching_decorators
|
85
88
|
return unless pattern_matching?
|
89
|
+
|
86
90
|
decorated_methods.each(&:pattern_match!)
|
87
91
|
end
|
88
92
|
|
@@ -107,13 +111,13 @@ module Contracts
|
|
107
111
|
current_engine = engine
|
108
112
|
|
109
113
|
# We are gonna redefine original method here
|
110
|
-
method_reference.make_definition(target) do |*args, &blk|
|
114
|
+
method_reference.make_definition(target) do |*args, **kargs, &blk|
|
111
115
|
engine = current_engine.nearest_decorated_ancestor
|
112
116
|
|
113
117
|
# If we weren't able to find any ancestor that has decorated methods
|
114
118
|
# FIXME : this looks like untested code (commenting it out doesn't make specs red)
|
115
119
|
unless engine
|
116
|
-
fail "Couldn't find decorator for method
|
120
|
+
fail "Couldn't find decorator for method #{self.class.name}:#{name}.\nDoes this method look correct to you? If you are using contracts from rspec, rspec wraps classes in it's own class.\nLook at the specs for contracts.ruby as an example of how to write contracts in this case."
|
117
121
|
end
|
118
122
|
|
119
123
|
# Fetch decorated methods out of the contracts engine
|
@@ -130,17 +134,20 @@ module Contracts
|
|
130
134
|
last_error = nil
|
131
135
|
|
132
136
|
decorated_methods.each do |decorated_method|
|
133
|
-
result = decorated_method.call_with_inner(true, self, *args, &blk)
|
137
|
+
result = decorated_method.call_with_inner(true, self, *args, **kargs, &blk)
|
134
138
|
return result unless result.is_a?(ParamContractError)
|
139
|
+
|
135
140
|
last_error = result
|
136
141
|
end
|
137
142
|
|
138
143
|
begin
|
139
|
-
if ::Contract.failure_callback(last_error
|
140
|
-
decorated_methods.last.call_with_inner(false, self, *args, &blk)
|
144
|
+
if ::Contract.failure_callback(last_error&.data, use_pattern_matching: false)
|
145
|
+
decorated_methods.last.call_with_inner(false, self, *args, **kargs, &blk)
|
141
146
|
end
|
147
|
+
# rubocop:disable Naming/RescuedExceptionsVariableName
|
142
148
|
rescue expected_error => final_error
|
143
149
|
raise final_error.to_contract_error
|
150
|
+
# rubocop:enable Naming/RescuedExceptionsVariableName
|
144
151
|
end
|
145
152
|
end
|
146
153
|
end
|
@@ -173,7 +180,8 @@ https://github.com/egonSchiele/contracts.ruby/issues
|
|
173
180
|
|
174
181
|
return if matched.empty?
|
175
182
|
|
176
|
-
fail ContractError.new(
|
183
|
+
fail ContractError.new(
|
184
|
+
%{
|
177
185
|
It looks like you are trying to use pattern-matching, but
|
178
186
|
multiple definitions for function '#{method_name}' have the same
|
179
187
|
contract for input parameters:
|
@@ -181,7 +189,9 @@ contract for input parameters:
|
|
181
189
|
#{(matched + [decorator]).map(&:to_s).join("\n")}
|
182
190
|
|
183
191
|
Each definition needs to have a different contract for the parameters.
|
184
|
-
|
192
|
+
},
|
193
|
+
{},
|
194
|
+
)
|
185
195
|
end
|
186
196
|
end
|
187
197
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
# MethodReference represents original method reference that was
|
3
5
|
# decorated by contracts.ruby. Used for instance methods.
|
@@ -39,8 +41,8 @@ module Contracts
|
|
39
41
|
|
40
42
|
# Calls original method on specified `this` argument with
|
41
43
|
# specified arguments `args` and block `&blk`.
|
42
|
-
def send_to(this, *args, &blk)
|
43
|
-
this.send(aliased_name, *args, &blk)
|
44
|
+
def send_to(this, *args, **kargs, &blk)
|
45
|
+
this.send(aliased_name, *args, **kargs, &blk)
|
44
46
|
end
|
45
47
|
|
46
48
|
private
|
data/lib/contracts/support.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module Support
|
3
5
|
class << self
|
@@ -8,7 +10,7 @@ module Contracts
|
|
8
10
|
if file.nil? || line.nil?
|
9
11
|
""
|
10
12
|
else
|
11
|
-
file
|
13
|
+
"#{file}:#{line}"
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
@@ -45,7 +47,7 @@ module Contracts
|
|
45
47
|
def indent_string(string, amount)
|
46
48
|
string.gsub(
|
47
49
|
/^(?!$)/,
|
48
|
-
(string[/^[ \t]/] || " ") * amount
|
50
|
+
(string[/^[ \t]/] || " ") * amount,
|
49
51
|
)
|
50
52
|
end
|
51
53
|
|
data/lib/contracts/validators.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module Validators
|
3
5
|
DEFAULT_VALIDATOR_STRATEGIES = {
|
@@ -9,6 +11,7 @@ module Contracts
|
|
9
11
|
Array => lambda do |contract|
|
10
12
|
lambda do |arg|
|
11
13
|
return false unless arg.is_a?(Array) && arg.length == contract.length
|
14
|
+
|
12
15
|
arg.zip(contract).all? do |_arg, _contract|
|
13
16
|
Contract.valid?(_arg, _contract)
|
14
17
|
end
|
@@ -19,6 +22,7 @@ module Contracts
|
|
19
22
|
Hash => lambda do |contract|
|
20
23
|
lambda do |arg|
|
21
24
|
return false unless arg.is_a?(Hash)
|
25
|
+
|
22
26
|
contract.keys.all? do |k|
|
23
27
|
Contract.valid?(arg[k], contract[k])
|
24
28
|
end
|
@@ -59,7 +63,7 @@ module Contracts
|
|
59
63
|
|
60
64
|
:default => lambda do |contract|
|
61
65
|
lambda { |arg| contract == arg }
|
62
|
-
end
|
66
|
+
end,
|
63
67
|
}.freeze
|
64
68
|
|
65
69
|
# Allows to override validator with custom one.
|
@@ -90,7 +94,7 @@ module Contracts
|
|
90
94
|
else
|
91
95
|
if contract.respond_to? :valid?
|
92
96
|
:valid
|
93
|
-
elsif
|
97
|
+
elsif [Class, Module].include?(klass)
|
94
98
|
:class
|
95
99
|
else
|
96
100
|
:default
|
data/lib/contracts/version.rb
CHANGED
@@ -376,10 +376,6 @@ RSpec.describe "Contracts:" do
|
|
376
376
|
fails { @o.hash_keywordargs(:hash => nil) }
|
377
377
|
fails { @o.hash_keywordargs(:hash => 1) }
|
378
378
|
end
|
379
|
-
|
380
|
-
it "should pass if a method is overloaded with non-KeywordArgs" do
|
381
|
-
passes { @o.person_keywordargs("name", 10) }
|
382
|
-
end
|
383
379
|
end
|
384
380
|
|
385
381
|
describe "Optional:" do
|
@@ -405,15 +401,15 @@ RSpec.describe "Contracts:" do
|
|
405
401
|
end
|
406
402
|
|
407
403
|
context "given a fulfilled contract" do
|
408
|
-
it { expect(@o.gives_max_value(:panda => 1, :bamboo => 2)).to eq(2) }
|
409
|
-
it { expect(@o.pretty_gives_max_value(:panda => 1, :bamboo => 2)).to eq(2) }
|
404
|
+
it { expect(@o.gives_max_value({ :panda => 1, :bamboo => 2 })).to eq(2) }
|
405
|
+
it { expect(@o.pretty_gives_max_value({ :panda => 1, :bamboo => 2 })).to eq(2) }
|
410
406
|
end
|
411
407
|
|
412
408
|
context "given an unfulfilled contract" do
|
413
|
-
it { fails { @o.gives_max_value(:panda => "1", :bamboo => "2") } }
|
409
|
+
it { fails { @o.gives_max_value({ :panda => "1", :bamboo => "2" }) } }
|
414
410
|
it { fails { @o.gives_max_value(nil) } }
|
415
411
|
it { fails { @o.gives_max_value(1) } }
|
416
|
-
it { fails { @o.pretty_gives_max_value(:panda => "1", :bamboo => "2") } }
|
412
|
+
it { fails { @o.pretty_gives_max_value({ :panda => "1", :bamboo => "2" }) } }
|
417
413
|
end
|
418
414
|
|
419
415
|
describe "#to_s" do
|
@@ -430,25 +426,25 @@ RSpec.describe "Contracts:" do
|
|
430
426
|
describe "StrictHash:" do
|
431
427
|
context "when given an exact correct input" do
|
432
428
|
it "does not raise an error" do
|
433
|
-
passes { @o.strict_person(:name => "calvin", :age => 10) }
|
429
|
+
passes { @o.strict_person({ :name => "calvin", :age => 10 }) }
|
434
430
|
end
|
435
431
|
end
|
436
432
|
|
437
433
|
context "when given an input with correct keys but wrong types" do
|
438
434
|
it "raises an error" do
|
439
|
-
fails { @o.strict_person(:name => "calvin", :age => "10") }
|
435
|
+
fails { @o.strict_person({ :name => "calvin", :age => "10" }) }
|
440
436
|
end
|
441
437
|
end
|
442
438
|
|
443
439
|
context "when given an input with missing keys" do
|
444
440
|
it "raises an error" do
|
445
|
-
fails { @o.strict_person(:name => "calvin") }
|
441
|
+
fails { @o.strict_person({ :name => "calvin" }) }
|
446
442
|
end
|
447
443
|
end
|
448
444
|
|
449
445
|
context "when given an input with extra keys" do
|
450
446
|
it "raises an error" do
|
451
|
-
fails { @o.strict_person(:name => "calvin", :age => 10, :soft => true) }
|
447
|
+
fails { @o.strict_person({ :name => "calvin", :age => 10, :soft => true }) }
|
452
448
|
end
|
453
449
|
end
|
454
450
|
|
data/spec/contracts_spec.rb
CHANGED
@@ -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
|
|
data/spec/fixtures/fixtures.rb
CHANGED
@@ -120,16 +120,11 @@ class GenericExample
|
|
120
120
|
end
|
121
121
|
|
122
122
|
Contract C::KeywordArgs[:name => String, :age => Fixnum] => nil
|
123
|
-
def person_keywordargs(
|
124
|
-
end
|
125
|
-
|
126
|
-
# Testing overloaded method
|
127
|
-
Contract String, Fixnum => nil
|
128
|
-
def person_keywordargs(name, age)
|
123
|
+
def person_keywordargs(name: "name", age: 10)
|
129
124
|
end
|
130
125
|
|
131
126
|
Contract C::KeywordArgs[:hash => C::HashOf[Symbol, C::Num]] => nil
|
132
|
-
def hash_keywordargs(
|
127
|
+
def hash_keywordargs(hash:)
|
133
128
|
end
|
134
129
|
|
135
130
|
Contract (/foo/) => nil
|
@@ -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,10 +1,10 @@
|
|
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
10
|
|
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: 2021-
|
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
|
@@ -121,7 +121,10 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
121
121
|
requirements:
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
124
|
+
version: '3.0'
|
125
|
+
- - "<"
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '4'
|
125
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
129
|
requirements:
|
127
130
|
- - ">="
|