avromatic 2.3.0 → 2.4.0

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: e695eda252fc8374491c000581104f96383a397a118097e9b9c87b256823b267
4
- data.tar.gz: 792f85d1fde7b223491265ff9a82024373063aeb942daa8818510e4fdbfb4d65
3
+ metadata.gz: a7401c0e72992925f02234f5d3d2ab8e762a36bc62aa25c631569d6f4d9918d2
4
+ data.tar.gz: f2121b2a62bf60893ea0eb143ed83a142ea069d07839fb8121d0618483b469d5
5
5
  SHA512:
6
- metadata.gz: e0fbee30489961e56e3ce6a7026beb7e687a38de373c72fd0cc6c39a0afa02ef1618f2c730e4647a3339059591ad2252744a0f1a760a1f5282dafeb0cfd93ff8
7
- data.tar.gz: ef315a61ba34cefec8508a20d64eb6eb4fe530837c9ade67ac8a6075e146c3516a9dbc0a9cfc34ec71265b333865f93a4d36c588d9c9703f80b5a455e1bad408
6
+ metadata.gz: 42be85747686fca4ec66d6e178f7e5aab6d9e9ab88cd4d107ed1c5de8a83017f083a510889ced33a2eb284f45865b3c32b3a9edad200011aed5b17c284f3a593
7
+ data.tar.gz: be13122bca8693d33e20d1317ae5cd88d8c1c975c08d94ed522d0330d70f202bc43d79d7f2941032bc7540ad83b2d647d3c7d29bb5b72c03a560dfedb85e62f7
@@ -1,5 +1,11 @@
1
1
  # avromatic changelog
2
2
 
3
+ ## v2.4.0
4
+ - Ignore the `validate` argument and always validate during serialization. This
5
+ argument will be removed in Avromatic 3.0.
6
+ - Optimize model validation during serialization.
7
+ - Don't cache immutable model validation results or serialized Avro attributes if a model has mutable children.
8
+
3
9
  ## v2.3.0
4
10
  - Add support for Rails 6.1.
5
11
  - Optimize nested model serialization.
@@ -27,7 +27,10 @@ module Avromatic
27
27
  def initialize(owner:, field:, type:)
28
28
  @owner = owner
29
29
  @field = field
30
+ @required = FieldHelper.required?(field)
31
+ @nullable = FieldHelper.nullable?(field)
30
32
  @type = type
33
+ @values_immutable = type.referenced_model_classes.all?(&:recursively_immutable?)
31
34
  @name = field.name.to_sym
32
35
  @name_string = field.name.to_s.dup.freeze
33
36
  @setter_name = "#{field.name}=".to_sym
@@ -40,8 +43,16 @@ module Avromatic
40
43
  end
41
44
  end
42
45
 
46
+ def nullable?
47
+ @nullable
48
+ end
49
+
43
50
  def required?
44
- FieldHelper.required?(field)
51
+ @required
52
+ end
53
+
54
+ def values_immutable?
55
+ @values_immutable
45
56
  end
46
57
 
47
58
  def coerce(input)
@@ -69,6 +80,8 @@ module Avromatic
69
80
  included do
70
81
  class_attribute :attribute_definitions, instance_writer: false
71
82
  self.attribute_definitions = {}
83
+
84
+ delegate :recursively_immutable?, to: :class
72
85
  end
73
86
 
74
87
  def initialize(data = {})
@@ -130,6 +143,12 @@ module Avromatic
130
143
  define_avro_attributes(avro_schema, generated_methods_module)
131
144
  end
132
145
 
146
+ def recursively_immutable?
147
+ return @recursively_immutable if defined?(@recursively_immutable)
148
+
149
+ @recursively_immutable = immutable? && attribute_definitions.each_value.all?(&:values_immutable?)
150
+ end
151
+
133
152
  private
134
153
 
135
154
  def check_for_field_conflicts!
@@ -174,10 +193,10 @@ module Avromatic
174
193
  generated_methods_module.send(:define_method, "#{field.name}?") { !!_attributes[symbolized_field_name] } if FieldHelper.boolean?(field)
175
194
 
176
195
  generated_methods_module.send(:define_method, "#{field.name}=") do |value|
177
- _attributes[symbolized_field_name] = attribute_definitions[symbolized_field_name].coerce(value)
196
+ _attributes[symbolized_field_name] = attribute_definition.coerce(value)
178
197
  end
179
198
 
180
- unless config.mutable # rubocop:disable Style/Next
199
+ unless mutable? # rubocop:disable Style/Next
181
200
  generated_methods_module.send(:private, "#{field.name}=")
