blood_contracts-core 0.4.2 → 0.4.3
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/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
|