avromatic 0.9.0.rc1 → 0.9.0.rc2

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: 8850a5d6dce59c5dd91c2476b3b541ca6d258b94
4
- data.tar.gz: eb4d4df178cf748408d7346251c7b0989eab7d8d
3
+ metadata.gz: 349bd4de9cfdaffd91c737ef6330d9b9d20ff8ed
4
+ data.tar.gz: cc1b8f6f5275cea982902ce4ae5d055e504a69a5
5
5
  SHA512:
6
- metadata.gz: 7feeb97078619ec38b8092fde8f01d5f9f7e209f7040b9dd6252cc7766f7ac700da35681c9a95ca4ce1d88e6477f68e1bf41c09414fff3cd854e97c9cdb3707b
7
- data.tar.gz: 3d2f3e5b0da52a97fcb5c555cda2eeda65767b4840e2ade715449e7a48df57c42a770d1ac8a56aadb45be8924e50aa060534e1c342f0bbc44c04c077573a3b29
6
+ metadata.gz: a1d4474e40c761995be5512600d9e84f6f743501b9f496901900686058cd4e5190c78712723125bf66d3e7a571cc786ea579fcc91021bcf35506a88f7ef69be7
7
+ data.tar.gz: 72e71deb56ff37c1f85a4c11b04d37f2672cc0d3b406ee574180257f690a6f7d057fe53250cfbd513610740120fe7e191d7d73970f7140be4c6b7b16d711c122
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
+ --require spec_helper
data/.rubocop.yml CHANGED
@@ -4,3 +4,6 @@ inherit_gem:
4
4
  AllCops:
5
5
  TargetRubyVersion: 2.2
6
6
 
7
+ Style/MultilineBlockChain:
8
+ Enabled: false
9
+
data/CHANGELOG.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # avromatic changelog
2
2
 
3
3
  ## v0.9.0 (unreleased)
4
- - Add support for more than one non-null type in a union.
4
+ - Experimental: Add support for more than one non-null type in a union.
5
+ - Allow nested models to be referenced and reused.
5
6
  - Fix the serialization of nested complex types.
6
7
 
7
8
  ## v0.8.0
data/README.md CHANGED
@@ -37,7 +37,11 @@ Or install it yourself as:
37
37
  returns an `Avro::Schema` object. An `AvroTurf::SchemaStore` can be used.
38
38
  The `schema_store` is unnecessary if models are generated directly from
