avromatic 0.13.0 → 0.14.0.rc0
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 +4 -0
- data/lib/avromatic/io/datum_reader.rb +74 -0
- data/lib/avromatic/messaging.rb +32 -0
- data/lib/avromatic/model/attribute/union.rb +15 -6
- data/lib/avromatic/model/messaging_serialization.rb +0 -2
- data/lib/avromatic/model/raw_serialization.rb +6 -3
- data/lib/avromatic/version.rb +1 -1
- data/lib/avromatic.rb +6 -4
- metadata +6 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: dd0e7ff37e01961347eb5222e574d1e1942078ec
         | 
| 4 | 
            +
              data.tar.gz: 3894c1ed617bc9525eac01c72969bed69702c015
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 87701549c8cb63b5636d2647f5b3025fef2f652ca886c876223b9d44457370827d8324536b7492fe786c5b9686f67991074130d69e29901f2739ca63b8098f43
         | 
| 7 | 
            +
              data.tar.gz: 9da95256f8eb308390bcd64517525ac8e4410a812666efcc3eb92bca6865a7f0f481ca7a086062d8844db69fb4b09ce874e7b19f7d7608b33aaa74d3565e3fde
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,9 @@ | |
| 1 1 | 
             
            # avromatic changelog
         | 
| 2 2 |  | 
| 3 | 
            +
            ## v0.14.0
         | 
| 4 | 
            +
            - Add `Avromatic::Messaging` and `Avromatic::IO::DatumReader` classes to
         | 
| 5 | 
            +
              optimize the decoding of Avro unions to Avromatic models.
         | 
| 6 | 
            +
             | 
| 3 7 | 
             
            ## v0.13.0
         | 
| 4 8 | 
             
            - Add interfaces to deserialize as a hash of attributes instead of a model.
         | 
| 5 9 |  | 
| @@ -0,0 +1,74 @@ | |
| 1 | 
            +
            # rubocop:disable Style/WhenThen
         | 
| 2 | 
            +
            module Avromatic
         | 
| 3 | 
            +
              module IO
         | 
| 4 | 
            +
                # Subclass DatumReader to include additional information about the union
         | 
| 5 | 
            +
                # index used.
         | 
| 6 | 
            +
                class DatumReader < Avro::IO::DatumReader
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  UNION_MEMBER_INDEX = '__avromatic_member_index'.freeze
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def read_data(writers_schema, readers_schema, decoder, initial_record = {})
         | 
| 11 | 
            +
                    # schema matching
         | 
| 12 | 
            +
                    unless self.class.match_schemas(writers_schema, readers_schema)
         | 
| 13 | 
            +
                      raise SchemaMatchException.new(writers_schema, readers_schema)
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    # schema resolution: reader's schema is a union, writer's schema is not
         | 
| 17 | 
            +
                    if writers_schema.type_sym != :union && readers_schema.type_sym == :union
         | 
| 18 | 
            +
                      rs_index = readers_schema.schemas.find_index do |s|
         | 
| 19 | 
            +
                        self.class.match_schemas(writers_schema, s)
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      union_info = { UNION_MEMBER_INDEX => rs_index }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      return read_data(writers_schema, readers_schema.schemas[rs_index], decoder, union_info) if rs_index
         | 
| 25 | 
            +
                      raise SchemaMatchException.new(writers_schema, readers_schema)
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    # function dispatch for reading data based on type of writer's
         | 
| 29 | 
            +
                    # schema
         | 
| 30 | 
            +
                    datum = case writers_schema.type_sym
         | 
| 31 | 
            +
                            when :null;    decoder.read_null
         | 
| 32 | 
            +
                            when :boolean; decoder.read_boolean
         | 
| 33 | 
            +
                            when :string;  decoder.read_string
         | 
| 34 | 
            +
                            when :int;     decoder.read_int
         | 
| 35 | 
            +
                            when :long;    decoder.read_long
         | 
| 36 | 
            +
                            when :float;   decoder.read_float
         | 
