deimos-ruby 2.6.0.pre.beta1 → 2.6.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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -2
  3. data/README.md +45 -20
  4. data/deimos-ruby.gemspec +0 -1
  5. data/docs/CONFIGURATION.md +5 -11
  6. data/lib/deimos/active_record_consume/batch_consumption.rb +128 -31
  7. data/lib/deimos/active_record_consume/batch_record.rb +3 -0
  8. data/lib/deimos/active_record_consume/message_consumption.rb +2 -2
  9. data/lib/deimos/active_record_consumer.rb +6 -1
  10. data/lib/deimos/active_record_producer.rb +1 -1
  11. data/lib/deimos/config/configuration.rb +11 -34
  12. data/lib/deimos/exceptions.rb +17 -0
  13. data/lib/deimos/ext/consumer_route.rb +3 -1
  14. data/lib/deimos/ext/producer_middleware.rb +2 -2
  15. data/lib/deimos/logging.rb +0 -9
  16. data/lib/deimos/producer.rb +5 -5
  17. data/lib/deimos/schema_backends/avro_base.rb +36 -7
  18. data/lib/deimos/schema_class/base.rb +62 -0
  19. data/lib/deimos/schema_class/enum.rb +49 -0
  20. data/lib/deimos/schema_class/record.rb +100 -0
  21. data/lib/deimos/transcoder.rb +6 -6
  22. data/lib/deimos/utils/deadlock_retry.rb +16 -2
  23. data/lib/deimos/utils/schema_class.rb +47 -14
  24. data/lib/deimos/version.rb +1 -1
  25. data/lib/deimos.rb +4 -3
  26. data/lib/generators/deimos/schema_class/templates/schema_class.rb.tt +8 -0
  27. data/lib/generators/deimos/schema_class/templates/schema_enum.rb.tt +13 -0
  28. data/lib/generators/deimos/schema_class/templates/schema_record.rb.tt +102 -0
  29. data/lib/generators/deimos/schema_class_generator.rb +369 -0
  30. data/lib/tasks/deimos.rake +4 -16
  31. data/regenerate_test_schema_classes.rb +3 -12
  32. data/spec/active_record_batch_consumer_association_spec.rb +1 -1
  33. data/spec/active_record_batch_consumer_spec.rb +286 -1
  34. data/spec/active_record_consume/batch_consumption_spec.rb +1 -1
  35. data/spec/active_record_consumer_spec.rb +3 -3
  36. data/spec/active_record_producer_spec.rb +1 -1
  37. data/spec/batch_consumer_spec.rb +2 -2
  38. data/spec/consumer_spec.rb +5 -5
  39. data/spec/generators/schema_class/my_schema_spec.rb +16 -0
  40. data/spec/generators/schema_class/my_schema_with_circular_reference_spec.rb +98 -0
  41. data/spec/generators/schema_class/my_schema_with_complex_types_spec.rb +237 -0
  42. data/spec/generators/schema_class_generator_spec.rb +283 -0
  43. data/spec/schemas/my_namespace/generated.rb +4 -4
  44. data/spec/schemas/my_namespace/my_long_namespace_schema.rb +2 -2
  45. data/spec/schemas/my_namespace/my_nested_schema.rb +3 -3
  46. data/spec/schemas/my_namespace/my_schema.rb +2 -2
  47. data/spec/schemas/my_namespace/my_schema_compound_key.rb +2 -2
  48. data/spec/schemas/my_namespace/my_schema_id_key.rb +2 -2
  49. data/spec/schemas/my_namespace/my_schema_key.rb +2 -2
  50. data/spec/schemas/my_namespace/my_schema_with_boolean.rb +2 -2
  51. data/spec/schemas/my_namespace/my_schema_with_circular_reference.rb +3 -3
  52. data/spec/schemas/my_namespace/my_schema_with_complex_type.rb +6 -6
  53. data/spec/schemas/my_namespace/my_schema_with_date_time.rb +2 -2
  54. data/spec/schemas/my_namespace/my_schema_with_id.rb +2 -2
  55. data/spec/schemas/my_namespace/my_schema_with_title.rb +2 -2
  56. data/spec/schemas/my_namespace/my_schema_with_union_type.rb +6 -6
  57. data/spec/schemas/my_namespace/my_schema_with_unique_id.rb +2 -2
  58. data/spec/schemas/my_namespace/my_updated_schema.rb +1 -1
  59. data/spec/schemas/my_namespace/request/create_topic.rb +2 -2
  60. data/spec/schemas/my_namespace/request/index.rb +2 -2
  61. data/spec/schemas/my_namespace/request/update_request.rb +2 -2
  62. data/spec/schemas/my_namespace/response/create_topic.rb +2 -2
  63. data/spec/schemas/my_namespace/response/index.rb +2 -2
  64. data/spec/schemas/my_namespace/response/update_response.rb +2 -2
  65. data/spec/schemas/my_namespace/wibble.rb +2 -2
  66. data/spec/schemas/my_namespace/widget.rb +2 -2
  67. data/spec/schemas/my_namespace/widget_the_second.rb +2 -2
  68. data/spec/schemas/my_namespace/widget_the_third.rb +2 -2
  69. data/spec/snapshots/consumers-no-nest.snap +1740 -0
  70. data/spec/snapshots/consumers.snap +1720 -0
  71. data/spec/snapshots/consumers_and_producers-no-nest.snap +1740 -0
  72. data/spec/snapshots/consumers_and_producers.snap +1720 -0
  73. data/spec/snapshots/consumers_circular-no-nest.snap +1740 -0
  74. data/spec/snapshots/consumers_circular.snap +1720 -0
  75. data/spec/snapshots/consumers_complex_types-no-nest.snap +1740 -0
  76. data/spec/snapshots/consumers_complex_types.snap +1720 -0
  77. data/spec/snapshots/consumers_nested-no-nest.snap +1740 -0
  78. data/spec/snapshots/consumers_nested.snap +1720 -0
  79. data/spec/snapshots/namespace_folders.snap +1800 -0
  80. data/spec/snapshots/namespace_map.snap +1800 -0
  81. data/spec/snapshots/producers_with_key-no-nest.snap +1740 -0
  82. data/spec/snapshots/producers_with_key.snap +1720 -0
  83. data/spec/spec_helper.rb +2 -2
  84. data/spec/utils/db_poller_spec.rb +1 -1
  85. data/spec/utils/deadlock_retry_spec.rb +57 -0
  86. metadata +27 -18
  87. data/lib/deimos/schema_class.rb +0 -72
  88. data/spec/schema_class_spec.rb +0 -65