39
39
  `Avro::Schema` objects. See [Models](#models).
40
-
40
+ * **nested_models**: An optional [ModelRegistry](https://github.com/salsify/avromatic/blob/master/lib/avromatic/model_registry.rb)
41
+ that is used to store, by full schema name, the generated models that are
42
+ embedded within top-level models. By default a new `Avromatic::ModelRegistry`
43
+ is created.
44
+
41
45
  #### Using a Schema Registry/Messaging API
42
46
 
43
47
  The configuration options below are required when using a schema registry
@@ -109,6 +113,72 @@ constant:
109
113
  MyModel = Avromatic::Model.model(schema_name :my_model)
110
114
  ```
111
115
 
116
+ #### Experimental: Union Support
117
+
118
+ Avromatic contains experimental support for unions containing more than one
119
+ non-null member type. This feature is experimental because Virtus attributes
120
+ may attempt to coerce between types too aggressively.
121
+
122
+ For now, if a union contains [nested models](#nested-models) then it is
123
+ recommended that you assign model instances.
124
+
125
+ Some combination of the ordering of member types in the union and relying on
126
+ model validation may be required so that the correct member is selected,
127
+ especially when deserializing from Avro.
128
+
129
+ In the future, the type coercion used in the gem will be replaced to better
130
+ support the union use case.
131
+
132
+ #### Nested Models
133
+
134
+ Nested models are models that are embedded within top-level models generated
135
+ using Avromatic. Normally these nested models are automatically generated.
136
+
137
+ By default, nested models are stored in `Avromatic.nested_models`. This is an
138
+ `Avromatic::ModelRegistry` instance that provides access to previously generated
139
+ nested models by the full name of their Avro schema.
140
+
141
+ ```ruby
142
+ Avromatic.nested_models['com.my_company.test.example']
143
+ #=> <model class>
144
+ ```
145
+
146
+ The `ModelRegistry` can be customized to remove a namespace prefix:
147
+
148
+ ```ruby
149
+ Avromatic.nested_models =
150
+ Avromatic::ModelRegistry.new(remove_namespace_prefix: 'com.my_company'
151
+ ```
152
+
153
+ The `:remove_namespace_prefix` value can be a string or a regexp.
154
+
155
+ By default, top-level generated models reuse `Avromatic.nested_models`. This
156
+ allows nested models to be shared across different generated models.
157
+ A `:nested_models` option can be specified when generating a model. This allows
158
+ the reuse of nested models to be scoped:
159
+
160
+ ```ruby
161
+ Avromatic::Model.model(schema_name, :my_model
162
+ nested_models: ModelRegistry.new)
163
+ ```
164
+
165
+ It is also possible to explicitly generate a nested model that should be reused
166
+ and add it to the registry. This is useful when the nested model is extended:
167
+
168
+ ```ruby
169
+ class UsefulSubrecord
170
+ include Avromatic::Model.build(schema_name: 'useful_subrecord')
171
+
172
+ def do_something_custom
173
+ ...
174
+ end
175
+ end
176
+ Avromatic.nested_models.register(UsefulSubrecord)
177
+ ```
178
+
179
+ With Rails, it may be necessary to perform this explicit registration in an
180
+ initializer so that lazy class loading works correctly in development.
181
+
112
182
  #### Custom Types
113
183
 
114
184
  Custom types can be configured for fields of named types (record, enum, fixed).
data/lib/avromatic.rb CHANGED
@@ -1,16 +1,18 @@
1
1
  require 'avromatic/version'
2
2
  require 'avromatic/model'
3
+ require 'avromatic/model_registry'
3
4
  require 'avro_turf'
4
5
  require 'avro_turf/messaging'
5
6
 
6
7
  module Avromatic
7
8
  class << self
8
9
  attr_accessor :schema_registry, :registry_url, :schema_store, :logger,
9
- :messaging, :type_registry
10
+ :messaging, :type_registry, :nested_models
10
11
 
11
12
  delegate :register_type, to: :type_registry
12
13
  end
13
14
 
15
+ self.nested_models = ModelRegistry.new
14
16
  self.logger = Logger.new($stdout)
15
17
  self.type_registry = Avromatic::Model::TypeRegistry.new
16
18
 
@@ -126,14 +126,7 @@ module Avromatic
126
126
  when :union
127
127
  union_field_class(field_type)
128
128
  when :record
129
- # TODO: This should add the generated model to a module.
130
- # A hash of generated models should be kept by name for reuse.
131
- Avromatic::Model.model(schema: field_type).tap do |record_class|
132
- # Register the generated model with Axiom to prevent it being
133
- # treated as a BasicObject.
134
- # See https://github.com/solnic/virtus/issues/284#issuecomment-56405137
135
- Axiom::Types::Object.new { primitive(record_class) }
136
- end
129
+ build_nested_model(field_type)
137
130
  else
138
131
  raise "Unsupported type #{field_type}"
139
132
  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/nested_models'
7
8
  require 'avromatic/model/attribute/union'
8
9
  require 'avromatic/model/attributes'
9
10
  require 'avromatic/model/attribute/record'
@@ -42,6 +43,7 @@ module Avromatic
42
43
  ActiveModel::Validations,
43
44
  Virtus.value_object,
44
45
  Avromatic::Model::Configurable,
46
+ Avromatic::Model::NestedModels,
45
47
  Avromatic::Model::Attributes,
46
48
  Avromatic::Model::ValueObject,
47
49
  Avromatic::Model::RawSerialization,
@@ -26,6 +26,10 @@ module Avromatic
26
26
  @key_avro_fields_by_name ||= mapped_by_name(key_avro_schema)
27
27
  end
28
28
 
29
+ def nested_models
30
+ config.nested_models || Avromatic.nested_models
31
+ end
32
+
29
33
  private
30
34
 
31
35
  def mapped_by_name(schema)
@@ -4,7 +4,7 @@ module Avromatic
4
4
  # This class holds configuration for a model built from Avro schema(s).
5
5
  class Configuration
6
6
 
7
- attr_reader :avro_schema, :key_avro_schema
7
+ attr_reader :avro_schema, :key_avro_schema, :nested_models
8
8
  delegate :schema_store, to: Avromatic
9
9
 
10
10
  # Either schema(_name) or value_schema(_name), but not both, must be
@@ -17,10 +17,12 @@ module Avromatic
17
17
  # @option options [String, Symbol] :value_schema_name
18
18
  # @option options [Avro::Schema] :key_schema
19
19
  # @option options [String, Symbol] :key_schema_name
20
+ # @option options [Avromatic::ModelRegistry] :nested_models
20
21
  def initialize(**options)
21
22
  @avro_schema = find_avro_schema(**options)
22
23
  raise ArgumentError.new('value_schema(_name) or schema(_name) must be specified') unless avro_schema
23
24
  @key_avro_schema = find_schema_by_option(:key_schema, **options)
25
+ @nested_models = options[:nested_models]
24
26
  end
25
27
 
26
28
  alias_method :value_avro_schema, :avro_schema
@@ -39,7 +41,6 @@ module Avromatic
39
41
  schema_name_option = :"#{option_name}_name"
40
42
  options[option_name] ||
41
43
  (options[schema_name_option] && schema_store.find(options[schema_name_option]))
42
-
43
44
  end
44
45
  end
45
46
  end
@@ -0,0 +1,29 @@
1
+ require 'active_support/inflector/methods'
2
+
3
+ module Avromatic
4
+ module Model
5
+ # This module handles integration with the ModelRegistry and support
6
+ # for nested model reuse.
7
+ module NestedModels
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+ def build_nested_model(schema)
12
+ fullname = schema.fullname
13
+
14
+ if nested_models.registered?(fullname)
15
+ nested_models[fullname]
16
+ else
17
+ nested_model = Avromatic::Model.model(schema: schema,
18
+ nested_models: nested_models)
19
+ # Register the generated model with Axiom to prevent it being
20
+ # treated as a BasicObject.
21
+ # See https://github.com/solnic/virtus/issues/284#issuecomment-56405137
22
+ Axiom::Types::Object.new { primitive(nested_model) }
23
+ nested_models.register(nested_model)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,6 +1,5 @@
1
1
  module Avromatic
2
2
  module Model
3
-
4
3
  # This module is used to override the comparisons defined by
5
4
  # Virtus::Equalizer which is pulled in by Virtus::ValueObject.
6
5
  module ValueObject
@@ -0,0 +1,45 @@
1
+ require 'active_support/core_ext/string/access'
2
+
3
+ module Avromatic
4
+ # The ModelRegistry class is used to store and fetch nested models by
5
+ # their fullname. An optional namespace prefix can be removed from the full
6
+ # name that is used to store and fetch models.
7
+ class ModelRegistry
8
+
9
+ def initialize(remove_namespace_prefix: nil)
10
+ @prefix = remove_namespace_prefix
11
+ @hash = Hash.new
12
+ end
13
+
14
+ def [](fullname)
15
+ @hash.fetch(fullname)
16
+ end
17
+
18
+ def register(model)
19
+ raise 'models with a key schema are not supported' if model.key_avro_schema
20
+ name = model.avro_schema.fullname
21
+ name = remove_prefix(name) if @prefix
22
+ @hash[name] = model
23
+ end
24
+
25
+ def registered?(fullname)
26
+ @hash.key?(fullname)
27
+ end
28
+
29
+ private
30
+
31
+ def remove_prefix(name)
32
+ value =
33
+ case @prefix
34
+ when String
35
+ name.from(@prefix.length) if name.start_with?(@prefix)
36
+ when Regexp
37
+ name.sub(@prefix, '')
38
+ else
39
+ raise "unsupported `remove_namespace_prefix` value: #{@prefix}"
40
+ end
41
+
42
+ value.start_with?('.') ? value.from(1) : value
43
+ end
44
+ end
45
+ end
@@ -1,3 +1,3 @@
1
1
  module Avromatic
2
- VERSION = '0.9.0.rc1'.freeze
2
+ VERSION = '0.9.0.rc2'.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.9.0.rc1
4
+ version: 0.9.0.rc2
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-09-03 00:00:00.000000000 Z
11
+ date: 2016-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro
@@ -257,11 +257,13 @@ files:
257
257
  - lib/avromatic/model/logical_types.rb
258
258
  - lib/avromatic/model/message_decoder.rb
259
259
  - lib/avromatic/model/messaging_serialization.rb
260
+ - lib/avromatic/model/nested_models.rb
260
261
  - lib/avromatic/model/null_custom_type.rb
261
262
  - lib/avromatic/model/passthrough_serializer.rb
262
263
  - lib/avromatic/model/raw_serialization.rb
263
264
  - lib/avromatic/model/type_registry.rb
264
265
  - lib/avromatic/model/value_object.rb
266
+ - lib/avromatic/model_registry.rb
265
267
  - lib/avromatic/railtie.rb
266
268
  - lib/avromatic/rspec.rb
267
269
  - lib/avromatic/version.rb