182
201
  generated_methods_module.send(:define_method, :clone) { self }
183
202
  generated_methods_module.send(:define_method, :dup) { self }
@@ -19,9 +19,12 @@ module Avromatic
19
19
  end
20
20
  end
21
21
 
22
+ included do
23
+ class_attribute :config, instance_accessor: false, instance_predicate: false
24
+ end
25
+
22
26
  module ClassMethods
23
- attr_accessor :config
24
- delegate :avro_schema, :value_avro_schema, :key_avro_schema, to: :config
27
+ delegate :avro_schema, :value_avro_schema, :key_avro_schema, :mutable?, :immutable?, to: :config
25
28
 
26
29
  def value_avro_field_names
27
30
  @value_avro_field_names ||= value_avro_schema.fields.map(&:name).map(&:to_sym).freeze
@@ -67,6 +70,7 @@ module Avromatic
67
70
  delegate :avro_schema, :value_avro_schema, :key_avro_schema,
68
71
  :value_avro_field_names, :key_avro_field_names,
69
72
  :value_avro_field_references, :key_avro_field_references,
73
+ :mutable?, :immutable?,
70
74
  to: :class
71
75
  end
72
76
  end
@@ -8,6 +8,7 @@ module Avromatic
8
8
 
9
9
  attr_reader :avro_schema, :key_avro_schema, :nested_models, :mutable,
10
10
  :allow_optional_key_fields
11
+ alias_method :mutable?, :mutable
11
12
  delegate :schema_store, to: Avromatic
12
13
 
13
14
  # Either schema(_name) or value_schema(_name), but not both, must be
@@ -34,6 +35,10 @@ module Avromatic
34
35
 
35
36
  alias_method :value_avro_schema, :avro_schema
36
37
 
38
+ def immutable?
39
+ !mutable?
40
+ end
41
+
37
42
  private
38
43
 
39
44
  def find_avro_schema(**options)
@@ -16,9 +16,13 @@ module Avromatic
16
16
  !optional?(field)
17
17
  end
18
18
 
19
+ def nullable?(field)
20
+ optional?(field) || field.type.type_sym == :null
21
+ end
22
+
19
23
  def boolean?(field)
20
24
  field.type.type_sym == :boolean ||
21
- (FieldHelper.optional?(field) && field.type.schemas.last.type_sym == :boolean)
25
+ (optional?(field) && field.type.schemas.last.type_sym == :boolean)
22
26
  end
23
27
  end
24
28
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/deprecation'
4
+
3
5
  module Avromatic
4
6
  module Model
5
7
 
@@ -11,55 +13,93 @@ module Avromatic
11
13
  module Encode
12
14
  extend ActiveSupport::Concern
13
15
 
16
+ UNSPECIFIED = Object.new
17
+
14
18
  delegate :datum_writer, :datum_reader, to: :class
15
19
  private :datum_writer, :datum_reader
16
20
 
17
- def avro_raw_value(validate: true)
18
- if self.class.config.mutable
19
- avro_raw_encode(value_attributes_for_avro(validate: validate), :value)
21
+ def avro_raw_value(validate: UNSPECIFIED)
22
+ unless validate == UNSPECIFIED
23
+ ActiveSupport::Deprecation.warn("The 'validate' argument to #{__method__} is deprecated.")
24
+ end
25
+
26
+ if self.class.recursively_immutable?
27
+ @avro_raw_value ||= avro_raw_encode(value_attributes_for_avro, :value)
20
28
  else
21
- @avro_raw_value ||= avro_raw_encode(value_attributes_for_avro(validate: validate), :value)
29
+ avro_raw_encode(value_attributes_for_avro, :value)
22
30
  end
23
31
  end
24
32
 
25
- def avro_raw_key(validate: true)
33
+ def avro_raw_key(validate: UNSPECIFIED)
34
+ unless validate == UNSPECIFIED
35
+ ActiveSupport::Deprecation.warn("The 'validate' argument to #{__method__} is deprecated.")
36
+ end
37
+
26
38
  raise 'Model has no key schema' unless key_avro_schema
27
- avro_raw_encode(key_attributes_for_avro(validate: validate), :key)
39
+ avro_raw_encode(key_attributes_for_avro, :key)
28
40
  end
29
41
 
