parametric 0.2.18 → 0.2.20

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 403aac3495b3e90bc32cf01c0229b83497d37413fb43b6180c3dafd0a9ec36b9
4
- data.tar.gz: a8396d8ab3831e7c93d1643f96562ff0d5fe1ddaa8794dfe7ed8993bd4732cd5
3
+ metadata.gz: 0a4f53d13fdfe4476f9467127b58740f86b8f301979b56940eb9e228fd588225
4
+ data.tar.gz: 5b26af44589c43bc2825a75e988318eefc3147581183a4c4cc65af5b98a47fed
5
5
  SHA512:
6
- metadata.gz: dbd04349edf7e8f4777ef7c681ec8f16d2cf570d915088fae04942760415d9fd88123741b68957839b21d7145d97b83b6cf6911fce5f0e2db806d34a304e1a0d
7
- data.tar.gz: 9851e0d8f4e5e48d38870027c8a987fbf65f264a8c9dfaafe74080ff463e3783267f605eae09ce65b1f43547a27935ae532fd75120d60823fd24e8bf09bd0c4d
6
+ metadata.gz: a5f7fbdfee4363b05e2044439499bd17954552b6a8c9b95442a613969e4775dcc2d05ee2340ba06df3b55b12df2414a16b672fe76c5b1ee2485ba6dede5cbdf7
7
+ data.tar.gz: 9ea239bd608ef85e8f92a07060e10aff57b0552b92562b4e4ea1507e1a91a71173382565fd7f198d8f3a2595756b8a6badeb46638de14997fd15bf13250e4910
data/README.md CHANGED
@@ -293,6 +293,21 @@ Like `:declared`, it stops the policy chain if a key is not in input, but it als
293
293
  field(:name).policy(:declared_no_default).present