| 37 | 
            +
                            when :double;  decoder.read_double
         | 
| 38 | 
            +
                            when :bytes;   decoder.read_bytes
         | 
| 39 | 
            +
                            when :fixed;   read_fixed(writers_schema, readers_schema, decoder)
         | 
| 40 | 
            +
                            when :enum;    read_enum(writers_schema, readers_schema, decoder)
         | 
| 41 | 
            +
                            when :array;   read_array(writers_schema, readers_schema, decoder)
         | 
| 42 | 
            +
                            when :map;     read_map(writers_schema, readers_schema, decoder)
         | 
| 43 | 
            +
                            when :union;   read_union(writers_schema, readers_schema, decoder)
         | 
| 44 | 
            +
                            when :record, :error, :request; read_record(writers_schema, readers_schema, decoder, initial_record)
         | 
| 45 | 
            +
                            else
         | 
| 46 | 
            +
                              raise AvroError.new("Cannot read unknown schema type: #{writers_schema.type}")
         | 
| 47 | 
            +
                            end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    if readers_schema.respond_to?(:logical_type)
         | 
| 50 | 
            +
                      readers_schema.type_adapter.decode(datum)
         | 
| 51 | 
            +
                    else
         | 
| 52 | 
            +
                      datum
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  # Override to specify initial record that may contain union index
         | 
| 57 | 
            +
                  def read_record(writers_schema, readers_schema, decoder, initial_record = {})
         | 
| 58 | 
            +
                    readers_fields_hash = readers_schema.fields_hash
         | 
| 59 | 
            +
                    read_record = Avromatic.use_custom_datum_reader ? initial_record : {}
         | 
| 60 | 
            +
                    writers_schema.fields.each do |field|
         | 
| 61 | 
            +
                      readers_field = readers_fields_hash[field.name]
         | 
| 62 | 
            +
                      if readers_field
         | 
| 63 | 
            +
                        field_val = read_data(field.type, readers_field.type, decoder)
         | 
| 64 | 
            +
                        read_record[field.name] = field_val
         | 
| 65 | 
            +
                      else
         | 
| 66 | 
            +
                        skip_data(field.type, decoder)
         | 
| 67 | 
            +
                      end
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    read_record
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            require 'avro_turf/messaging'
         | 
| 2 | 
            +
            require 'avromatic/io/datum_reader'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Avromatic
         | 
| 5 | 
            +
              # Subclass AvroTurf::Messaging to use a custom DatumReader
         | 
| 6 | 
            +
              class Messaging < AvroTurf::Messaging
         | 
| 7 | 
            +
                def decode(data, schema_name: nil, namespace: @namespace)
         | 
| 8 | 
            +
                  readers_schema = schema_name && @schema_store.find(schema_name, namespace)
         | 
| 9 | 
            +
                  stream = StringIO.new(data)
         | 
| 10 | 
            +
                  decoder = Avro::IO::BinaryDecoder.new(stream)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # The first byte is MAGIC!!!
         | 
| 13 | 
            +
                  magic_byte = decoder.read(1)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  if magic_byte != MAGIC_BYTE
         | 
| 16 | 
            +
                    raise "Expected data to begin with a magic byte, got `#{magic_byte.inspect}`"
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  # The schema id is a 4-byte big-endian integer.
         | 
| 20 | 
            +
                  schema_id = decoder.read(4).unpack('N').first
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  writers_schema = @schemas_by_id.fetch(schema_id) do
         | 
| 23 | 
            +
                    schema_json = @registry.fetch(schema_id)
         | 
| 24 | 
            +
                    @schemas_by_id[schema_id] = Avro::Schema.parse(schema_json)
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  # The following line was changed to use a custom DatumReader
         | 
| 28 | 
            +
                  reader = Avromatic::IO::DatumReader.new(writers_schema, readers_schema)
         | 
| 29 | 
            +
                  reader.read(decoder)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            require 'avromatic/model/attribute_type/union'
         | 
| 2 | 
            +
            require 'avromatic/io/datum_reader'
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Avromatic
         | 
