avromatic 2.3.0 → 2.4.0

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 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