294
294
  ```
295
295
 
296
+ ### :nullable
297
+
298
+ Check that key is present in input. If value is `nil`, processing and validations stop, but key is still included in output.
299
+
300
+ ```ruby
301
+ schema = Parametric::Schema.new do
302
+ field(:age).nullable.type(:integer).policy(:gt: 21)
303
+ end
304
+
305
+ schema.resolve(age: '22').output[:age] # 22
306
+ schema.resolve(age: 10).errors[:age] # has error because < 21
307
+ schema.resolve(age: nil).output[:age] # nil, no errors
308
+ schema.resolve({}).output[:age] # nil, no errors
309
+ ```
310
+
296
311
  ### :gt
297
312
 
298
313
  Validate that the value is greater than a number
@@ -387,6 +402,12 @@ class MyPolicyRunner
387
402
  true
388
403
  end
389
404
 
405
+ # If this policy is not eligible, should the key and value be included in the output?
406
+ # @return [Boolean]
407
+ def include_non_eligible_in_ouput?
408
+ true
409
+ end
410
+
390
411
  # If [false], add [#message] to result errors and halt processing field.
391
412
  # @return [Boolean]
392
413
  def valid?
@@ -88,7 +88,7 @@ module Parametric
88
88
  begin
89
89
  pol = policy.build(key, value, payload:, context:)
90
90
  if !pol.eligible?
91
- eligible = false
91
+ eligible = pol.include_non_eligible_in_ouput?
92
92
  if has_default?
93
93
  eligible = true
94
94
  value = default_block.call(key, payload, context)
@@ -22,5 +22,9 @@ module Parametric
22
22
  def options(opts)
23
23
  policy :options, opts
24
24
  end
25
+
26
+ def nullable
27
+ policy :nullable
28
+ end
25
29
  end
26
30
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Parametric
4
+ # A policy that allows a field to be nullable.
5
+ # Fields with nil values are not processed further, and the value is returned as-is.
6
+ # @example
7
+ # field(:age).nullable.type(:integer)
8
+ # field(:age).nullable.type(:integer).required
9
+ #
10
+ class NullablePolicy
11
+ def meta_data; {}; end
12
+
13
+ def build(key, value, payload:, context:)
14
+ Runner.new(key, value, payload, context)
15
+ end
16
+
17
+ class Runner
18
+ def initialize(key, value, payload, context)
19
+ @key = key
20
+ @value = value
21
+ @payload = payload
22
+ @context = context
23
+ end
24
+
25
+ # Should this policy run at all?
26
+ # returning [false] halts the field policy chain.
27
+ # @return [Boolean]
28
+ def eligible?
29
+ @payload.key?(@key) && !@value.nil?
30
+ end
31
+
32
+ # If this policy is not eligible, should the key and value be included in the output?
33
+ # @return [Boolean]
34
+ def include_non_eligible_in_ouput?
35
+ true
36
+ end
37
+
38
+ # If [false], add [#message] to result errors and halt processing field.
39
+ # @return [Boolean]
40
+ def valid?
41
+ true
42
+ end
43
+
44
+ # Coerce the value, or return as-is.
45
+ # @return [Any]
46
+ def value
47
+ @value
48
+ end
49
+
50
+ # Error message for this policy
51
+ # @return [String]
52
+ def message
53
+ ''
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'parametric/nullable_policy'
4
+
3
5
  module Parametric
4
6
  module Policies
5
7
  class Format
@@ -59,6 +61,7 @@ module Parametric
59
61
  Parametric.policy :format, Policies::Format
60
62
  Parametric.policy :email, Policies::Format.new(EMAIL_REGEXP, 'invalid email')
61
63
  Parametric.policy :value, Policies::Value
64
+ Parametric.policy :nullable, Parametric::NullablePolicy
62
65
 
63
66
  Parametric.policy :noop do
64
67
  eligible do |value, key, payload|
@@ -14,6 +14,14 @@ module Parametric
14
14
  @policy.eligible?(@raw_value, @key, @payload)
15
15
  end
16
16
 
17
+ # If a policy is not #eligible?, use this to decide if its key
18
+ # should still be included in output hash.
19
+ #
20
+ # @return [Boolean]
21
+ def include_non_eligible_in_ouput?
22
+ false
23
+ end
24
+
17
25
  # @return [Boolean]
18
26
  def valid?
19
27
  @policy.valid?(value, @key, @payload)
@@ -76,6 +76,8 @@ module Parametric
76
76
  meta = field.meta_data.dup
77
77
  sc = meta.delete(:schema)
78
78
  meta[:structure] = sc.schema.structure if sc
79
+ one_of = meta.delete(:one_of)
80
+ meta[:one_of] = one_of.values.map(&:structure) if one_of
79
81
  obj[field.key] = meta
80
82
  end
81
83
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parametric
4
- VERSION = '0.2.18'
4
+ VERSION = '0.2.20'
5
5
  end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'nullable policy' do
6
+ specify 'dealing with nil values' do
7
+ schema = Parametric::Schema.new do
8
+ field(:age).nullable.type(:integer)
9
+ end
10
+
11
+ expect(schema.resolve({ age: 10 }).output[:age]).to eq 10
12
+ expect(schema.resolve({ age: '10' }).output[:age]).to eq 10
13
+ expect(schema.resolve({ age: nil }).output[:age]).to eq nil
14
+ expect(schema.resolve({ age: false }).output[:age]).to be false
15
+ expect(schema.resolve({ nope: 1 }).output.key?(:age)).to be true
16
+ end
17
+
18
+ specify 'with required/present types' do
19
+ schema = Parametric::Schema.new do
20
+ field(:age).nullable.type(:integer).present.policy(:gt, 9)
21
+ end
22
+
23
+ schema.resolve({ age: '10' }).tap do |r|
24
+ expect(r.output[:age]).to eq 10
25
+ expect(r.errors.any?).to be false
26
+ end
27
+
28
+ schema.resolve({ age: '7' }).tap do |r|
29
+ expect(r.output[:age]).to eq 7
30
+ expect(r.errors.any?).to be true
31
+ end
32
+
33
+ schema.resolve({ age: nil }).tap do |r|
34
+ expect(r.output[:age]).to eq nil
35
+ expect(r.errors.any?).to be false
36
+ end
37
+
38
+ schema.resolve({}).tap do |r|
39
+ expect(r.output.key?(:age)).to be true
40
+ expect(r.output[:age]).to eq nil
41
+ expect(r.errors.any?).to be false
42
+ end
43
+ end
44
+
45
+ specify 'interacting with custom types that validate' do
46
+ Parametric.policy :validating_integer do
47
+ exp = /^\d+$/
48
+
49
+ validate do |value, _key, _context|
50
+ !!(value.to_s =~ exp)
51
+ end
52
+
53
+ coerce do |value, _key, _context|
54
+ if value.to_s =~ exp
55
+ value.to_i
56
+ else
57
+ value
58
+ end
59
+ end
60
+
61
+ message do
62
+ 'error!'
63
+ end
64
+ end
65
+
66
+ schema = Parametric::Schema.new do
67
+ field(:age).nullable.type(:validating_integer)
68
+ end
69
+
70
+ expect(schema.resolve({ age: 10 }).output[:age]).to eq 10
71
+ expect(schema.resolve({ age: '10' }).output[:age]).to eq 10
72
+ schema.resolve({ age: 'nope' }).tap do |r|
73
+ expect(r.output[:age]).to eq 'nope'
74
+ expect(r.errors.any?).to be true
75
+ end
76
+ schema.resolve({ age: nil }).tap do |r|
77
+ expect(r.output.key?(:age)).to be(true)
78
+ expect(r.output[:age]).to eq nil
79
+ expect(r.errors.any?).to be false
80
+ end
81
+ expect(schema.resolve({ age: nil }).output[:age]).to eq nil
82
+ expect(schema.resolve({ nope: 1 }).output.key?(:age)).to be true
83
+ end
84
+
85
+ specify 'interacting with required fields' do
86
+ schema = Parametric::Schema.new do
87
+ field(:age).nullable.type(:integer).required
88
+ end
89
+
90
+ result = schema.resolve({})
91
+ expect(result.output.key?(:age)).to be(true)
92
+ expect(result.output[:age]).to eq nil
93
+ expect(result.errors['$.age']).to eq nil
94
+ end
95
+
96
+ specify 'copying policies via Field#from' do
97
+ source_schema = Parametric::Schema.new do
98
+ field(:age).nullable.type(:integer).required
99
+ end
100
+
101
+ target_schema = Parametric::Schema.new do |sc, _|
102
+ source_schema.fields.each do |key, f|
103
+ sc.field(key).from(f)
104
+ end
105
+ end
106
+
107
+ result = target_schema.resolve({})
108
+ expect(result.output[:age]).to eq nil
109
+ expect(result.errors['$.age']).to eq nil
110
+ end
111
+ end
data/spec/schema_spec.rb CHANGED
@@ -307,6 +307,29 @@ describe Parametric::Schema do
307
307
  expect(result.valid?).to be true
308
308
  expect(result.output).to eq({ type: 'user', sub: { name: 'Joe', age: 30 } })
309
309
  end
310
+
311
+ specify '#structure' do
312
+ user_or_company = Parametric::TaggedOneOf.new do |sub|
313
+ sub.on('user', user_schema)
314
+ sub.on('company', company_schema)
315
+ end
316
+
317
+ schema = described_class.new do |sc, _|
318
+ sc.field(:type).type(:string)
319
+ sc.field(:sub).type(:object).tagged_one_of(user_or_company.index_by(:type))
320
+ end
321
+
322
+ structure = schema.structure
323
+ structure.dig(:sub).tap do |sub|
324
+ expect(sub[:type]).to eq :object
325
+ expect(sub[:one_of][0][:name][:type]).to eq :string
326
+ expect(sub[:one_of][0][:name][:required]).to be true
327
+ expect(sub[:one_of][0][:name][:present]).to be true
328
+ expect(sub[:one_of][0][:age][:type]).to eq :integer
329
+
330
+ expect(sub[:one_of][1][:company_code][:type]).to eq :string
331
+ end
332
+ end
310
333
  end
311
334
 
312
335
  describe "#ignore" do
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.18
4
+ version: 0.2.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ismael Celis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-10 00:00:00.000000000 Z
11
+ date: 2023-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -76,6 +76,7 @@ files:
76
76
  - lib/parametric/dsl.rb
77
77
  - lib/parametric/field.rb
78
78
  - lib/parametric/field_dsl.rb
79
+ - lib/parametric/nullable_policy.rb
79
80
  - lib/parametric/policies.rb
80
81
  - lib/parametric/policy_adapter.rb
81
82
  - lib/parametric/registry.rb
@@ -89,6 +90,7 @@ files:
89
90
  - spec/dsl_spec.rb
90
91
  - spec/expand_spec.rb
91
92
  - spec/field_spec.rb
93
+ - spec/nullable_policy_spec.rb
92
94
  - spec/policies_spec.rb
93
95
  - spec/schema_lifecycle_hooks_spec.rb
94
96
  - spec/schema_spec.rb
@@ -115,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
117
  - !ruby/object:Gem::Version
116
118
  version: '0'
117
119
  requirements: []
118
- rubygems_version: 3.4.18
120
+ rubygems_version: 3.4.22
119
121
  signing_key:
120
122
  specification_version: 4
121
123
  summary: DSL for declaring allowed parameters with options, regexp patern and default
@@ -125,6 +127,7 @@ test_files:
125
127
  - spec/dsl_spec.rb
126
128
  - spec/expand_spec.rb
127
129
  - spec/field_spec.rb
130
+ - spec/nullable_policy_spec.rb
128
131
  - spec/policies_spec.rb
129
132
  - spec/schema_lifecycle_hooks_spec.rb
130
133
  - spec/schema_spec.rb