avromatic 0.8.0 → 0.9.0.rc0

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
  SHA1:
3
- metadata.gz: c5cf8d1ba64c3de84625325553fbeeff017e3ac7
4
- data.tar.gz: f536b77ce377759a8f70f7ba3d9d0ea4cb796815
3
+ metadata.gz: 1a36670183dc6e3348395dcc336222da9ac1bd00
4
+ data.tar.gz: b37d41366b6737fb4aa7d4793128c13d90860920
5
5
  SHA512:
6
- metadata.gz: 9241a4262287eda4cadfb2f636b3ee74c655863ccab6b779c0a6c28f12c7cce84cc3fcddc6bbdf7b8f3ce817b96c9cabd95f34afa8a0099c2fcb9daf7518ea40
7
- data.tar.gz: 389244130b9ac99560219d2019222c5f7576f3b5df1b4795926569f0d6e158b4db1378ec33e9c8b0459836cf4d490dec58d26adae51f0c4131f498435a0889bf
6
+ metadata.gz: d316d600c9e38743538abc7b1a598cb13cfa102c6181476ed7e2684ccf63000a6856998ebc9ad065bfb71c98a54adab72a8675aa06f67622f5d2eb415acfea73
7
+ data.tar.gz: 86ac869dd270188af7f24f22ce1259ed2cf0b109e09d23ff3da9af4288deb191ab1fc7a05f4c2d81159335216b07e3e63e8618c86d3e0c4b09055246881b830f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # avromatic changelog
2
2
 
3
+ ## v0.9.0 (unreleased)
4
+ - Add support for more than one non-null type in a union.
5
+
3
6
  ## v0.8.0
4
7
  - Add support for logical types. Currently this requires using the
5
8
  `avro-salsify-fork` gem for logical types support with Ruby.
data/README.md CHANGED
@@ -116,6 +116,9 @@ These customizations are registered on the `Avromatic` module. Once a custom typ
116
116
  is registered, it is used for all models with a schema that references that type.
117
117
  It is recommended to register types within a block passed to `Avromatic.configure`:
118
118
 