@@ -75,7 +75,7 @@ module Deimos
75
75
  end
76
76
 
77
77
  # Publish the payload to the topic.
78
- # @param payload [Hash, AvroGen::SchemaClass::Record] with an optional payload_key hash key.
78
+ # @param payload [Hash, SchemaClass::Record] with an optional payload_key hash key.
79
79
  # @param topic [String] if specifying the topic
80
80
  # @param headers [Hash] if specifying headers
81
81
  # @return [void]
@@ -99,7 +99,7 @@ module Deimos
99
99
  end
100
100
 
101
101
  # Publish a list of messages.
102
- # @param payloads [Array<Hash, AvroGen::SchemaClass::Record>] with optional payload_key hash key.
102
+ # @param payloads [Array<Hash, SchemaClass::Record>] with optional payload_key hash key.
103
103
  # @param sync [Boolean] if given, override the default setting of
104
104
  # whether to publish synchronously.
105
105
  # @param force_send [Boolean] if true, ignore the configured backend
@@ -112,7 +112,7 @@ module Deimos
112
112
 
113
113
  messages = Array(payloads).map do |p|
114
114
  payload = p
115
- payload = payload.to_h if p.is_a?(AvroGen::SchemaClass::Record)
115
+ payload = payload.to_h if p.is_a?(SchemaClass::Record)
116
116
  m = {
117
117
  payload: payload,
118
118
  headers: headers,
@@ -121,10 +121,10 @@ module Deimos
121
121
  }