30
- def value_attributes_for_avro(validate: true)
31
- if self.class.config.mutable
32
- avro_hash(value_avro_field_references, validate: validate)
42
+ def value_attributes_for_avro(validate: UNSPECIFIED)
43
+ unless validate == UNSPECIFIED
44
+ ActiveSupport::Deprecation.warn("The 'validate' argument to #{__method__} is deprecated.")
45
+ end
46
+
47
+ if self.class.recursively_immutable?
48
+ @value_attributes_for_avro ||= avro_hash(value_avro_field_references)
33
49
  else
34
- @value_attributes_for_avro ||= avro_hash(value_avro_field_references, validate: validate)
50
+ avro_hash(value_avro_field_references)
35
51
  end
36
52
  end
37
53
 
38
- def key_attributes_for_avro(validate: true)
39
- avro_hash(key_avro_field_references, validate: validate)
54
+ def key_attributes_for_avro(validate: UNSPECIFIED)
55
+ unless validate == UNSPECIFIED
56
+ ActiveSupport::Deprecation.warn("The 'validate' argument to #{__method__} is deprecated.")
57
+ end
58
+
59
+ avro_hash(key_avro_field_references)
40
60
  end
41
61
 
42
- def avro_value_datum(validate: true)
43
- if self.class.config.mutable
44
- avro_hash(value_avro_field_references, strict: true, validate: validate)
62
+ def avro_value_datum(validate: UNSPECIFIED)
63
+ unless validate == UNSPECIFIED
64
+ ActiveSupport::Deprecation.warn("The 'validate' argument to #{__method__} is deprecated.")
65
+ end
66
+
67
+ if self.class.recursively_immutable?
68
+ @avro_value_datum ||= avro_hash(value_avro_field_references, strict: true)
45
69
  else
46
- @avro_datum ||= avro_hash(value_avro_field_references, strict: true, validate: validate)
70
+ avro_hash(value_avro_field_references, strict: true)
47
71
  end
48
72
  end
49
73
 
50
- def avro_key_datum(validate: true)
51
- avro_hash(key_avro_field_references, strict: true, validate: validate)
74
+ def avro_key_datum(validate: UNSPECIFIED)
75
+ unless validate == UNSPECIFIED
76
+ ActiveSupport::Deprecation.warn("The 'validate' argument to #{__method__} is deprecated.")
77
+ end
78
+
79
+ avro_hash(key_avro_field_references, strict: true)
52
80
  end
53
81
 
54
82
  private
55
83
 
56
- def avro_hash(field_references, strict: false, validate:)
57
- avro_validate! if validate
84
+ def avro_hash(field_references, strict: false)
58
85
  field_references.each_with_object(Hash.new) do |field_reference, result|
59
- next unless _attributes.include?(field_reference.name_sym)
60
-
86
+ attribute_definition = self.class.attribute_definitions[field_reference.name_sym]
61
87
  value = _attributes[field_reference.name_sym]
62
- result[field_reference.name] = attribute_definitions[field_reference.name_sym].serialize(value, strict)
88
+
89
+ if value.nil? && !attribute_definition.nullable?
90
+ # We're missing a required attribute so perform an explicit validation to generate
91
+ # a more complete error message
92
+ avro_validate!
93
+ elsif _attributes.include?(field_reference.name_sym)
94
+ begin
95
+ result[field_reference.name] = attribute_definition.serialize(value, strict)
96
+ rescue Avromatic::Model::ValidationError
97
+ # Perform an explicit validation to generate a more complete error message
98
+ avro_validate!
99
+ # We should never get here but just in case...
100
+ raise
101
+ end
102
+ end
63
103
  end
64
104
  end
65
105
 
@@ -42,9 +42,10 @@ module Avromatic
42
42
  def serialize(value, strict)
43
43
  if value.nil?
44
44
  value
45
+ elsif strict
46
+ value.avro_value_datum
45
47
  else
46
- # This is only used for recursive serialization so validation has already been done
47
- strict ? value.avro_value_datum(validate: false) : value.value_attributes_for_avro(validate: false)
48
+ value.value_attributes_for_avro
48
49
  end
49
50
  end
50
51
 
@@ -64,8 +64,8 @@ module Avromatic
64
64
  end
65
65
  end
66
66
 
67
- unless self.class.config.mutable
68
- @missing_attributes = missing_attributes.deep_freeze
67
+ if recursively_immutable?
68
+ @missing_attributes = missing_attributes.freeze
69
69
  end
70
70
 
71
71
  missing_attributes
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Avromatic
4
- VERSION = '2.3.0'
4
+ VERSION = '2.4.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: avromatic
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Salsify Engineering
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-11 00:00:00.000000000 Z
11
+ date: 2021-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel