parametric 0.2.19 → 0.2.21

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: 98676c8edebba19bd1adff020f5eadf9dce07e44f89125be4f0451d6bdee50cf
4
- data.tar.gz: 5a5d0ecded864e5185d2b1d1cafe5eeb2307d92cf52c4111e7dd85ebe65c0321
3
+ metadata.gz: 61eb99562317fd43b8ea59c1ed4e48c13320ff04334d372c07b5c78efe7ebd22
4
+ data.tar.gz: ee09741be15edf7ff3082b285ae16549029e995eb5841518e7f4394f5c131049
5
5
  SHA512:
6
- metadata.gz: 1a3b81c816a02516450bdeb70ddb5b804741481ec0084167f31cdddcef4c3734f25b0d69da022048d9889c7b47a462cd821bf3a4cef07d719aff9b57293e10ef
7
- data.tar.gz: 5a2cd8dd4d68cd896901d744f656fca3385f6d3763bc05d62dbd1b6c53362a32092781e7bd57e439b27c6e0071d1179a0355372778d33b9c96f893e6fd4e07d3
6
+ metadata.gz: 9c994b498b30870bb501890ecb44fe007d8ea49ae0ab2737033b70388ba36036b12a6795cf392a712e8b2d5121aa86e875280c080cf9a4ac4af953db2784c1fd
7
+ data.tar.gz: 55e8c26767574c8090d144465c23be6fe0a170470f736173cbde5beb059a8adcd1ca4aa52ad9a56038bc487b00e4595e7257d31ad84227df5720fe276c964012
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?
@@ -23,6 +23,10 @@ module Parametric
23
23
  @policies = []
24
24
  end
25
25
 
26
+ def ==(other)
27
+ other.is_a?(Field) && key == other.key && policies == other.policies && meta_data == other.meta_data
28
+ end
29
+
26
30
  def meta(hash = nil)
27
31
  @meta_data = @meta_data.merge(hash) if hash.is_a?(Hash)
28
32
  self
@@ -88,7 +92,7 @@ module Parametric
88
92
  begin
89
93
  pol = policy.build(key, value, payload:, context:)
90
94
  if !pol.eligible?
91
- eligible = false
95
+ eligible = pol.include_non_eligible_in_ouput?
92
96
  if has_default?
93
97
  eligible = true
94
98
  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)
@@ -53,5 +61,9 @@ module Parametric
53
61
  def key
54
62
  @policy.key
55
63
  end
64
+
65
+ def ==(other)
66
+ key == other.key && meta_data == other.meta_data
67
+ end
56
68
  end
57
69
  end
@@ -18,6 +18,10 @@ module Parametric
18
18
  @after_hooks = []
19
19
  end
20
20
 
21
+ def ==(other)
22
+ other.is_a?(Schema) && fields == other.fields
23
+ end
24
+
21
25
  def schema
22
26
  self
23
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parametric
4
- VERSION = '0.2.19'
4
+ VERSION = '0.2.21'
5
5
  end
data/spec/field_spec.rb CHANGED
@@ -59,6 +59,24 @@ describe Parametric::Field do
59
59
  end
60
60
  end
61
61
 
62
+ describe '#==' do
63
+ it 'compares by key' do
64
+ field_a = described_class.new(:a_key, registry)
65
+ field_b = described_class.new(:a_key, registry)
66
+ field_c = described_class.new(:another_key, registry)
67
+ expect(field_a).to eq(field_b)
68
+ expect(field_a).not_to eq(field_c)
69
+ end
70
+
71
+ it 'compares by policies' do
72
+ field_a = described_class.new(:a_key, registry).type(:integer)
73
+ field_b = described_class.new(:a_key, registry).type(:integer)
74
+ field_c = described_class.new(:a_key, registry).type(:integer).options([1,2,3])
75
+ expect(field_a).to eq(field_b)
76
+ expect(field_a).not_to eq(field_c)
77
+ end
78
+ end
79
+
62
80
  describe "#policy" do
63
81
  it "coerces integer" do
64
82
  subject.policy(:integer)
@@ -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
@@ -46,6 +46,29 @@ describe Parametric::Schema do
46
46
  end
47
47
  end
48
48
 
49
+ specify '#==' do
50
+ schema2 = described_class.new do
51
+ field(:title).policy(:string).present
52
+ field(:price).policy(:integer).meta(label: "A price")
53
+ field(:status).policy(:string).options(['visible', 'hidden'])
54
+ field(:tags).policy(:split).policy(:array)
55
+ field(:description).policy(:string)
56
+ field(:variants).policy(:array).schema do
57
+ field(:name).policy(:string).present
58
+ field(:sku)
59
+ field(:stock).policy(:integer).default(1)
60
+ field(:available_if_no_stock).policy(:boolean).policy(:flexible_bool).default(false)
61
+ end
62
+ end
63
+
64
+ schema3 = described_class.new do
65
+ field(:title).policy(:string).present
66
+ end
67
+
68
+ expect(subject).to eq schema2
69
+ expect(subject).not_to eq schema3
70
+ end
71
+
49
72
  def resolve(schema, payload, &block)
50
73
  yield schema.resolve(payload)
51
74
  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.19
4
+ version: 0.2.21
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: 2024-01-30 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