122
122
  if payload.is_a?(Hash) && payload.key?(:key) && payload.key?(:message)
123
123
  m[:key] = payload[:key]
124
- m[:key] = m[:key].to_h if m[:key].nil? || m[:key].is_a?(AvroGen::SchemaClass::Record)
124
+ m[:key] = m[:key].to_h if m[:key].nil? || m[:key].is_a?(SchemaClass::Record)
125
125
  m[:payload] = payload[:message]
126
126
  m[:payload] = m[:payload].to_h if m[:payload].nil? ||
127
- m[:payload].is_a?(AvroGen::SchemaClass::Record)
127
+ m[:payload].is_a?(SchemaClass::Record)
128
128
  end
129
129
  m
130
130
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require_relative 'base'
4
4
  require 'schema_registry_client'
5
- require 'avro_gen/avro_parser'
6
5
  require_relative 'avro_schema_coercer'
7
6
 
8
7
  module Deimos
@@ -107,25 +106,55 @@ module Deimos
107
106
 
108
107
  # @param schema [Avro::Schema::NamedSchema] A named schema
109
108
  # @return [String]
110
- # @deprecated Use AvroGen::AvroParser.schema_classname instead.
111
109
  def self.schema_classname(schema)
112
- AvroGen::AvroParser.schema_classname(schema)
110
+ schema.name.underscore.camelize.singularize
113
111
  end
114
112
 
115
113
  # Converts Avro::Schema::NamedSchema's to String form for generated YARD docs.
114
+ # Recursively handles the typing for Arrays, Maps and Unions.
116
115
  # @param avro_schema [Avro::Schema::NamedSchema]
117
116
  # @return [String] A string representation of the Type of this SchemaField
118
- # @deprecated Use AvroGen::AvroParser.field_type instead.
119
117
  def self.field_type(avro_schema)
120
- AvroGen::AvroParser.field_type(avro_schema)
118
+ case avro_schema.type_sym
119
+ when :string, :boolean
120
+ avro_schema.type_sym.to_s.titleize
121
+ when :int, :long
122
+ 'Integer'
123
+ when :float, :double
124
+ 'Float'
125
+ when :record, :enum
126
+ schema_classname(avro_schema)
127
+ when :array
128
+ arr_t = field_type(Deimos::SchemaField.new('n/a', avro_schema.items).type)
129
+ "Array<#{arr_t}>"
130
+ when :map
131
+ map_t = field_type(Deimos::SchemaField.new('n/a', avro_schema.values).type)
132
+ "Hash<String, #{map_t}>"
133
+ when :union
134
+ types = avro_schema.schemas.map do |t|
135
+ field_type(Deimos::SchemaField.new('n/a', t).type)
136
+ end
137
+ types.join(', ')
138
+ when :null
139
+ 'nil'
140
+ end
121
141
  end
122
142
 
123
143
  # Returns the base type of this schema. Decodes Arrays, Maps and Unions
124
144
  # @param schema [Avro::Schema::NamedSchema]
125
145
  # @return [Avro::Schema::NamedSchema]
126
- # @deprecated Use AvroGen::AvroParser.schema_base_class instead.
127
146
  def self.schema_base_class(schema)
128
- AvroGen::AvroParser.schema_base_class(schema)
147
+ case schema.type_sym
148
+ when :array
149
+ schema_base_class(schema.items)
150
+ when :map
151
+ schema_base_class(schema.values)
152
+ when :union
153
+ schema.schemas.map(&method(:schema_base_class)).
154
+ reject { |s| s.type_sym == :null }.first
155
+ else
156
+ schema
157
+ end
129
158
  end
