avromatic 0.23.0 → 0.24.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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +8 -0
- data/lib/avromatic/io/datum_reader.rb +1 -1
- data/lib/avromatic/io/datum_writer.rb +25 -0
- data/lib/avromatic/io.rb +8 -0
- data/lib/avromatic/messaging.rb +27 -2
- data/lib/avromatic/model/attribute/union.rb +1 -1
- data/lib/avromatic/model/raw_serialization.rb +51 -12
- data/lib/avromatic/version.rb +1 -1
- data/lib/avromatic.rb +3 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c482b56c1e0e8d3f9271d7a7e61e763a9dffdd83
|
4
|
+
data.tar.gz: 0005f4ef6ff0abdc4a484b67873de53fab595244
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ad6910fa3bda579cc807aec5b2e374d7821f7fb8c47b43de99b58c738b822b4f95d884594ec89a6f083baac97b018621d6ff70e9795caf8c050b96f3c4cc974
|
7
|
+
data.tar.gz: d11cb1499b84dc7b4dfea172f0391eeee61e4f161d35a5e8e44c8181c775693b86902e2b0825e31fe4829058fbef51299a65da1d262c1aacfd689d74cacc9b43
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# avromatic changelog
|
2
2
|
|
3
|
+
## v0.24.0
|
4
|
+
- Add `Avromatic::IO::DatumWriter` to optimize the encoding of Avro unions
|
5
|
+
from Avromatic models.
|
6
|
+
- Expose the `#key_attributes_for_avro` method on models.
|
7
|
+
|
3
8
|
## v0.23.0
|
4
9
|
- Add mutable option when defining a model.
|
5
10
|
|
data/README.md
CHANGED
@@ -97,6 +97,14 @@ end
|
|
97
97
|
is included in the hash returned by the `DatumReader` but can be omitted by
|
98
98
|
setting this option to `false`.
|
99
99
|
|
100
|
+
#### Encoding
|
101
|
+
* **use_custom_datum_writer**: `Avromatic` includes a modified subclass of
|
102
|
+
`Avro::IO::DatumWriter`. This subclass uses additional information about
|
103
|
+
the index of union members to optimize the encoding of Avro messages.
|
104
|
+
By default this information is included in the hash passed to the encoder
|
105
|
+
but can be omitted by setting this option to `false`.
|
106
|
+
|
107
|
+
|
100
108
|
### Models
|
101
109
|
|
102
110
|
Models are defined based on an Avro schema for a record.
|
@@ -6,7 +6,7 @@ module Avromatic
|
|
6
6
|
# branch 'salsify-master' with the tag 'v1.9.0.3'
|
7
7
|
class DatumReader < Avro::IO::DatumReader
|
8
8
|
|
9
|
-
UNION_MEMBER_INDEX =
|
9
|
+
UNION_MEMBER_INDEX = Avromatic::IO::UNION_MEMBER_INDEX
|
10
10
|
|
11
11
|
def read_data(writers_schema, readers_schema, decoder, initial_record = {})
|
12
12
|
# schema matching
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Avromatic
|
2
|
+
module IO
|
3
|
+
# Subclass DatumWriter to use additional information about union member
|
4
|
+
# index.
|
5
|
+
class DatumWriter < Avro::IO::DatumWriter
|
6
|
+
def write_union(writers_schema, datum, encoder)
|
7
|
+
optional = writers_schema.schemas.first.type_sym == :null
|
8
|
+
if datum.is_a?(Hash) && datum.key?(Avromatic::IO::UNION_MEMBER_INDEX)
|
9
|
+
index_of_schema = datum[Avromatic::IO::UNION_MEMBER_INDEX]
|
10
|
+
# Avromatic does not treat the null of an optional field as part of the union
|
11
|
+
index_of_schema += 1 if optional
|
12
|
+
else
|
13
|
+
index_of_schema = writers_schema.schemas.find_index do |schema|
|
14
|
+
Avro::Schema.validate(schema, datum)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
unless index_of_schema
|
18
|
+
raise Avro::IO::AvroTypeError.new(writers_schema, datum)
|
19
|
+
end
|
20
|
+
encoder.write_long(index_of_schema)
|
21
|
+
write_data(writers_schema.schemas[index_of_schema], datum, encoder)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/avromatic/io.rb
ADDED
data/lib/avromatic/messaging.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'avro_turf/messaging'
|
2
|
-
require 'avromatic/io
|
2
|
+
require 'avromatic/io'
|
3
3
|
|
4
4
|
module Avromatic
|
5
|
-
# Subclass AvroTurf::Messaging to use a custom DatumReader
|
5
|
+
# Subclass AvroTurf::Messaging to use a custom DatumReader and DatumWriter
|
6
6
|
class Messaging < AvroTurf::Messaging
|
7
7
|
attr_reader :registry
|
8
8
|
|
@@ -30,5 +30,30 @@ module Avromatic
|
|
30
30
|
reader = Avromatic::IO::DatumReader.new(writers_schema, readers_schema)
|
31
31
|
reader.read(decoder)
|
32
32
|
end
|
33
|
+
|
34
|
+
def encode(message, schema_name: nil, namespace: @namespace, subject: nil)
|
35
|
+
schema = @schema_store.find(schema_name, namespace)
|
36
|
+
|
37
|
+
# Schemas are registered under the full name of the top level Avro record
|
38
|
+
# type, or `subject` if it's provided.
|
39
|
+
schema_id = @registry.register(subject || schema.fullname, schema)
|
40
|
+
|
41
|
+
stream = StringIO.new
|
42
|
+
encoder = Avro::IO::BinaryEncoder.new(stream)
|
43
|
+
|
44
|
+
# Always start with the magic byte.
|
45
|
+
encoder.write(MAGIC_BYTE)
|
46
|
+
|
47
|
+
# The schema id is encoded as a 4-byte big-endian integer.
|
48
|
+
encoder.write([schema_id].pack('N'))
|
49
|
+
|
50
|
+
# The following line differs from the parent class to use a custom DatumWriter
|
51
|
+
writer = Avromatic::IO::DatumWriter.new(schema)
|
52
|
+
|
53
|
+
# The actual message comes last.
|
54
|
+
writer.write(message, encoder)
|
55
|
+
|
56
|
+
stream.string
|
57
|
+
end
|
33
58
|
end
|
34
59
|
end
|
@@ -15,18 +15,53 @@ module Avromatic
|
|
15
15
|
to: :class
|
16
16
|
private :avro_serializer, :datum_writer, :datum_reader
|
17
17
|
|
18
|
+
EMPTY_ARRAY = [].freeze
|
19
|
+
|
20
|
+
included do
|
21
|
+
@attribute_member_types = {}
|
22
|
+
end
|
23
|
+
|
18
24
|
module ClassMethods
|
19
|
-
def recursive_serialize(value,
|
25
|
+
def recursive_serialize(value, name: nil, member_types: nil)
|
26
|
+
member_types = attribute_member_types(name) if name
|
27
|
+
member_types ||= EMPTY_ARRAY
|
28
|
+
|
20
29
|
if value.is_a?(Avromatic::Model::Attributes)
|
21
|
-
value.value_attributes_for_avro
|
30
|
+
hash = value.value_attributes_for_avro
|
31
|
+
if Avromatic.use_custom_datum_writer
|
32
|
+
member_index = member_types.index(value.class) if member_types.any?
|
33
|
+
hash[Avromatic::IO::UNION_MEMBER_INDEX] = member_index if member_index
|
34
|
+
end
|
35
|
+
hash
|
22
36
|
elsif value.is_a?(Array)
|
23
|
-
value.map { |v| recursive_serialize(v) }
|
37
|
+
value.map { |v| recursive_serialize(v, member_types: member_types) }
|
24
38
|
elsif value.is_a?(Hash)
|
25
|
-
value.each_with_object({}) do |(k, v),
|
26
|
-
|
39
|
+
value.each_with_object({}) do |(k, v), map|
|
40
|
+
map[k] = recursive_serialize(v, member_types: member_types)
|
27
41
|
end
|
28
42
|
else
|
29
|
-
avro_serializer[
|
43
|
+
avro_serializer[name].call(value)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def attribute_member_types(name)
|
50
|
+
@attribute_member_types.fetch(name) do
|
51
|
+
member_types = nil
|
52
|
+
attribute = attribute_set[name] if name
|
53
|
+
if attribute
|
54
|
+
if attribute.primitive == Array &&
|
55
|
+
attribute.member_type.is_a?(Avromatic::Model::Attribute::Union)
|
56
|
+
member_types = attribute.member_type.primitive.types
|
57
|
+
elsif attribute.primitive == Hash &&
|
58
|
+
attribute.value_type.is_a?(Avromatic::Model::Attribute::Union)
|
59
|
+
member_types = attribute.value_type.primitive.types
|
60
|
+
elsif attribute.options[:primitive] == Avromatic::Model::AttributeType::Union
|
61
|
+
member_types = attribute.primitive.types
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@attribute_member_types[name] = member_types
|
30
65
|
end
|
31
66
|
end
|
32
67
|
end
|
@@ -44,15 +79,15 @@ module Avromatic
|
|
44
79
|
avro_hash(value_avro_field_names)
|
45
80
|
end
|
46
81
|
|
47
|
-
private
|
48
|
-
|
49
82
|
def key_attributes_for_avro
|
50
83
|
avro_hash(key_avro_field_names)
|
51
84
|
end
|
52
85
|
|
86
|
+
private
|
87
|
+
|
53
88
|
def avro_hash(fields)
|
54
89
|
attributes.slice(*fields).each_with_object(Hash.new) do |(key, value), result|
|
55
|
-
result[key.to_s] = self.class.recursive_serialize(value, key)
|
90
|
+
result[key.to_s] = self.class.recursive_serialize(value, name: key)
|
56
91
|
end
|
57
92
|
end
|
58
93
|
|
@@ -94,7 +129,11 @@ module Avromatic
|
|
94
129
|
|
95
130
|
module ClassMethods
|
96
131
|
def datum_reader_class
|
97
|
-
Avromatic::IO::DatumReader
|
132
|
+
Avromatic.use_custom_datum_reader ? Avromatic::IO::DatumReader : Avro::IO::DatumReader
|
133
|
+
end
|
134
|
+
|
135
|
+
def datum_writer_class
|
136
|
+
Avromatic.use_custom_datum_writer ? Avromatic::IO::DatumWriter : Avro::IO::DatumWriter
|
98
137
|
end
|
99
138
|
|
100
139
|
# Store a hash of Procs by field name (as a symbol) to convert
|
@@ -106,8 +145,8 @@ module Avromatic
|
|
106
145
|
|
107
146
|
def datum_writer
|
108
147
|
@datum_writer ||= begin
|
109
|
-
hash = { value:
|
110
|
-
hash[:key] =
|
148
|
+
hash = { value: datum_writer_class.new(value_avro_schema) }
|
149
|
+
hash[:key] = datum_writer_class.new(key_avro_schema) if key_avro_schema
|
111
150
|
hash
|
112
151
|
end
|
113
152
|
end
|
data/lib/avromatic/version.rb
CHANGED
data/lib/avromatic.rb
CHANGED
@@ -9,7 +9,8 @@ module Avromatic
|
|
9
9
|
class << self
|
10
10
|
attr_accessor :schema_registry, :registry_url, :schema_store, :logger,
|
11
11
|
:messaging, :type_registry, :nested_models,
|
12
|
-
:use_custom_datum_reader, :
|
12
|
+
:use_custom_datum_reader, :use_custom_datum_writer,
|
13
|
+
:use_schema_fingerprint_lookup
|
13
14
|
|
14
15
|
delegate :register_type, to: :type_registry
|
15
16
|
end
|
@@ -18,6 +19,7 @@ module Avromatic
|
|
18
19
|
self.logger = Logger.new($stdout)
|
19
20
|
self.type_registry = Avromatic::Model::TypeRegistry.new
|
20
21
|
self.use_custom_datum_reader = true
|
22
|
+
self.use_custom_datum_writer = true
|
21
23
|
self.use_schema_fingerprint_lookup = true
|
22
24
|
|
23
25
|
def self.configure
|
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.
|
4
|
+
version: 0.24.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: 2017-06-
|
11
|
+
date: 2017-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: avro
|
@@ -273,7 +273,9 @@ files:
|
|
273
273
|
- gemfiles/rails4_2.gemfile
|
274
274
|
- gemfiles/rails5_0.gemfile
|
275
275
|
- lib/avromatic.rb
|
276
|
+
- lib/avromatic/io.rb
|
276
277
|
- lib/avromatic/io/datum_reader.rb
|
278
|
+
- lib/avromatic/io/datum_writer.rb
|
277
279
|
- lib/avromatic/messaging.rb
|
278
280
|
- lib/avromatic/model.rb
|
279
281
|
- lib/avromatic/model/attribute/abstract_timestamp.rb
|