contracts 0.16.1 → 0.17.1
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 +4 -4
- data/.github/workflows/code_style_checks.yaml +2 -2
- data/.github/workflows/tests.yaml +7 -13
- data/.rubocop.yml +34 -8
- data/.rubocop_todo.yml +7 -4
- data/CHANGELOG.markdown +2 -0
- data/Gemfile +8 -4
- data/README.md +10 -2
- data/Rakefile +2 -0
- data/TUTORIAL.md +14 -14
- data/contracts.gemspec +3 -4
- data/features/builtin_contracts/keyword_args_with_optional_positional_args.feature +76 -0
- data/features/builtin_contracts/none.feature +15 -9
- data/features/support/env.rb +2 -0
- data/lib/contracts/attrs.rb +2 -0
- data/lib/contracts/builtin_contracts.rb +43 -10
- data/lib/contracts/call_with.rb +52 -30
- data/lib/contracts/core.rb +3 -1
- data/lib/contracts/decorators.rb +5 -2
- 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/engine.rb +2 -0
- data/lib/contracts/errors.rb +3 -0
- data/lib/contracts/formatters.rb +17 -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/lib/contracts.rb +31 -39
- data/spec/builtin_contracts_spec.rb +9 -13
- data/spec/contracts_spec.rb +12 -9
- data/spec/fixtures/fixtures.rb +11 -16
- 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 +11 -9
@@ -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
|
|
@@ -93,9 +95,10 @@ module Contracts
|
|
93
95
|
|
94
96
|
# Takes a variable number of contracts.
|
95
97
|
# The contract passes if any of the contracts pass.
|
96
|
-
# Example: <tt>Or[
|
98
|
+
# Example: <tt>Or[Integer, Float]</tt>
|
97
99
|
class Or < CallableClass
|
98
100
|
def initialize(*vals)
|
101
|
+
super()
|
99
102
|
@vals = vals
|
100
103
|
end
|
101
104
|
|
@@ -107,17 +110,20 @@ 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
|
|
116
121
|
# Takes a variable number of contracts.
|
117
122
|
# The contract passes if exactly one of those contracts pass.
|
118
|
-
# Example: <tt>Xor[
|
123
|
+
# Example: <tt>Xor[Integer, Float]</tt>
|
119
124
|
class Xor < CallableClass
|
120
125
|
def initialize(*vals)
|
126
|
+
super()
|
121
127
|
@vals = vals
|
122
128
|
end
|
123
129
|
|
@@ -130,17 +136,20 @@ 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
|
|
139
147
|
# Takes a variable number of contracts.
|
140
148
|
# The contract passes if all contracts pass.
|
141
|
-
# Example: <tt>And[
|
149
|
+
# Example: <tt>And[Integer, 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,31 @@
|
|
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
|
-
|
14
|
-
|
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
|
15
29
|
|
16
30
|
# Loop forward validating the arguments up to the splat (if there is one)
|
17
31
|
(@args_contract_index || args.size).times do |i|
|
@@ -20,14 +34,16 @@ module Contracts
|
|
20
34
|
validator = @args_validators[i]
|
21
35
|
|
22
36
|
unless validator && validator[arg]
|
23
|
-
data = {
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
37
|
+
data = {
|
38
|
+
arg: arg,
|
39
|
+
contract: contract,
|
40
|
+
class: klass,
|
41
|
+
method: method,
|
42
|
+
contracts: self,
|
43
|
+
arg_pos: i+1,
|
44
|
+
total_args: args.size,
|
45
|
+
return_value: false,
|
46
|
+
}
|
31
47
|
return ParamContractError.new("as return value", data) if returns
|
32
48
|
return unless Contract.failure_callback(data)
|
33
49
|
end
|
@@ -57,14 +73,18 @@ module Contracts
|
|
57
73
|
validator = @args_validators[args_contracts.size - 1 - j]
|
58
74
|
|
59
75
|
unless validator && validator[arg]
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
76
|
+
# rubocop:disable Style/SoleNestedConditional
|
77
|
+
return unless Contract.failure_callback({
|
78
|
+
:arg => arg,
|
79
|
+
:contract => contract,
|
80
|
+
:class => klass,
|
81
|
+
:method => method,
|
82
|
+
:contracts => self,
|
83
|
+
:arg_pos => i - 1,
|
84
|
+
:total_args => args.size,
|
85
|
+
:return_value => false,
|
86
|
+
})
|
87
|
+
# rubocop:enable Style/SoleNestedConditional
|
68
88
|
end
|
69
89
|
|
70
90
|
if contract.is_a?(Contracts::Func)
|
@@ -78,22 +98,24 @@ module Contracts
|
|
78
98
|
args.slice!(-1) if blk || nil_block_appended
|
79
99
|
result = if method.respond_to?(:call)
|
80
100
|
# proc, block, lambda, etc
|
81
|
-
method.call(*args, &blk)
|
101
|
+
method.call(*args, **kargs, &blk)
|
82
102
|
else
|
83
103
|
# original method name reference
|
84
104
|
# Don't reassign blk, else Travis CI shows "stack level too deep".
|
85
105
|
target_blk = blk
|
86
|
-
target_blk = lambda { |*params| blk.call(*params) } if blk
|
87
|
-
method.send_to(this, *args, &target_blk)
|
106
|
+
target_blk = lambda { |*params| blk.call(*params) } if blk.is_a?(Contract)
|
107
|
+
method.send_to(this, *args, **kargs, &target_blk)
|
88
108
|
end
|
89
109
|
|
90
110
|
unless @ret_validator[result]
|
91
|
-
Contract.failure_callback(
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
111
|
+
Contract.failure_callback({
|
112
|
+
arg: result,
|
113
|
+
contract: ret_contract,
|
114
|
+
class: klass,
|
115
|
+
method: method,
|
116
|
+
contracts: self,
|
117
|
+
return_value: true,
|
118
|
+
})
|
97
119
|
end
|
98
120
|
|
99
121
|
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)
|
@@ -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/engine.rb
CHANGED
data/lib/contracts/errors.rb
CHANGED
data/lib/contracts/formatters.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Lint/RedundantRequireStatement
|
4
|
+
# If removed, spec/module_spec fails
|
1
5
|
require "pp"
|
6
|
+
# rubocop:enable Lint/RedundantRequireStatement
|
2
7
|
|
3
8
|
module Contracts
|
4
9
|
# A namespace for classes related to formatting.
|
@@ -7,18 +12,19 @@ module Contracts
|
|
7
12
|
class Expected
|
8
13
|
# @param full [Boolean] if false only unique `to_s` values will be output,
|
9
14
|
# non unique values become empty string.
|
10
|
-
def initialize(contract, full
|
15
|
+
def initialize(contract, full: true)
|
11
16
|
@contract, @full = contract, full
|
12
17
|
end
|
13
18
|
|
14
19
|
# Formats any type of Contract.
|
15
20
|
def contract(contract = @contract)
|
16
|
-
|
21
|
+
case contract
|
22
|
+
when Hash
|
17
23
|
hash_contract(contract)
|
18
|
-
|
24
|
+
when Array
|
19
25
|
array_contract(contract)
|
20
26
|
else
|
21
|
-
InspectWrapper.create(contract, @full)
|
27
|
+
InspectWrapper.create(contract, full: @full)
|
22
28
|
end
|
23
29
|
end
|
24
30
|
|
@@ -26,14 +32,14 @@ module Contracts
|
|
26
32
|
def hash_contract(hash)
|
27
33
|
@full = true # Complex values output completely, overriding @full
|
28
34
|
hash.inject({}) do |repr, (k, v)|
|
29
|
-
repr.merge(k => InspectWrapper.create(contract(v), @full))
|
35
|
+
repr.merge(k => InspectWrapper.create(contract(v), full: @full))
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
39
|
# Formats Array contracts.
|
34
40
|
def array_contract(array)
|
35
41
|
@full = true
|
36
|
-
array.map { |v| InspectWrapper.create(contract(v), @full) }
|
42
|
+
array.map { |v| InspectWrapper.create(contract(v), full: @full) }
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
@@ -42,8 +48,8 @@ module Contracts
|
|
42
48
|
module InspectWrapper
|
43
49
|
# InspectWrapper is a factory, will never be an instance
|
44
50
|
# @return [ClassInspectWrapper, ObjectInspectWrapper]
|
45
|
-
def self.create(value, full
|
46
|
-
if value.
|
51
|
+
def self.create(value, full: true)
|
52
|
+
if value.instance_of?(Class)
|
47
53
|
ClassInspectWrapper
|
48
54
|
else
|
49
55
|
ObjectInspectWrapper
|
@@ -66,6 +72,7 @@ module Contracts
|
|
66
72
|
return @value.inspect if empty_val?
|
67
73
|
return @value.to_s if plain?
|
68
74
|
return delim(@value.to_s) if useful_to_s?
|
75
|
+
|
69
76
|
useful_inspect
|
70
77
|
end
|
71
78
|
|
@@ -96,7 +103,7 @@ module Contracts
|
|
96
103
|
end
|
97
104
|
|
98
105
|
def useful_to_s?
|
99
|
-
# Useless to_s value or no custom to_s
|
106
|
+
# Useless to_s value or no custom to_s behaviour defined
|
100
107
|
!empty_to_s? && custom_to_s?
|
101
108
|
end
|
102
109
|
|
@@ -125,7 +132,7 @@ module Contracts
|
|
125
132
|
include InspectWrapper
|
126
133
|
|
127
134
|
def custom_to_s?
|
128
|
-
!@value.to_s.match(
|
135
|
+
!@value.to_s.match(/#<\w+:.+>/)
|
129
136
|
end
|
130
137
|
|
131
138
|
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)
|