blood_contracts-core 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/blood_contracts-core.gemspec +1 -1
- data/lib/blood_contracts/core.rb +1 -0
- data/lib/blood_contracts/core/contract_failure.rb +11 -0
- data/lib/blood_contracts/core/refined.rb +1 -1
- data/lib/blood_contracts/core/sum.rb +21 -2
- data/lib/blood_contracts/core/tuple.rb +13 -3
- data/lib/blood_contracts/core/tuple_contract_failure.rb +8 -0
- data/spec/blood_contracts/core_spec.rb +38 -25
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf9fc4121fbf8d1f0b7ccbef5feea8337bddaffe58035359a0eb8ff0617337c9
|
4
|
+
data.tar.gz: e697e1e0c1ef8fd849a243e083b45a1dc0ea18a9998feb3fd63c4d709b220b12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12cad0640c0a2eab92e5189ff7a658bf0e93b055e0ef4fac15e16c3d6083a9e4463673d1be916a73b9e32b626c7c2e98d0f4e0af34164081296c0a7114284fea
|
7
|
+
data.tar.gz: 251436c1bbef4e1736140f0dd01625ede3ab08f689ad0af0f91ad1e32a6dd7758720ba8a5e11cefed729915bf3c98fac8692c34f8613eb9bd60d7f6983876a34
|
data/CHANGELOG.md
CHANGED
@@ -5,7 +5,15 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
7
7
|
|
8
|
-
##
|
8
|
+
## [0.4.3] - [2019-08-30]
|
9
|
+
|
10
|
+
Features/Fixes:
|
11
|
+
- Changed the way we treat context in Sum and Tuple. Each validation of those aggregations now uses its own context.
|
12
|
+
In other word, each validation path has its own context, which will not be corrupted in parallel validaions.
|
13
|
+
If validation succeded we return the only valid context, otherwise if we failed on Tuple or Sum we return set of
|
14
|
+
contexts, to inspect why did validation fail.
|
15
|
+
|
16
|
+
## [0.4.2] - [2019-08-29]
|
9
17
|
|
10
18
|
Features:
|
11
19
|
|
data/lib/blood_contracts/core.rb
CHANGED
@@ -8,6 +8,7 @@ module BloodContracts
|
|
8
8
|
require_relative "./core/pipe.rb"
|
9
9
|
require_relative "./core/contract.rb"
|
10
10
|
require_relative "./core/sum.rb"
|
11
|
+
require_relative "./core/sum_contract_failure.rb"
|
11
12
|
require_relative "./core/tuple.rb"
|
12
13
|
require_relative "./core/tuple_contract_failure.rb"
|
13
14
|
|
@@ -13,6 +13,17 @@ module BloodContracts::Core
|
|
13
13
|
@context[:errors] = (@context[:errors].to_a << @value.to_h)
|
14
14
|
end
|
15
15
|
|
16
|
+
# Merge errors with the errors of another ContractFailure
|
17
|
+
#
|
18
|
+
# @param contract_failure [ContractFailure] other errors container which to
|
19
|
+
# merge with
|
20
|
+
# @return [ContractFailure]
|
21
|
+
#
|
22
|
+
def merge!(contract_failure)
|
23
|
+
@context[:errors] = @context[:errors].to_a + contract_failure.errors
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
16
27
|
# List of errors per type after the data matching process
|
17
28
|
#
|
18
29
|
# @return [Array<Hash<BC::Refined, String>>]
|
@@ -41,7 +41,7 @@ module BloodContracts::Core
|
|
41
41
|
|
42
42
|
# Override of case equality operator, to handle Tuple correctly
|
43
43
|
def ===(object)
|
44
|
-
return object.
|
44
|
+
return object.attributes.values.any?(self) if object.is_a?(Tuple)
|
45
45
|
super
|
46
46
|
end
|
47
47
|
|
@@ -63,6 +63,15 @@ module BloodContracts::Core
|
|
63
63
|
return super if name
|
64
64
|
"Sum(#{sum_of.map(&:inspect).join(',')})"
|
65
65
|
end
|
66
|
+
|
67
|
+
# Inheritance handler:
|
68
|
+
# - adds current sum_of to child
|
69
|
+
# - sets default value for failure_klass
|
70
|
+
def inherited(new_klass)
|
71
|
+
new_klass.instance_variable_set(:@sum_of, sum_of)
|
72
|
+
new_klass.failure_klass ||= SumContractFailure
|
73
|
+
super
|
74
|
+
end
|
66
75
|
end
|
67
76
|
|
68
77
|
# The type which is the result of data matching process
|
@@ -72,13 +81,13 @@ module BloodContracts::Core
|
|
72
81
|
#
|
73
82
|
def match
|
74
83
|
@or_matches = self.class.sum_of.map do |type|
|
75
|
-
type.match(@value, context: @context)
|
84
|
+
type.match(@value, context: @context.dup)
|
76
85
|
end
|
77
86
|
|
78
87
|
if (match = @or_matches.find(&:valid?))
|
79
88
|
match
|
80
89
|
else
|
81
|
-
failure(:
|
90
|
+
@or_matches.dup.unshift(failure).reduce(:merge!)
|
82
91
|
end
|
83
92
|
end
|
84
93
|
|
@@ -90,6 +99,16 @@ module BloodContracts::Core
|
|
90
99
|
@context[:errors]
|
91
100
|
end
|
92
101
|
|
102
|
+
# Helper to build a ContractFailure with shared context
|
103
|
+
#
|
104
|
+
# @return [ContractFailure]
|
105
|
+
#
|
106
|
+
private def failure(*)
|
107
|
+
sum_context = @or_matches.map(&:context)
|
108
|
+
@context[:sum_failure_contexts] = sum_context
|
109
|
+
self.class.failure_klass.new(context: @context)
|
110
|
+
end
|
111
|
+
|
93
112
|
# @private
|
94
113
|
private def inspect
|
95
114
|
"#<sum #{self.class.name} is #{self.class.sum_of.to_a.join(' or ')}"\
|
@@ -143,10 +143,12 @@ module BloodContracts::Core
|
|
143
143
|
def match
|
144
144
|
@matches = attributes_enumerator.map do |(type, value), index|
|
145
145
|
attribute_name = self.class.names[index]
|
146
|
-
attributes.store(
|
146
|
+
attributes.store(
|
147
|
+
attribute_name, type.match(value, context: @context.dup)
|
148
|
+
)
|
147
149
|
end
|
148
|
-
return if @matches.
|
149
|
-
failure(:
|
150
|
+
return if (failures = @matches.select(&:invalid?)).empty?
|
151
|
+
failures.unshift(failure).reduce(:merge!)
|
150
152
|
end
|
151
153
|
|
152
154
|
# Turns match into array of unpacked values
|
@@ -190,6 +192,14 @@ module BloodContracts::Core
|
|
190
192
|
{}
|
191
193
|
end
|
192
194
|
|
195
|
+
# Helper to build a ContractFailure with shared context
|
196
|
+
#
|
197
|
+
# @return [ContractFailure]
|
198
|
+
#
|
199
|
+
private def failure(*)
|
200
|
+
self.class.failure_klass.new(context: @context)
|
201
|
+
end
|
202
|
+
|
193
203
|
# @private
|
194
204
|
private def parse_values_from_context(context)
|
195
205
|
return if context.empty?
|
@@ -17,6 +17,14 @@ module BloodContracts::Core
|
|
17
17
|
attributes.select { |_name, type| type.invalid? }
|
18
18
|
end
|
19
19
|
|
20
|
+
# Contexts for subset of attributes which are invalid
|
21
|
+
#
|
22
|
+
# @return [Hash<String, ContractFailure>]
|
23
|
+
#
|
24
|
+
def attribute_contexts
|
25
|
+
attribute_errors.transform_values!(&:context)
|
26
|
+
end
|
27
|
+
|
20
28
|
# Unpacked matching errors in form of a hash per attribute
|
21
29
|
#
|
22
30
|
# @return [Hash<String, ContractFailure>]
|
@@ -108,12 +108,8 @@ RSpec.describe BloodContracts::Core do
|
|
108
108
|
context "when login is valid" do
|
109
109
|
context "when login is email" do
|
110
110
|
let(:login) { "admin@example.com" }
|
111
|
-
let(:errors) { [{ Test::Phone => ["Not a phone"] }] }
|
112
111
|
let(:validation_context) do
|
113
112
|
hash_including(
|
114
|
-
phone: login,
|
115
|
-
clean_phone: login,
|
116
|
-
errors: errors,
|
117
113
|
email: login
|
118
114
|
)
|
119
115
|
end
|
@@ -129,13 +125,10 @@ RSpec.describe BloodContracts::Core do
|
|
129
125
|
context "when login is phone" do
|
130
126
|
let(:login) { "8(800) 200 - 11 - 00" }
|
131
127
|
let(:cleaned_phone) { "88002001100" }
|
132
|
-
let(:errors) { [{ Test::Email => ["Not an email"] }] }
|
133
128
|
let(:validation_context) do
|
134
129
|
hash_including(
|
135
130
|
phone: login,
|
136
|
-
clean_phone: cleaned_phone
|
137
|
-
errors: errors,
|
138
|
-
email: login
|
131
|
+
clean_phone: cleaned_phone
|
139
132
|
)
|
140
133
|
end
|
141
134
|
|
@@ -153,15 +146,13 @@ RSpec.describe BloodContracts::Core do
|
|
153
146
|
let(:errors) do
|
154
147
|
[
|
155
148
|
{ Test::Email => ["Not an email"] },
|
156
|
-
{ Test::Phone => ["Not a phone"] }
|
157
|
-
{ Test::Login => [:no_matches] }
|
149
|
+
{ Test::Phone => ["Not a phone"] }
|
158
150
|
]
|
159
151
|
end
|
160
152
|
|
161
153
|
it do
|
162
154
|
is_expected.to be_invalid
|
163
155
|
expect(subject.errors).to match_array(errors)
|
164
|
-
expect(subject.unpack).to match({ Test::Login => [:no_matches] })
|
165
156
|
end
|
166
157
|
end
|
167
158
|
end
|
@@ -254,15 +245,12 @@ RSpec.describe BloodContracts::Core do
|
|
254
245
|
attribute_errors.merge(password: kind_of(dynamic_password_type))
|
255
246
|
end
|
256
247
|
let(:attribute_errors) { { email: kind_of(BC::ContractFailure) } }
|
257
|
-
let(:tuple_invalid) do
|
258
|
-
{ Test::InlineRegistrationInput => [:invalid_tuple] }
|
259
|
-
end
|
260
248
|
|
261
249
|
it do
|
262
250
|
expect(subject).to be_invalid
|
263
251
|
expect(subject.attributes).to match(attributes)
|
264
252
|
expect(subject.to_h).to match(email: email_error)
|
265
|
-
expect(subject.errors).to match_array([email_error
|
253
|
+
expect(subject.errors).to match_array([email_error])
|
266
254
|
expect(subject.attribute_errors).to match(attribute_errors)
|
267
255
|
end
|
268
256
|
end
|
@@ -295,13 +283,12 @@ RSpec.describe BloodContracts::Core do
|
|
295
283
|
attribute_errors.merge(password: kind_of(Test::Ascii))
|
296
284
|
end
|
297
285
|
let(:attribute_errors) { { email: kind_of(BC::ContractFailure) } }
|
298
|
-
let(:tuple_invalid) { { Test::RegistrationInput => [:invalid_tuple] } }
|
299
286
|
|
300
287
|
it do
|
301
288
|
expect(subject).to be_invalid
|
302
289
|
expect(subject.attributes).to match(attributes)
|
303
290
|
expect(subject.to_h).to match(email: email_error)
|
304
|
-
expect(subject.errors).to match_array([email_error
|
291
|
+
expect(subject.errors).to match_array([email_error])
|
305
292
|
expect(subject.attribute_errors).to match(attribute_errors)
|
306
293
|
end
|
307
294
|
end
|
@@ -347,19 +334,22 @@ RSpec.describe BloodContracts::Core do
|
|
347
334
|
context "when value is valid JSON" do
|
348
335
|
context "when value is invalid registration data" do
|
349
336
|
let(:response) { '{"phone":"+78889992211"}' }
|
350
|
-
let(:error)
|
351
|
-
let(:errors) do
|
337
|
+
let(:error) do
|
352
338
|
[
|
353
339
|
{ Test::Email => ["Not an email"] },
|
354
340
|
{ Test::Phone => ["Not a phone"] },
|
355
|
-
{ Test::Ascii => ["Not ASCII"] }
|
356
|
-
{ Test::RegistrationInput => [:invalid_tuple] }
|
341
|
+
{ Test::Ascii => ["Not ASCII"] }
|
357
342
|
]
|
358
343
|
end
|
359
|
-
let(:
|
344
|
+
let(:password_errors) do
|
345
|
+
[
|
346
|
+
{ Test::Ascii => ["Not ASCII"] }
|
347
|
+
]
|
348
|
+
end
|
349
|
+
let(:password_context) do
|
360
350
|
hash_including(
|
361
351
|
raw_value: response,
|
362
|
-
errors: array_including(
|
352
|
+
errors: array_including(password_errors),
|
363
353
|
steps: ["Test::Json", "BloodContracts::Core::TupleContractFailure"],
|
364
354
|
steps_values: {
|
365
355
|
parse: response,
|
@@ -367,12 +357,35 @@ RSpec.describe BloodContracts::Core do
|
|
367
357
|
}
|
368
358
|
)
|
369
359
|
end
|
360
|
+
let(:login_errors) do
|
361
|
+
[
|
362
|
+
{ Test::Email => ["Not an email"] },
|
363
|
+
{ Test::Phone => ["Not a phone"] }
|
364
|
+
]
|
365
|
+
end
|
366
|
+
let(:login_context) do
|
367
|
+
hash_including(
|
368
|
+
raw_value: response,
|
369
|
+
errors: array_including(login_errors),
|
370
|
+
steps: ["Test::Json", "BloodContracts::Core::TupleContractFailure"],
|
371
|
+
steps_values: {
|
372
|
+
parse: response,
|
373
|
+
validate: { phone: "+78889992211" }
|
374
|
+
}
|
375
|
+
)
|
376
|
+
end
|
377
|
+
let(:validation_context) do
|
378
|
+
{
|
379
|
+
login: login_context,
|
380
|
+
password: password_context
|
381
|
+
}
|
382
|
+
end
|
370
383
|
|
371
384
|
it do
|
372
385
|
is_expected.to be_invalid
|
373
386
|
is_expected.to be_kind_of(BC::TupleContractFailure)
|
374
|
-
expect(subject.
|
375
|
-
expect(subject.
|
387
|
+
expect(subject.attribute_contexts).to match(validation_context)
|
388
|
+
expect(subject.errors).to match(error)
|
376
389
|
end
|
377
390
|
end
|
378
391
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blood_contracts-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergey Dolganov (sclinede)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|