130
159
 
131
160
  def generate_key_schema(field_name)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Deimos
6
+ module SchemaClass
7
+ # Base Class for Schema Classes generated from Avro.
8
+ class Base
9
+
10
+ # @param _args [Array<Object>]
11
+ def initialize(*_args)
12
+ end
13
+
14
+ # Converts the object to a hash which can be used for debugging or comparing objects.
15
+ # @param _opts [Hash]
16
+ # @return [Hash] a hash representation of the payload
17
+ def as_json(_opts={})
18
+ raise MissingImplementationError
19
+ end
20
+
21
+ # @param key [String,Symbol]
22
+ # @param val [Object]
23
+ # @return [void]
24
+ def []=(key, val)
25
+ self.send("#{key}=", val)
26
+ end
27
+
28
+ # @param other [SchemaClass::Base]
29
+ # @return [Boolean]
30
+ def ==(other)
31
+ comparison = other
32
+ if other.class == self.class
33
+ comparison = other.as_json
34
+ end
35
+
36
+ comparison == self.as_json
37
+ end
38
+
39
+ alias_method :eql?, :==
40
+
41
+ # @return [String]
42
+ def inspect
43
+ klass = self.class
44
+ "#{klass}(#{self.as_json})"
45
+ end
46
+
47
+ # Initializes this class from a given value
48
+ # @param value [Object]
49
+ # @return [SchemaClass::Base]
50
+ def self.initialize_from_value(_value)
51
+ raise MissingImplementationError
52
+ end
53
+
54
+ protected
55
+
56
+ # @return [Integer]
57
+ def hash
58
+ as_json.hash
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require 'json'
5
+
6
+ module Deimos
7
+ module SchemaClass
8
+ # Base Class for Enum Classes generated from Avro.
9
+ class Enum < Base
10
+
11
+ # @return [String]
12
+ attr_accessor :value
13
+
14
+ # @param other [Deimos::SchemaClass::Enum]
15
+ # @return [Boolean]
16
+ def ==(other)
17
+ other.is_a?(self.class) ? other.value == @value : other == @value
18
+ end
19
+
20
+ # @return [String]
21
+ def to_s
22
+ @value.to_s
23
+ end
24
+
25
+ # @param value [String]
26
+ def initialize(value)
27
+ @value = value
28
+ end
29
+
30
+ # Returns all the valid symbols for this enum.
31
+ # @return [Array<String>]
32
+ def symbols
33
+ raise MissingImplementationError
34
+ end
35
+
36
+ # @return [String]
37
+ def as_json(_opts={})
38
+ @value
39
+ end
40
+
41
+ # @return [SchemaClass::Enum]
42
+ def self.initialize_from_value(value, from_message: false)
43
+ return nil if value.nil?
44
+
45
+ value.is_a?(self) ? value : self.new(value)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require 'json'
5
+
6
+ module Deimos
7
+ module SchemaClass
8
+ # Base Class of Record Classes generated from Avro.
9
+ class Record < Base
10
+
11
+ attr_accessor :tombstone_key, :_from_message
12
+
13
+ # Converts the object attributes to a hash which can be used for Kafka
14
+ # @return [Hash] the payload as a hash.
15
+ def to_h
16
+ if self.tombstone_key
17
+ { payload_key: self.tombstone_key&.as_json }
18
+ else
19
+ self.as_json
20
+ end
21
+ end
22
+
23
+ # Merge a hash or an identical schema object with this one and return a new object.
24
+ # @param other_hash [Hash,SchemaClass::Base]
25
+ # @return [SchemaClass::Base]
26
+ def merge(other_hash)
27
+ obj = self.class.new(**self.to_h.symbolize_keys)
28
+ other_hash.to_h.each do |k, v|
29
+ obj.send("#{k}=", v)
30
+ end
31
+ obj
32
+ end
33
+
34
+ # Element access method as if this Object were a hash
35
+ # @param key[String,Symbol]
36
+ # @return [Object] The value of the attribute if exists, nil otherwise
37
+ def [](key)
38
+ self.try(key.to_sym)
39
+ end
40
+
41
+ # @return [SchemaClass::Record]
42
+ def with_indifferent_access
43
+ self
44
+ end
45
+
46
+ # Returns the schema name of the inheriting class.
47
+ # @return [String]
48
+ def schema
49
+ raise MissingImplementationError
50
+ end
51
+
52
+ # Returns the namespace for the schema of the inheriting class.
53
+ # @return [String]
54
+ def namespace
55
+ raise MissingImplementationError
56
+ end
57
+
58
+ # Returns the full schema name of the inheriting class.
59
+ # @return [String]
60
+ def full_schema
61
+ "#{namespace}.#{schema}"
62
+ end
63
+
64
+ # Returns the schema validator from the schema backend
65
+ # @return [Deimos::SchemaBackends::Base]
66
+ def validator
67
+ Deimos.schema_backend(schema: schema, namespace: namespace)
68
+ end
69
+
70
+ # @return [Array<String>] an array of fields names in the schema.
71
+ def schema_fields
72
+ validator.schema_fields.map(&:name)
73
+ end
74
+
75
+ # Used internally within Deimos so that we don't crash on unknown fields that come from
76
+ # a backwards compatible schema.
77
+ # @param kwargs [Hash] the attributes to set on the new object.
78
+ # @return [SchemaClass::Record]
79
+ def self.new_from_message(**kwargs)
80
+ record = self.new
81
+ attrs = kwargs.select { |k, _v| record.respond_to?("#{k}=") }
82
+ self.new(_from_message: true, **attrs)
83
+ end
84
+
85
+ # @return [SchemaClass::Record]
86
+ # @param from_message [Boolean] whether it's being initialized from a real Avro message.
87
+ def self.initialize_from_value(value, from_message: false)
88
+ return nil if value.nil?
89
+
90
+ return value if value.is_a?(self)
91
+
92
+ if from_message
93
+ self.new_from_message(**value&.symbolize_keys || {})
94
+ else
95
+ self.new(**value&.symbolize_keys || {})
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -58,9 +58,9 @@ module Deimos
58
58
 
