avromatic 0.9.0.rc1 → 0.9.0.rc2

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