| 4 5 | 
             
              module Model
         | 
| @@ -12,6 +13,8 @@ module Avromatic | |
| 12 13 | 
             
                  class Union < Virtus::Attribute
         | 
| 13 14 | 
             
                    primitive Avromatic::Model::AttributeType::Union
         | 
| 14 15 |  | 
| 16 | 
            +
                    MEMBER_INDEX = ::Avromatic::IO::DatumReader::UNION_MEMBER_INDEX
         | 
| 17 | 
            +
             | 
| 15 18 | 
             
                    def initialize(*)
         | 
| 16 19 | 
             
                      super
         | 
| 17 20 |  | 
| @@ -24,12 +27,11 @@ module Avromatic | |
| 24 27 | 
             
                      return input if value_coerced?(input)
         | 
| 25 28 |  | 
| 26 29 | 
             
                      result = nil
         | 
| 27 | 
            -
                       | 
| 28 | 
            -
                         | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
                          nil
         | 
| 30 | 
            +
                      if input.key?(MEMBER_INDEX)
         | 
| 31 | 
            +
                        result = safe_coerce(member_attributes[input.delete(MEMBER_INDEX)], input)
         | 
| 32 | 
            +
                      else
         | 
| 33 | 
            +
                        member_attributes.find do |union_attribute|
         | 
| 34 | 
            +
                          result = safe_coerce(union_attribute, input)
         | 
| 33 35 | 
             
                        end
         | 
| 34 36 | 
             
                      end
         | 
| 35 37 | 
             
                      result
         | 
| @@ -43,6 +45,13 @@ module Avromatic | |
| 43 45 |  | 
| 44 46 | 
             
                    private
         | 
| 45 47 |  | 
| 48 | 
            +
                    def safe_coerce(member_attribute, input)
         | 
| 49 | 
            +
                      coerced = member_attribute.coerce(input)
         | 
| 50 | 
            +
                      coerced unless coerced.is_a?(Avromatic::Model::Attributes) && coerced.invalid?
         | 
| 51 | 
            +
                    rescue
         | 
| 52 | 
            +
                      nil
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 46 55 | 
             
                    def member_attributes
         | 
| 47 56 | 
             
                      @member_attributes ||= Array.new
         | 
| 48 57 | 
             
                    end
         | 
| @@ -88,11 +88,14 @@ module Avromatic | |
| 88 88 | 
             
                    end
         | 
| 89 89 |  | 
| 90 90 | 
             
                    def custom_datum_reader(schema, key_or_value)
         | 
| 91 | 
            -
                       | 
| 91 | 
            +
                      datum_reader_class.new(schema, send("#{key_or_value}_avro_schema"))
         | 
| 92 92 | 
             
                    end
         | 
| 93 93 | 
             
                  end
         | 
| 94 94 |  | 
| 95 95 | 
             
                  module ClassMethods
         | 
| 96 | 
            +
                    def datum_reader_class
         | 
| 97 | 
            +
                      Avromatic::IO::DatumReader
         | 
| 98 | 
            +
                    end
         | 
| 96 99 |  | 
| 97 100 | 
             
                    # Store a hash of Procs by field name (as a symbol) to convert
         | 
| 98 101 | 
             
                    # the value before Avro serialization.
         | 
| @@ -111,8 +114,8 @@ module Avromatic | |
| 111 114 |  | 
| 112 115 | 
             
                    def datum_reader
         | 
| 113 116 | 
             
                      @datum_reader ||= begin
         | 