59
59
  schema_key = decoded_key.is_a?(Hash) ? decoded_key : { self.key_field => decoded_key }
60
60
 
61
- AvroGen::SchemaClass.instance(schema_key,
62
- "#{@schema}_key",
63
- @namespace)
61
+ Utils::SchemaClass.instance(schema_key,
62
+ "#{@schema}_key",
63
+ @namespace)
64
64
  end
65
65
 
66
66
  # @param payload [String]
@@ -71,9 +71,9 @@ module Deimos
71
71
  decoded_payload = self.backend.decode(payload)
72
72
  return decoded_payload if !@use_schema_classes || !decoded_payload.is_a?(Hash)
73
73
 
74
- AvroGen::SchemaClass.instance(decoded_payload,
75
- @schema,
76
- @namespace)
74
+ Utils::SchemaClass.instance(decoded_payload,
75
+ @schema,
76
+ @namespace)
77
77
  end
78
78
 
79
79
  # @param payload [Object]
@@ -21,6 +21,15 @@ module Deimos
21
21
  'deadlock detected'
22
22
  ].freeze
23
23
 
24
+ # Whether the given exception is a deadlock or lock wait timeout, i.e. transient
25
+ # contention on the database rather than a problem with the data being written.
26
+ # @param error [Exception]
27
+ # @return [Boolean]
28
+ def deadlock?(error)
29
+ error.is_a?(ActiveRecord::StatementInvalid) &&
30
+ DEADLOCK_MESSAGES.any? { |m| error.message.include?(m) }
31
+ end
32
+
24
33
  # Retry the given block when encountering a deadlock. For any other
25
34
  # exceptions, they are reraised. This is used to handle cases where