119
+ Note: custom types are not currently supported on members of unions with more
120
+ than one non-null type.
121
+
119
122
  ```ruby
120
123
  Avromatic.configure do |config|
121
124
  config.register_type('com.example.my_string', MyString)
@@ -248,8 +251,7 @@ The following validations are supported:
248
251
 
249
252
  The following types/features are not supported for generated models:
250
253
 
251
- - Generic union fields: The special case of an optional field, the union of `:null` and
252
- another type, is supported.
254
+ - Custom types for members within a union.
253
255
  - Reused models for nested records: Currently an anonymous model class is
254
256
  generated for each subrecord.
255
257
 
@@ -0,0 +1,52 @@
1
+ require 'avromatic/model/attribute_type/union'
2
+
3
+ module Avromatic
4
+ module Model
5
+ module Attribute
6
+
7
+ # This subclass of Virtus::Attribute is used for any unions that are
8
+ # defined as subclasses of the primitive Avromatic::Model::AttributeType::Union.
9
+ # Values are coerced by first checking if they already match one of the
10
+ # member types, and then by attempting to coerce to each member type in
11
+ # order.
12
+ class Union < Virtus::Attribute
13
+ primitive Avromatic::Model::AttributeType::Union
14
+
15
+ def initialize(*)
16
+ super
17
+
18
+ primitive.types.each do |type|
19
+ member_attributes << Virtus::Attribute.build(type)
20
+ end
21
+ end
22
+
23
+ def coerce(input)
24
+ return input if value_coerced?(input)
25
+
26
+ result = nil
27
+ member_attributes.find do |union_attribute|
28
+ begin
29
+ coerced = union_attribute.coerce(input)
30
+ result = coerced unless coerced.is_a?(Avromatic::Model::Attributes) && coerced.invalid?
31
+ rescue
32
+ nil
33
+ end
34
+ end
35
+ result
36
+ end
37
+
38
+ def value_coerced?(value)
39
+ member_attributes.any? do |union_attribute|
40
+ union_attribute.value_coerced?(value)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def member_attributes
47
+ @member_attributes ||= Array.new
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ module Avromatic
2
+ module Model
3
+ module AttributeType
4
+
5
+ # This class is used as the base class for Virtus attributes that
6
+ # represent unions. A subclass is generated using Union[*types]. This
7
+ # subclass is specified when defining a Virtus attribute.
8
+ class Union
9
+ class << self
10
+ attr_reader :types
11
+
12
+ protected
13
+
14
+ attr_writer :types
15
+ end
16
+
17
+ # Factory method to define Union types with the specified list of
18
+ # types (classes).
19
+ def self.[](*types)
20
+ Class.new(self).tap do |klass|
21
+ klass.types = types
22
+ # See https://github.com/solnic/virtus/issues/284#issuecomment-56405137
23
+ Axiom::Types::Object.new { primitive(klass) }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -13,9 +13,10 @@ module Avromatic
13
13
  # TODO: This is a hack until I find a better solution for unions with
14
14
  # Virtus. This only handles a union for an optional field with :null
15
15
  # and one other type.
16
- schemas = field_type.schemas.reject { |schema| schema.type_sym == :null }
17
- raise "Only the union of null with one other type is supported #{field_type}" if schemas.size > 1
18
- schemas.first
16
+ # This hack lives on for now because custom type coercion is not pushed
17
+ # down into unions. This means that custom types can only be optional
18
+ # fields, not members of real unions.
19
+ field_type.schemas.reject { |schema| schema.type_sym == :null }.first
19
20
  end
20
21
 
21
22
  module ClassMethods
@@ -60,10 +61,10 @@ module Avromatic
60
61
 
61
62
  attribute(field.name,
62
63
  field_class,
63
- avro_field_options(field))
64
+ avro_field_options(field, field_class))
64
65
 
65
66
  add_validation(field)
66
- add_serializer(field)
67
+ add_serializer(field, field_class)
67
68
  end
68
69
  end
69
70
 
@@ -127,22 +128,29 @@ module Avromatic
127
128
  when :record
128
129
  # TODO: This should add the generated model to a module.
129
130
  # A hash of generated models should be kept by name for reuse.
130
- Class.new do
131
- include Avromatic::Model.build(schema: field_type)
132
- end
131
+ Avromatic::Model.model(schema: field_type)
133
132
  else
134
133
  raise "Unsupported type #{field_type}"
135
134
  end
136
135
  end
137
136
 
138
137
  def union_field_class(field_type)
139
- avro_field_class(Avromatic::Model::Attributes.first_union_schema(field_type))
138
+ field_classes = field_type.schemas.reject { |schema| schema.type_sym == :null }
139
+ .map { |schema| avro_field_class(schema) }
140
+
141
+ if field_classes.size == 1
142
+ field_classes.first
143
+ else
144
+ Avromatic::Model::AttributeType::Union[*field_classes]
145
+ end
140
146
  end
141
147
 
142
- def avro_field_options(field)
148
+ def avro_field_options(field, field_class)
143
149
  options = {}
144
150
 
145
- custom_type = Avromatic.type_registry.fetch(field)
151
+ prevent_union_including_custom_type!(field, field_class)
152
+
153
+ custom_type = Avromatic.type_registry.fetch(field, field_class)
146
154
  coercer = custom_type.deserializer
147
155
  options[:coercer] = coercer if coercer
148
156
 
@@ -154,8 +162,10 @@ module Avromatic
154
162
  options
155
163
  end
156
164
 
157
- def add_serializer(field)
158
- custom_type = Avromatic.type_registry.fetch(field)
165
+ def add_serializer(field, field_class)
166
+ prevent_union_including_custom_type!(field, field_class)
167
+
168
+ custom_type = Avromatic.type_registry.fetch(field, field_class)
159
169
  serializer = custom_type.serializer
160
170
 
161
171
  avro_serializer[field.name.to_sym] = serializer if serializer
@@ -164,6 +174,24 @@ module Avromatic
164
174
  def default_for(value)
165
175
  value.duplicable? ? value.dup.deep_freeze : value
166
176
  end
177
+
178
+ # TODO: the methods below are temporary until support for custom types
179
+ # as union members are supported.
180
+ def member_uses_custom_type?(field)
181
+ field.type.schemas.any? do |klass|
182
+ Avromatic.type_registry.fetch(klass) != NullCustomType
183
+ end
184
+ end
185
+
186
+ def prevent_union_including_custom_type!(field, field_class)
187
+ if field_class.is_a?(Class) &&
188
+ field_class < Avromatic::Model::AttributeType::Union &&
189
+ member_uses_custom_type?(field)
190
+
191
+ raise 'custom types within unions are currently unsupported'
192
+ end
193
+ end
194
+
167
195
  end
168
196
 
169
197
  end
@@ -4,6 +4,7 @@ require 'active_model'
4
4
  require 'avromatic/model/configuration'
5
5
  require 'avromatic/model/value_object'
6
6
  require 'avromatic/model/configurable'
7
+ require 'avromatic/model/attribute/union'
7
8
  require 'avromatic/model/attributes'
8
9
  require 'avromatic/model/raw_serialization'
9
10
  require 'avromatic/model/messaging_serialization'
@@ -23,10 +23,11 @@ module Avromatic
23
23
 
24
24
  # @object [Avro::Schema] Custom type may be fetched based on a Avro field
25
25
  # or schema. If there is no custom type, then NullCustomType is returned.
26
- def fetch(object)
26
+ # @field_class [Object] Value class that has been determined for a field.
27
+ def fetch(object, field_class = nil)
27
28
  field_type = object.is_a?(Avro::Schema::Field) ? object.type : object
28
29
 
29
- if field_type.type_sym == :union
30
+ if field_class && field_type.type_sym == :union && !(field_class < Avromatic::Model::AttributeType::Union)
30
31
  field_type = Avromatic::Model::Attributes.first_union_schema(field_type)
31
32
  end
32
33
 
@@ -1,3 +1,3 @@
1
1
  module Avromatic
2
- VERSION = '0.8.0'.freeze
2
+ VERSION = '0.9.0.rc0'.freeze
3
3
  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: 0.8.0
4
+ version: 0.9.0.rc0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Salsify Engineering
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-08-29 00:00:00.000000000 Z
11
+ date: 2016-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro
@@ -246,6 +246,8 @@ files:
246
246
  - gemfiles/rails4_2.gemfile
247
247
  - lib/avromatic.rb
248
248
  - lib/avromatic/model.rb
249
+ - lib/avromatic/model/attribute/union.rb
250
+ - lib/avromatic/model/attribute_type/union.rb
249
251
  - lib/avromatic/model/attributes.rb
250
252
  - lib/avromatic/model/builder.rb
251
253
  - lib/avromatic/model/configurable.rb
@@ -278,12 +280,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
278
280
  version: '0'
279
281
  required_rubygems_version: !ruby/object:Gem::Requirement
280
282
  requirements:
281
- - - ">="
283
+ - - ">"
282
284
  - !ruby/object:Gem::Version
283
- version: '0'
285
+ version: 1.3.1
284
286
  requirements: []
285
287
  rubyforge_project:
286
- rubygems_version: 2.6.6
288
+ rubygems_version: 2.4.8
287
289
  signing_key:
288
290
  specification_version: 4
289
291
  summary: Generate Ruby models from Avro schemas