parametric 0.2.17 → 0.2.18
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/README.md +133 -36
- data/lib/parametric/field.rb +26 -22
- data/lib/parametric/policy_adapter.rb +57 -0
- data/lib/parametric/tagged_one_of.rb +134 -0
- data/lib/parametric/version.rb +1 -1
- data/parametric.gemspec +1 -1
- data/spec/schema_spec.rb +58 -0
- data/spec/struct_spec.rb +5 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 403aac3495b3e90bc32cf01c0229b83497d37413fb43b6180c3dafd0a9ec36b9
|
4
|
+
data.tar.gz: a8396d8ab3831e7c93d1643f96562ff0d5fe1ddaa8794dfe7ed8993bd4732cd5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbd04349edf7e8f4777ef7c681ec8f16d2cf570d915088fae04942760415d9fd88123741b68957839b21d7145d97b83b6cf6911fce5f0e2db806d34a304e1a0d
|
7
|
+
data.tar.gz: 9851e0d8f4e5e48d38870027c8a987fbf65f264a8c9dfaafe74080ff463e3783267f605eae09ce65b1f43547a27935ae532fd75120d60823fd24e8bf09bd0c4d
|
data/README.md
CHANGED
@@ -127,6 +127,60 @@ person_schema = Parametric::Schema.new do |sc, options|
|
|
127
127
|
sc.field(:friends).type(:array).schema(friends_schema)
|
128
128
|
end
|
129
129
|
```
|
130
|
+
|
131
|
+
## Tagged One Of (multiple nested schemas, discriminated by payload key).
|
132
|
+
|
133
|
+
You can use `Field#tagged_one_of` to resolve a nested schema based on the value of a top-level field.
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
user_schema = Parametric::Schema.new do |sc, _|
|
137
|
+
field(:name).type(:string).present
|
138
|
+
field(:age).type(:integer).present
|
139
|
+
end
|
140
|
+
|
141
|
+
company_schema = Parametric::Schema.new do
|
142
|
+
field(:name).type(:string).present
|
143
|
+
field(:company_code).type(:string).present
|
144
|
+
end
|
145
|
+
|
146
|
+
schema = Parametric::Schema.new do |sc, _|
|
147
|
+
# Use :type field to locate the sub-schema to use for :sub
|
148
|
+
sc.field(:type).type(:string)
|
149
|
+
|
150
|
+
# Use the :one_of policy to select the sub-schema based on the :type field above
|
151
|
+
sc.field(:sub).type(:object).tagged_one_of do |sub|
|
152
|
+
sub.index_by(:type)
|
153
|
+
sub.on('user', user_schema)
|
154
|
+
sub.on('company', company_schema)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# The schema will now select the correct sub-schema based on the value of :type
|
159
|
+
result = schema.resolve(type: 'user', sub: { name: 'Joe', age: 30 })
|
160
|
+
|
161
|
+
# Instances can also be created separately and used as a policy:
|
162
|
+
|
163
|
+
UserOrCompany = Parametric::TaggedOneOf.new do |sc, _|
|
164
|
+
sc.on('user', user_schema)
|
165
|
+
sc.on('company', company_schema)
|
166
|
+
end
|
167
|
+
|
168
|
+
schema = Parametric::Schema.new do |sc, _|
|
169
|
+
sc.field(:type).type(:string)
|
170
|
+
sc.field(:sub).type(:object).policy(UserOrCompany.index_by(:type))
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
`#index_by` can take a block to decide what value to resolve schemas by:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
sc.field(:sub).type(:object).tagged_one_of do |sub|
|
178
|
+
sub.index_by { |payload| payload[:entity_type] }
|
179
|
+
sub.on('user', user_schema)
|
180
|
+
sub.on('company', company_schema)
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
130
184
|
## Built-in policies
|
131
185
|
|
132
186
|
Type coercions (the `type` method) and validations (the `validate` method) are all _policies_.
|
@@ -285,60 +339,96 @@ field(:currency).policy(:value, 'gbp') # this field always resolves to 'gbp'
|
|
285
339
|
|
286
340
|
## Custom policies
|
287
341
|
|
288
|
-
You can also register your own custom policy objects.
|
342
|
+
You can also register your own custom policy objects.
|
343
|
+
A policy consist of the following:
|
344
|
+
|
345
|
+
* A `PolicyFactory` interface:
|
289
346
|
|
290
347
|
```ruby
|
291
348
|
class MyPolicy
|
292
|
-
#
|
293
|
-
|
294
|
-
|
349
|
+
# Initializer signature is up to you.
|
350
|
+
# These are the arguments passed to the policy when using in a Field,
|
351
|
+
# ex. field(:name).policy(:my_policy, 'arg1', 'arg2')
|
352
|
+
def initialize(arg1, arg2)
|
353
|
+
@arg1, @arg2 = arg1, arg2
|
295
354
|
end
|
296
355
|
|
297
|
-
#
|
298
|
-
|
299
|
-
|
300
|
-
true
|
356
|
+
# @return [Hash]
|
357
|
+
def meta_data
|
358
|
+
{ type: :string }
|
301
359
|
end
|
302
360
|
|
303
|
-
#
|
304
|
-
|
305
|
-
|
361
|
+
# Buld a Policy Runner, which is instantiated
|
362
|
+
# for each field when resolving a schema
|
363
|
+
# @param key [Symbol]
|
364
|
+
# @param value [Any]
|
365
|
+
# @option payload [Hash]
|
366
|
+
# @option context [Parametric::Context]
|
367
|
+
# @return [PolicyRunner]
|
368
|
+
def build(key, value, payload:, context:)
|
369
|
+
MyPolicyRunner.new(key, value, payload, context)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
```
|
373
|
+
|
374
|
+
* A `PolicyRunner` interface.
|
375
|
+
|
376
|
+
```ruby
|
377
|
+
class MyPolicyRunner
|
378
|
+
# Initializer is up to you. See `MyPolicy#build`
|
379
|
+
def initialize(key, value, payload, context)
|
380
|
+
|
306
381
|
end
|
307
382
|
|
308
|
-
#
|
309
|
-
|
383
|
+
# Should this policy run at all?
|
384
|
+
# returning [false] halts the field policy chain.
|
385
|
+
# @return [Boolean]
|
386
|
+
def eligible?
|
310
387
|
true
|
311
388
|
end
|
312
389
|
|
313
|
-
#
|
314
|
-
|
315
|
-
|
390
|
+
# If [false], add [#message] to result errors and halt processing field.
|
391
|
+
# @return [Boolean]
|
392
|
+
def valid?
|
393
|
+
true
|
394
|
+
end
|
395
|
+
|
396
|
+
# Coerce the value, or return as-is.
|
397
|
+
# @return [Any]
|
398
|
+
def value
|
399
|
+
@value
|
400
|
+
end
|
401
|
+
|
402
|
+
# Error message for this policy
|
403
|
+
# @return [String]
|
404
|
+
def message
|
405
|
+
"#{@value} is invalid"
|
316
406
|
end
|
317
407
|
end
|
318
408
|
```
|
319
409
|
|
320
|
-
|
410
|
+
Then register your custom policy factory:
|
321
411
|
|
322
412
|
```ruby
|
323
|
-
Parametric.policy :
|
413
|
+
Parametric.policy :my_polict, MyPolicy
|
324
414
|
```
|
325
415
|
|
326
416
|
And then refer to it by name when declaring your schema fields
|
327
417
|
|
328
418
|
```ruby
|
329
|
-
field(:title).policy(:my_policy)
|
419
|
+
field(:title).policy(:my_policy, 'arg1', 'arg2')
|
330
420
|
```
|
331
421
|
|
332
422
|
You can chain custom policies with other policies.
|
333
423
|
|
334
424
|
```ruby
|
335
|
-
field(:title).required.policy(:my_policy)
|
425
|
+
field(:title).required.policy(:my_policy, 'arg1', 'arg2')
|
336
426
|
```
|
337
427
|
|
338
428
|
Note that you can also register instances.
|
339
429
|
|
340
430
|
```ruby
|
341
|
-
Parametric.policy :my_policy, MyPolicy.new
|
431
|
+
Parametric.policy :my_policy, MyPolicy.new('arg1', 'arg2')
|
342
432
|
```
|
343
433
|
|
344
434
|
For example, a policy that can be configured on a field-by-field basis:
|
@@ -349,27 +439,34 @@ class AddJobTitle
|
|
349
439
|
@job_title = job_title
|
350
440
|
end
|
351
441
|
|
352
|
-
def
|
353
|
-
|
442
|
+
def build(key, value, payload:, context:)
|
443
|
+
Runner.new(@job_title, key, value, payload, context)
|
354
444
|
end
|
355
445
|
|
356
|
-
|
357
|
-
|
358
|
-
true
|
446
|
+
def meta_data
|
447
|
+
{}
|
359
448
|
end
|
360
449
|
|
361
|
-
|
362
|
-
|
363
|
-
"#{value}, #{@job_title}"
|
364
|
-
end
|
450
|
+
class Runner
|
451
|
+
attr_reader :message
|
365
452
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
453
|
+
def initialize(job_title, key, value, payload, _context)
|
454
|
+
@job_title = job_title
|
455
|
+
@key, @value, @payload = key, value, payload
|
456
|
+
@message = 'is invalid'
|
457
|
+
end
|
370
458
|
|
371
|
-
|
372
|
-
|
459
|
+
def eligible?
|
460
|
+
true
|
461
|
+
end
|
462
|
+
|
463
|
+
def valid?
|
464
|
+
true
|
465
|
+
end
|
466
|
+
|
467
|
+
def value
|
468
|
+
"#{@value}, #{@job_title}"
|
469
|
+
end
|
373
470
|
end
|
374
471
|
end
|
375
472
|
|
data/lib/parametric/field.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'delegate'
|
4
|
-
require
|
4
|
+
require 'parametric/field_dsl'
|
5
|
+
require 'parametric/policy_adapter'
|
6
|
+
require 'parametric/tagged_one_of'
|
5
7
|
|
6
8
|
module Parametric
|
7
9
|
class ConfigurationError < StandardError; end
|
@@ -40,6 +42,10 @@ module Parametric
|
|
40
42
|
end
|
41
43
|
alias_method :type, :policy
|
42
44
|
|
45
|
+
def tagged_one_of(instance = nil, &block)
|
46
|
+
policy(instance || Parametric::TaggedOneOf.new(&block))
|
47
|
+
end
|
48
|
+
|
43
49
|
def schema(sc = nil, &block)
|
44
50
|
sc = (sc ? sc : Schema.new(&block))
|
45
51
|
meta schema: sc
|
@@ -79,20 +85,26 @@ module Parametric
|
|
79
85
|
end
|
80
86
|
|
81
87
|
policies.each do |policy|
|
82
|
-
|
83
|
-
|
84
|
-
if
|
85
|
-
eligible =
|
86
|
-
|
88
|
+
begin
|
89
|
+
pol = policy.build(key, value, payload:, context:)
|
90
|
+
if !pol.eligible?
|
91
|
+
eligible = false
|
92
|
+
if has_default?
|
93
|
+
eligible = true
|
94
|
+
value = default_block.call(key, payload, context)
|
95
|
+
end
|
96
|
+
break
|
97
|
+
else
|
98
|
+
value = pol.value
|
99
|
+
if !pol.valid?
|
100
|
+
eligible = true # eligible, but has errors
|
101
|
+
context.add_error pol.message
|
102
|
+
break # only one error at a time
|
103
|
+
end
|
87
104
|
end
|
105
|
+
rescue StandardError => e
|
106
|
+
context.add_error e.message
|
88
107
|
break
|
89
|
-
else
|
90
|
-
value = resolve_one(policy, value, context)
|
91
|
-
if !policy.valid?(value, key, payload)
|
92
|
-
eligible = true # eligible, but has errors
|
93
|
-
context.add_error policy.message
|
94
|
-
break # only one error at a time
|
95
|
-
end
|
96
108
|
end
|
97
109
|
end
|
98
110
|
|
@@ -107,15 +119,6 @@ module Parametric
|
|
107
119
|
|
108
120
|
attr_reader :registry, :default_block
|
109
121
|
|
110
|
-
def resolve_one(policy, value, context)
|
111
|
-
begin
|
112
|
-
policy.coerce(value, key, context)
|
113
|
-
rescue StandardError => e
|
114
|
-
context.add_error e.message
|
115
|
-
value
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
122
|
def has_default?
|
120
123
|
!!default_block && !meta_data[:skip_default]
|
121
124
|
end
|
@@ -127,6 +130,7 @@ module Parametric
|
|
127
130
|
|
128
131
|
obj = obj.new(*args) if obj.respond_to?(:new)
|
129
132
|
obj = PolicyWithKey.new(obj, key)
|
133
|
+
obj = PolicyAdapter.new(obj) unless obj.respond_to?(:build)
|
130
134
|
|
131
135
|
obj
|
132
136
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Parametric
|
4
|
+
# Adapt legacy policies to the new policy interface
|
5
|
+
class PolicyAdapter
|
6
|
+
class PolicyRunner
|
7
|
+
def initialize(policy, key, value, payload, context)
|
8
|
+
@policy, @key, @raw_value, @payload, @context = policy, key, value, payload, context
|
9
|
+
end
|
10
|
+
|
11
|
+
# The PolicyRunner interface
|
12
|
+
# @return [Boolean]
|
13
|
+
def eligible?
|
14
|
+
@policy.eligible?(@raw_value, @key, @payload)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Boolean]
|
18
|
+
def valid?
|
19
|
+
@policy.valid?(value, @key, @payload)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Any]
|
23
|
+
def value
|
24
|
+
@value ||= @policy.coerce(@raw_value, @key, @context)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String]
|
28
|
+
def message
|
29
|
+
@policy.message
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(policy)
|
34
|
+
@policy = policy
|
35
|
+
end
|
36
|
+
|
37
|
+
# The PolicyFactory interface
|
38
|
+
# Buld a Policy Runner, which is instantiated
|
39
|
+
# for each field when resolving a schema
|
40
|
+
# @param key [Symbol]
|
41
|
+
# @param value [Any]
|
42
|
+
# @option payload [Hash]
|
43
|
+
# @option context [Parametric::Context]
|
44
|
+
# @return [PolicyRunner]
|
45
|
+
def build(key, value, payload:, context:)
|
46
|
+
PolicyRunner.new(@policy, key, value, payload, context)
|
47
|
+
end
|
48
|
+
|
49
|
+
def meta_data
|
50
|
+
@policy.meta_data
|
51
|
+
end
|
52
|
+
|
53
|
+
def key
|
54
|
+
@policy.key
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Parametric
|
4
|
+
# A policy that allows you to select a sub-schema based on a value in the payload.
|
5
|
+
# @example
|
6
|
+
#
|
7
|
+
# user_schema = Parametric::Schema.new do |sc, _|
|
8
|
+
# field(:name).type(:string).present
|
9
|
+
# field(:age).type(:integer).present
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# company_schema = Parametric::Schema.new do
|
13
|
+
# field(:name).type(:string).present
|
14
|
+
# field(:company_code).type(:string).present
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# schema = Parametric::Schema.new do |sc, _|
|
18
|
+
# # Use :type field to locate the sub-schema to use for :sub
|
19
|
+
# sc.field(:type).type(:string)
|
20
|
+
#
|
21
|
+
# # Use the :one_of policy to select the sub-schema based on the :type field above
|
22
|
+
# sc.field(:sub).type(:object).tagged_one_of do |sub|
|
23
|
+
# sub.index_by(:type)
|
24
|
+
# sub.on('user', user_schema)
|
25
|
+
# sub.on('company', company_schema)
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # The schema will now select the correct sub-schema based on the value of :type
|
30
|
+
# result = schema.resolve(type: 'user', sub: { name: 'Joe', age: 30 })
|
31
|
+
#
|
32
|
+
# Instances can also be created separately and used as a policy:
|
33
|
+
# @example
|
34
|
+
#
|
35
|
+
# UserOrCompany = Parametric::TaggedOneOf.new do |sc, _|
|
36
|
+
# sc.on('user', user_schema)
|
37
|
+
# sc.on('company', company_schema)
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# schema = Parametric::Schema.new do |sc, _|
|
41
|
+
# sc.field(:type).type(:string)
|
42
|
+
# sc.field(:sub).type(:object).policy(UserOrCompany.index_by(:type))
|
43
|
+
# end
|
44
|
+
class TaggedOneOf
|
45
|
+
NOOP_INDEX = ->(payload) { payload }.freeze
|
46
|
+
def initialize(index: NOOP_INDEX, matchers: {}, &block)
|
47
|
+
@index = index
|
48
|
+
@matchers = matchers
|
49
|
+
@configuring = false
|
50
|
+
if block_given?
|
51
|
+
@configuring = true
|
52
|
+
block.call(self)
|
53
|
+
@configuring = false
|
54
|
+
end
|
55
|
+
freeze
|
56
|
+
end
|
57
|
+
|
58
|
+
def index_by(callable = nil, &block)
|
59
|
+
if callable.is_a?(Symbol)
|
60
|
+
key = callable
|
61
|
+
callable = ->(payload) { payload[key] }
|
62
|
+
end
|
63
|
+
index = callable || block
|
64
|
+
if configuring?
|
65
|
+
@index = index
|
66
|
+
else
|
67
|
+
self.class.new(index:, matchers: @matchers)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def on(key, schema)
|
72
|
+
@matchers[key] = schema
|
73
|
+
end
|
74
|
+
|
75
|
+
# The [PolicyFactory] interface
|
76
|
+
def build(key, value, payload:, context:)
|
77
|
+
Runner.new(@index, @matchers, key, value, payload, context)
|
78
|
+
end
|
79
|
+
|
80
|
+
def meta_data
|
81
|
+
{ type: :object, one_of: @matchers }
|
82
|
+
end
|
83
|
+
|
84
|
+
private def configuring?
|
85
|
+
@configuring
|
86
|
+
end
|
87
|
+
|
88
|
+
class Runner
|
89
|
+
def initialize(index, matchers, key, value, payload, context)
|
90
|
+
@matchers = matchers
|
91
|
+
@key = key
|
92
|
+
@raw_value = value
|
93
|
+
@payload = payload
|
94
|
+
@context = context
|
95
|
+
@index_value = index.call(payload)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Should this policy run at all?
|
99
|
+
# returning [false] halts the field policy chain.
|
100
|
+
# @return [Boolean]
|
101
|
+
def eligible?
|
102
|
+
true
|
103
|
+
end
|
104
|
+
|
105
|
+
# If [false], add [#message] to result errors and halt processing field.
|
106
|
+
# @return [Boolean]
|
107
|
+
def valid?
|
108
|
+
has_sub_schema?
|
109
|
+
end
|
110
|
+
|
111
|
+
# Coerce the value, or return as-is.
|
112
|
+
# @return [Any]
|
113
|
+
def value
|
114
|
+
@value ||= has_sub_schema? ? sub_schema.coerce(@raw_value, @key, @context) : @raw_value
|
115
|
+
end
|
116
|
+
|
117
|
+
# Error message for this policy
|
118
|
+
# @return [String]
|
119
|
+
def message
|
120
|
+
"#{@value} is invalid. No sub-schema found for '#{@index_value}'"
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def has_sub_schema?
|
126
|
+
@matchers.key?(@index_value)
|
127
|
+
end
|
128
|
+
|
129
|
+
def sub_schema
|
130
|
+
@sub_schema ||= @matchers[@index_value]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/lib/parametric/version.rb
CHANGED
data/parametric.gemspec
CHANGED
@@ -18,6 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
20
|
spec.add_development_dependency "rake"
|
21
|
-
spec.add_development_dependency "rspec", '3.
|
21
|
+
spec.add_development_dependency "rspec", '3.12.0'
|
22
22
|
spec.add_development_dependency "byebug"
|
23
23
|
end
|
data/spec/schema_spec.rb
CHANGED
@@ -251,6 +251,64 @@ describe Parametric::Schema do
|
|
251
251
|
end
|
252
252
|
end
|
253
253
|
|
254
|
+
describe '#tagged_one_of for multiple sub-schemas' do
|
255
|
+
let(:user_schema) do
|
256
|
+
described_class.new do
|
257
|
+
field(:name).type(:string).present
|
258
|
+
field(:age).type(:integer).present
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
let(:company_schema) do
|
263
|
+
described_class.new do
|
264
|
+
field(:name).type(:string).present
|
265
|
+
field(:company_code).type(:string).present
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'picks the right sub-schema' do
|
270
|
+
schema = described_class.new do |sc, _|
|
271
|
+
sc.field(:type).type(:string)
|
272
|
+
sc.field(:sub).type(:object).tagged_one_of do |sub|
|
273
|
+
sub.index_by(:type)
|
274
|
+
sub.on('user', user_schema)
|
275
|
+
sub.on('company', company_schema)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
result = schema.resolve(type: 'user', sub: { name: 'Joe', age: 30 })
|
280
|
+
expect(result.valid?).to be true
|
281
|
+
expect(result.output).to eq({ type: 'user', sub: { name: 'Joe', age: 30 } })
|
282
|
+
|
283
|
+
result = schema.resolve(type: 'company', sub: { name: 'ACME', company_code: 123 })
|
284
|
+
expect(result.valid?).to be true
|
285
|
+
expect(result.output).to eq({ type: 'company', sub: { name: 'ACME', company_code: '123' } })
|
286
|
+
|
287
|
+
result = schema.resolve(type: 'company', sub: { name: nil, company_code: 123 })
|
288
|
+
expect(result.valid?).to be false
|
289
|
+
expect(result.errors['$.sub.name']).not_to be_empty
|
290
|
+
|
291
|
+
result = schema.resolve(type: 'foo', sub: { name: 'ACME', company_code: 123 })
|
292
|
+
expect(result.valid?).to be false
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'can be assigned to instance and reused' do
|
296
|
+
user_or_company = Parametric::TaggedOneOf.new do |sub|
|
297
|
+
sub.on('user', user_schema)
|
298
|
+
sub.on('company', company_schema)
|
299
|
+
end
|
300
|
+
|
301
|
+
schema = described_class.new do |sc, _|
|
302
|
+
sc.field(:type).type(:string)
|
303
|
+
sc.field(:sub).type(:object).tagged_one_of(user_or_company.index_by(:type))
|
304
|
+
end
|
305
|
+
|
306
|
+
result = schema.resolve(type: 'user', sub: { name: 'Joe', age: 30 })
|
307
|
+
expect(result.valid?).to be true
|
308
|
+
expect(result.output).to eq({ type: 'user', sub: { name: 'Joe', age: 30 } })
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
254
312
|
describe "#ignore" do
|
255
313
|
it "ignores fields" do
|
256
314
|
s1 = described_class.new.ignore(:title, :status) do
|
data/spec/struct_spec.rb
CHANGED
@@ -301,6 +301,9 @@ describe Parametric::Struct do
|
|
301
301
|
|
302
302
|
schema do
|
303
303
|
field(:title).type(:string).present
|
304
|
+
field(:consumption).type(:object).present.schema do
|
305
|
+
field(:type).present.options(%w[aaa bbb])
|
306
|
+
end
|
304
307
|
end
|
305
308
|
end
|
306
309
|
|
@@ -308,9 +311,10 @@ describe Parametric::Struct do
|
|
308
311
|
klass.new!(title: '')
|
309
312
|
rescue Parametric::InvalidStructError => e
|
310
313
|
expect(e.errors['$.title']).not_to be nil
|
314
|
+
expect(e.errors['$.consumption']).not_to be nil
|
311
315
|
end
|
312
316
|
|
313
|
-
valid = klass.new!(title: 'foo')
|
317
|
+
valid = klass.new!(title: 'foo', consumption: { type: 'aaa' })
|
314
318
|
expect(valid.title).to eq 'foo'
|
315
319
|
end
|
316
320
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parametric
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ismael Celis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 3.
|
33
|
+
version: 3.12.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 3.
|
40
|
+
version: 3.12.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: byebug
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -77,10 +77,12 @@ files:
|
|
77
77
|
- lib/parametric/field.rb
|
78
78
|
- lib/parametric/field_dsl.rb
|
79
79
|
- lib/parametric/policies.rb
|
80
|
+
- lib/parametric/policy_adapter.rb
|
80
81
|
- lib/parametric/registry.rb
|
81
82
|
- lib/parametric/results.rb
|
82
83
|
- lib/parametric/schema.rb
|
83
84
|
- lib/parametric/struct.rb
|
85
|
+
- lib/parametric/tagged_one_of.rb
|
84
86
|
- lib/parametric/version.rb
|
85
87
|
- parametric.gemspec
|
86
88
|
- spec/custom_block_validator_spec.rb
|
@@ -113,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
115
|
- !ruby/object:Gem::Version
|
114
116
|
version: '0'
|
115
117
|
requirements: []
|
116
|
-
rubygems_version: 3.
|
118
|
+
rubygems_version: 3.4.18
|
117
119
|
signing_key:
|
118
120
|
specification_version: 4
|
119
121
|
summary: DSL for declaring allowed parameters with options, regexp patern and default
|