26
35
  # the database may be busy but the transaction would succeed if
@@ -28,10 +37,15 @@ module Deimos
28
37
  # be wrapped in a transaction.
29
38
  # Sleeps for a random number of seconds to prevent multiple transactions
30
39
  # from retrying at the same time.
31
- # @param tags [Array] Tags to attach when logging and reporting metrics.
40
+ # @param tags [Array, String, nil] Tags to attach when logging and reporting metrics.
41
+ # A scalar (e.g. a topic String) is coerced to a single-element array.
32
42
  # @yield Yields to the block that may deadlock.
33
43
  # @return [void]
34
44
  def wrap(tags=[])
45
+ # Normalize to an Array so downstream logging/metrics (and dogstatsd's
46
+ # `tags.to_a`) never receive a bare String, which would raise
47
+ # NoMethodError inside this rescue and defeat the retry mechanism.
48
+ tags = Array(tags)
35
49
  count = RETRY_COUNT
36
50
 
37
51
  begin
@@ -40,7 +54,7 @@ module Deimos
40
54
  end
41
55
  rescue ActiveRecord::StatementInvalid => e
42
56
  # Reraise if not a known deadlock
43
- raise if DEADLOCK_MESSAGES.none? { |m| e.message.include?(m) }
57
+ raise unless deadlock?(e)
44
58
 
45
59
  # Reraise if all retries exhausted
46
60
  raise if count <= 0
@@ -1,27 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'avro_gen'
4
-
5
3
  module Deimos
6
4
  module Utils
7
- # Backwards-compatible shim. The schema class helpers moved to
8
- # AvroGen::SchemaClass. This delegates with a deprecation warning.
5
+ # Class used by SchemaClassGenerator and Consumer/Producer interfaces
9
6
  module SchemaClass
10
7
  class << self
11
- def method_missing(name, ...)
12
- if AvroGen::SchemaClass.respond_to?(name)
13
- Deimos::Logging.deprecate(
14
- "Deimos::Utils::SchemaClass.#{name} is deprecated; use AvroGen::SchemaClass.#{name} instead."
15
- )
16
- AvroGen::SchemaClass.send(name, ...)
17
- else
18
- super
8
+
9
+ # @param namespace [String]
10
+ # @return [Array<String>]
11
+ def modules_for(namespace)
12
+ modules = ['Schemas']
13
+ namespace_override = nil
14
+ module_namespace = namespace
15
+
16
+ if Deimos.config.schema.use_full_namespace
17
+ if Deimos.config.schema.schema_namespace_map.present?
18
+ namespace_keys = Deimos.config.schema.schema_namespace_map.keys.sort_by { |k| -k.length }
19
+ namespace_override = namespace_keys.find { |k| module_namespace.include?(k) }
20
+ end
21
+
22
+ if namespace_override.present?
23
+ # override default module
24
+ modules = Array(Deimos.config.schema.schema_namespace_map[namespace_override])
25
+ module_namespace = module_namespace.gsub(/#{namespace_override}\.?/, '')
26
+ end
27
+
28
+ namespace_folders = module_namespace.split('.').map { |f| f.underscore.camelize }
29
+ modules.concat(namespace_folders) if namespace_folders.any?
19
30
  end
31
+
32
+ modules
20
33
  end
21
34
 
22
- def respond_to_missing?(name, include_private=false)
23
- AvroGen::SchemaClass.respond_to?(name, include_private) || super
35
+ # Converts a raw payload into an instance of the Schema Class
36
+ # @param payload [Hash, Deimos::SchemaClass::Base]
37
+ # @param schema [String]
38
+ # @param namespace [String]
39
+ # @return [Deimos::SchemaClass::Record]
40
+ def instance(payload, schema, namespace='')
41
+ return payload if payload.is_a?(Deimos::SchemaClass::Base)
42
+
43
+ klass = klass(schema, namespace)
44
+ return payload if klass.nil? || payload.nil?
45
+
46
+ klass.new_from_message(**payload.symbolize_keys)
24
47
  end
48
+
49
+ # Determine and return the SchemaClass with the provided schema and namespace
50
+ # @param schema [String]
51
+ # @param namespace [String]
52
+ # @return [Deimos::SchemaClass]
53
+ def klass(schema, namespace)
54
+ constants = modules_for(namespace) + [schema.underscore.camelize.singularize]
55
+ constants.join('::').safe_constantize
56
+ end
57
+
25
58
  end
26
59
  end
27
60
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deimos
4
- VERSION = '2.6.0-beta1'
4
+ VERSION = '2.6.0'
5
5
  end
data/lib/deimos.rb CHANGED
@@ -17,9 +17,9 @@ require 'deimos/backends/kafka_async'
17
17
  require 'deimos/backends/test'
18
18
 
19
19
  require 'deimos/schema_backends/base'
20
- require 'avro_gen'
21
- require 'deimos/schema_class'
22
20
  require 'deimos/utils/schema_class'
21
+ require 'deimos/schema_class/enum'
22
+ require 'deimos/schema_class/record'
23
23
 
24
24
  require 'deimos/ext/schema_route'
25
25
  require 'deimos/ext/consumer_route'
@@ -49,6 +49,7 @@ module Deimos
49
49
  deimos.encode_message
50
50
  deimos.batch_consumption.invalid_records
51
51
  deimos.batch_consumption.valid_records
52
+ deimos.batch_consumption.initial_failure
52
53
  deimos.outbox.produce
53
54
  ).freeze