| 114 | 
            -
                        hash = { value:  | 
| 115 | 
            -
                        hash[:key] =  | 
| 117 | 
            +
                        hash = { value: datum_reader_class.new(value_avro_schema) }
         | 
| 118 | 
            +
                        hash[:key] = datum_reader_class.new(key_avro_schema) if key_avro_schema
         | 
| 116 119 | 
             
                        hash
         | 
| 117 120 | 
             
                      end
         | 
| 118 121 | 
             
                    end
         | 
    
        data/lib/avromatic/version.rb
    CHANGED
    
    
    
        data/lib/avromatic.rb
    CHANGED
    
    | @@ -1,14 +1,15 @@ | |
| 1 1 | 
             
            require 'avromatic/version'
         | 
| 2 | 
            +
            require 'avro_turf'
         | 
| 2 3 | 
             
            require 'avromatic/model'
         | 
| 3 4 | 
             
            require 'avromatic/model_registry'
         | 
| 4 | 
            -
            require ' | 
| 5 | 
            -
            require 'avro_turf/messaging'
         | 
| 5 | 
            +
            require 'avromatic/messaging'
         | 
| 6 6 | 
             
            require 'active_support/core_ext/string/inflections'
         | 
| 7 7 |  | 
| 8 8 | 
             
            module Avromatic
         | 
| 9 9 | 
             
              class << self
         | 
| 10 10 | 
             
                attr_accessor :schema_registry, :registry_url, :schema_store, :logger,
         | 
| 11 | 
            -
                              :messaging, :type_registry, :nested_models
         | 
| 11 | 
            +
                              :messaging, :type_registry, :nested_models,
         | 
| 12 | 
            +
                              :use_custom_datum_reader
         | 
| 12 13 |  | 
| 13 14 | 
             
                delegate :register_type, to: :type_registry
         | 
| 14 15 | 
             
              end
         | 
| @@ -16,6 +17,7 @@ module Avromatic | |
| 16 17 | 
             
              self.nested_models = ModelRegistry.new
         | 
| 17 18 | 
             
              self.logger = Logger.new($stdout)
         | 
| 18 19 | 
             
              self.type_registry = Avromatic::Model::TypeRegistry.new
         | 
| 20 | 
            +
              self.use_custom_datum_reader = true
         | 
| 19 21 |  | 
| 20 22 | 
             
              def self.configure
         | 
| 21 23 | 
             
                yield self
         | 
| @@ -31,7 +33,7 @@ module Avromatic | |
| 31 33 |  | 
| 32 34 | 
             
              def self.build_messaging
         | 
| 33 35 | 
             
                raise 'Avromatic must be configured with a schema_store' unless schema_store
         | 
| 34 | 
            -
                 | 
| 36 | 
            +
                Avromatic::Messaging.new(
         | 
| 35 37 | 
             
                  registry: schema_registry || build_schema_registry,
         | 
| 36 38 | 
             
                  schema_store: schema_store,
         | 
| 37 39 | 
             
                  logger: logger
         | 
    
        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.14.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: 2017-01- | 
| 11 | 
            +
            date: 2017-01-17 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: avro
         | 
| @@ -245,6 +245,8 @@ files: | |
| 245 245 | 
             
            - gemfiles/rails4_1.gemfile
         | 
| 246 246 | 
             
            - gemfiles/rails4_2.gemfile
         | 
| 247 247 | 
             
            - lib/avromatic.rb
         | 
| 248 | 
            +
            - lib/avromatic/io/datum_reader.rb
         | 
| 249 | 
            +
            - lib/avromatic/messaging.rb
         | 
| 248 250 | 
             
            - lib/avromatic/model.rb
         | 
| 249 251 | 
             
            - lib/avromatic/model/attribute/abstract_timestamp.rb
         | 
| 250 252 | 
             
            - lib/avromatic/model/attribute/record.rb
         | 
| @@ -287,9 +289,9 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 287 289 | 
             
                  version: '0'
         | 
| 288 290 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 289 291 | 
             
              requirements:
         | 
| 290 | 
            -
              - - " | 
| 292 | 
            +
              - - ">"
         | 
| 291 293 | 
             
                - !ruby/object:Gem::Version
         | 
| 292 | 
            -
                  version:  | 
| 294 | 
            +
                  version: 1.3.1
         | 
| 293 295 | 
             
            requirements: []
         | 
| 294 296 | 
             
            rubyforge_project: 
         | 
| 295 297 | 
             
            rubygems_version: 2.6.8
         |