54
55
 
@@ -102,7 +103,7 @@ module Deimos
102
103
  # Initialize an instance of the provided schema
103
104
  # in the event the schema class is an override, the inherited
104
105
  # schema and namespace will be applied
105
- schema_class = AvroGen::SchemaClass.klass(schema, namespace)
106
+ schema_class = Utils::SchemaClass.klass(schema, namespace)
106
107
  if schema_class.nil?
107
108
  schema_backend_class(backend: backend).
108
109
  new(
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is autogenerated by Deimos, Do NOT modify
4
+ <%= @modules.map { |m| "module #{m}"}.join('; ') %>
5
+ ### Primary Schema Class ###
6
+ <%=- @main_class_definition -%>
7
+
8
+ <%= @modules.map { "end" }.join('; ') %>
@@ -0,0 +1,13 @@
1
+ # Autogenerated Schema for Enum at <%= @current_schema.namespace %>.<%= @current_schema.name %>
2
+ class <%= Deimos::SchemaBackends::AvroBase.schema_classname(@current_schema) %> < Deimos::SchemaClass::Enum
3
+ # @override
4
+ def symbols
5
+ %w(<%= @current_schema.symbols.join(' ') %>)
6
+ end
7
+
8
+ <% @current_schema.symbols.each do |symbol| %>
9
+ def <%= symbol.underscore %>?
10
+ @value == '<%= symbol %>'
11
+ end
12
+ <% end %>
13
+ end
@@ -0,0 +1,102 @@
1
+ # Autogenerated Schema for Record at <%= @current_schema.namespace %>.<%= @current_schema.name %>
2
+ class <%= Deimos::SchemaBackends::AvroBase.schema_classname(@current_schema) %> < Deimos::SchemaClass::Record
3
+ <% if @sub_schema_templates.present? -%>
4
+
5
+ ### Secondary Schema Classes ###
6
+ <%- @sub_schema_templates.each do |schema_template| -%>
7
+ <%=- schema_template.gsub(/^/, " ") %>
8
+
9
+ <%- end -%>
10
+ <% end -%>
11
+
12
+ <%- if @field_assignments.select{ |h| h[:is_schema_class] }.any? -%>
13
+ ### Attribute Readers ###
14
+ <%- @field_assignments.select{ |h| h[:is_schema_class] }.each do |method_definition| -%>
15
+ # @return [<%= method_definition[:deimos_type] %>]
16
+ attr_reader :<%= method_definition[:field].name %>
17
+ <%- end -%>
18
+
19
+ <% end -%>
20
+ <%- if @field_assignments.select{ |h| !h[:is_schema_class] }.any? -%>
21
+ ### Attribute Accessors ###
22
+ <%- @field_assignments.select{ |h| !h[:is_schema_class] }.each do |method_definition| -%>
23
+ # @return [<%= method_definition[:deimos_type] %>]
24
+ attr_accessor :<%= method_definition[:field].name %>
25
+ <%- end -%>
26
+
27
+ <% end -%>
28
+ <%- if @field_assignments.select{ |h| h[:is_schema_class] }.any? -%>
29
+ ### Attribute Writers ###
30
+ <%- @field_assignments.select{ |h| h[:is_schema_class] }.each do |method_definition| -%>
31
+ # @return [<%= method_definition[:deimos_type] %>]
32
+ def <%= method_definition[:field].name %>=(<%= method_definition[:method_argument] %>)
33
+ <%- if method_definition[:field_type] == :array -%>
34
+ @<%= method_definition[:field].name %> = values&.map do |value|
35
+ <%= method_definition[:field_initialization] %>
36
+ end
37
+ <%- elsif method_definition[:field_type] == :map -%>
38
+ @<%= method_definition[:field].name %> = values&.transform_values do |value|
39
+ <%= method_definition[:field_initialization] %>
40
+ end
41
+ <%- else -%>
42
+ @<%= method_definition[:field].name %> = <%= method_definition[:field_initialization] %>
43
+ <%- end -%>
44
+ end
45
+
46
+ <%- end -%>
47
+ <% end -%>
48
+ <%- if @field_assignments.select{ |h| h[:is_complex_union] }.any? -%>
49
+ <%- @field_assignments.select{ |h| h[:is_complex_union] }.each do |method_definition| -%>
50
+ # Helper method to determine which schema type to use for <%= method_definition[:field].name %>
51
+ # @param value [Hash, nil]
52
+ # @param from_message [Boolean] whether this was initialized from a real Avro message
53
+ # @return [Object, nil]
54
+ def initialize_<%= method_definition[:field].name %>_type(value, from_message: false)
55
+ return nil if value.nil?
56
+
57
+ klass = [<%= method_definition[:field].type.schemas.reject { |s| s.type_sym == :null }.select { |s| s.type_sym == :record }.map { |s| Deimos::SchemaBackends::AvroBase.schema_classname(s) }.join(', ') %>].find do |candidate|
58
+ fields = candidate.new.as_json.keys
59
+ (value.keys - fields).empty?
60
+ end
61
+
62
+ klass.initialize_from_value(value, from_message: self._from_message)
63
+ end
64
+
65
+ <%- end -%>
66
+ <% end -%>
67
+ # @override
68
+ <%= @initialization_definition %>
69
+ @_from_message = _from_message
70
+ super
71
+ <%- @fields.each do |field| -%>
72
+ self.<%= field.name %> = <%= field.name %>
73
+ <% end -%>
74
+ end
75
+
76
+ # @override
77
+ def schema
78
+ '<%= @current_schema.name %>'
79
+ end
80
+
81
+ # @override
82
+ def namespace
83
+ '<%= @current_schema.namespace %>'
84
+ end
85
+ <%- if @tombstone_assignment %>
86
+ def self.tombstone(key)
87
+ record = self.allocate
88
+ <%- if @tombstone_assignment.present? -%>
89
+ <%= @tombstone_assignment %>
90
+ <%- end -%>
91
+ record
92
+ end
93
+ <%- end %>
94
+ # @override
95
+ def as_json(_opts={})
96
+ {
97
+ <%- @fields.each do |field| -%>
98
+ <%= field_as_json(field) %>
99
+ <% end -%>
100
+ }
101
+ end
102
+ end