deimos-ruby 1.16.1 → 1.16.4

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile +6 -0
  5. data/README.md +22 -0
  6. data/lib/deimos/active_record_consume/batch_consumption.rb +7 -2
  7. data/lib/deimos/active_record_consume/batch_slicer.rb +2 -0
  8. data/lib/deimos/active_record_consume/message_consumption.rb +8 -4
  9. data/lib/deimos/active_record_consumer.rb +7 -4
  10. data/lib/deimos/active_record_producer.rb +10 -0
  11. data/lib/deimos/backends/base.rb +4 -2
  12. data/lib/deimos/backends/kafka.rb +1 -0
  13. data/lib/deimos/backends/kafka_async.rb +1 -0
  14. data/lib/deimos/config/configuration.rb +4 -0
  15. data/lib/deimos/config/phobos_config.rb +2 -1
  16. data/lib/deimos/consume/batch_consumption.rb +8 -1
  17. data/lib/deimos/consume/message_consumption.rb +4 -1
  18. data/lib/deimos/instrumentation.rb +11 -4
  19. data/lib/deimos/kafka_message.rb +1 -0
  20. data/lib/deimos/kafka_source.rb +5 -0
  21. data/lib/deimos/kafka_topic_info.rb +4 -0
  22. data/lib/deimos/message.rb +19 -2
  23. data/lib/deimos/metrics/datadog.rb +2 -1
  24. data/lib/deimos/metrics/mock.rb +2 -2
  25. data/lib/deimos/metrics/provider.rb +6 -0
  26. data/lib/deimos/monkey_patches/phobos_cli.rb +1 -1
  27. data/lib/deimos/monkey_patches/phobos_producer.rb +1 -0
  28. data/lib/deimos/producer.rb +12 -6
  29. data/lib/deimos/schema_backends/base.rb +31 -17
  30. data/lib/deimos/schema_backends/mock.rb +2 -2
  31. data/lib/deimos/schema_class/base.rb +9 -5
  32. data/lib/deimos/schema_class/enum.rb +4 -2
  33. data/lib/deimos/schema_class/record.rb +12 -6
  34. data/lib/deimos/shared_config.rb +6 -2
  35. data/lib/deimos/test_helpers.rb +21 -4
  36. data/lib/deimos/tracing/datadog.rb +1 -1
  37. data/lib/deimos/tracing/mock.rb +4 -3
  38. data/lib/deimos/tracing/provider.rb +5 -0
  39. data/lib/deimos/utils/db_poller.rb +9 -1
  40. data/lib/deimos/utils/db_producer.rb +14 -2
  41. data/lib/deimos/utils/deadlock_retry.rb +3 -0
  42. data/lib/deimos/utils/inline_consumer.rb +14 -6
  43. data/lib/deimos/utils/lag_reporter.rb +11 -0
  44. data/lib/deimos/utils/schema_controller_mixin.rb +8 -0
  45. data/lib/deimos/version.rb +1 -1
  46. data/lib/deimos.rb +3 -2
  47. data/lib/generators/deimos/active_record_generator.rb +1 -1
  48. data/lib/generators/deimos/db_backend_generator.rb +1 -0
  49. data/lib/generators/deimos/db_poller_generator.rb +1 -0
  50. data/lib/generators/deimos/schema_class/templates/schema_record.rb.tt +13 -5
  51. data/lib/generators/deimos/schema_class_generator.rb +51 -24
  52. data/rbs_collection.lock.yaml +176 -0
  53. data/rbs_collection.yaml +15 -0
  54. data/regenerate_test_schema_classes.rb +68 -0
  55. data/sig/avro.rbs +14 -0
  56. data/sig/defs.rbs +1859 -0
  57. data/sig/fig_tree.rbs +2 -0
  58. data/spec/active_record_producer_spec.rb +24 -0
  59. data/spec/generators/schema_class/my_schema_spec.rb +16 -0
  60. data/spec/generators/schema_class/my_schema_with_circular_reference_spec.rb +1 -1
  61. data/spec/generators/schema_class/my_schema_with_complex_types_spec.rb +32 -24
  62. data/spec/producer_spec.rb +11 -11
  63. data/spec/schemas/{generated.rb → my_namespace/generated.rb} +28 -32
  64. data/spec/schemas/{my_nested_schema.rb → my_namespace/my_nested_schema.rb} +23 -16
  65. data/spec/schemas/{my_schema.rb → my_namespace/my_schema.rb} +12 -5
  66. data/spec/schemas/my_namespace/my_schema_compound_key.rb +41 -0
  67. data/spec/schemas/my_namespace/my_schema_id_key.rb +36 -0
  68. data/spec/schemas/{my_schema_key.rb → my_namespace/my_schema_key.rb} +3 -3
  69. data/spec/schemas/my_namespace/my_schema_with_boolean.rb +41 -0
  70. data/spec/schemas/{my_schema_with_circular_reference.rb → my_namespace/my_schema_with_circular_reference.rb} +15 -10
  71. data/spec/schemas/{my_schema_with_complex_type.rb → my_namespace/my_schema_with_complex_type.rb} +34 -49
  72. data/spec/schemas/my_namespace/my_schema_with_date_time.rb +56 -0
  73. data/spec/schemas/my_namespace/my_schema_with_id.rb +51 -0
  74. data/spec/schemas/my_namespace/my_schema_with_unique_id.rb +56 -0
  75. data/spec/schemas/my_namespace/wibble.rb +76 -0
  76. data/spec/schemas/my_namespace/widget.rb +56 -0
  77. data/spec/schemas/my_namespace/widget_the_second.rb +56 -0
  78. data/spec/schemas/request/create_topic.rb +36 -0
  79. data/spec/schemas/request/index.rb +36 -0
  80. data/spec/schemas/request/update_request.rb +36 -0
  81. data/spec/schemas/response/create_topic.rb +36 -0
  82. data/spec/schemas/response/index.rb +36 -0
  83. data/spec/schemas/response/update_response.rb +36 -0
  84. data/spec/snapshots/consumers-no-nest.snap +93 -86
  85. data/spec/snapshots/consumers.snap +94 -87
  86. data/spec/snapshots/consumers_and_producers-no-nest.snap +108 -87
  87. data/spec/snapshots/consumers_and_producers.snap +109 -88
  88. data/spec/snapshots/consumers_circular-no-nest.snap +93 -86
  89. data/spec/snapshots/consumers_circular.snap +94 -87
  90. data/spec/snapshots/consumers_complex_types-no-nest.snap +93 -86
  91. data/spec/snapshots/consumers_complex_types.snap +94 -87
  92. data/spec/snapshots/consumers_nested-no-nest.snap +93 -86
  93. data/spec/snapshots/consumers_nested.snap +94 -87
  94. data/spec/snapshots/namespace_folders.snap +111 -90
  95. data/spec/snapshots/producers_with_key-no-nest.snap +94 -87
  96. data/spec/snapshots/producers_with_key.snap +95 -88
  97. data/spec/spec_helper.rb +2 -1
  98. metadata +53 -15
@@ -8,8 +8,11 @@ module Deimos
8
8
  include Phobos::Producer
9
9
  attr_accessor :id, :current_topic
10
10
 
11
+ # @return [Integer]
11
12
  BATCH_SIZE = 1000
13
+ # @return [Integer]
12
14
  DELETE_BATCH_SIZE = 10
15
+ # @return [Integer]
13
16
  MAX_DELETE_ATTEMPTS = 3
14
17
 
15
18
  # @param logger [Logger]
@@ -19,12 +22,13 @@ module Deimos
19
22
  @logger.push_tags("DbProducer #{@id}") if @logger.respond_to?(:push_tags)
20
23
  end
21
24
 
22
- # @return [Deimos::DbProducerConfig]
25
+ # @return [FigTree]
23
26
  def config
24
27
  Deimos.config.db_producer
25
28
  end
26
29
 
27
30
  # Start the poll.
31
+ # @return [void]
28
32
  def start
29
33
  @logger.info('Starting...')
30
34
  @signal_to_stop = false
@@ -40,12 +44,14 @@ module Deimos
40
44
  end
41
45
 
42
46
  # Stop the poll.
47
+ # @return [void]
43
48
  def stop
44
49
  @logger.info('Received signal to stop')
45
50
  @signal_to_stop = true
46
51
  end
47
52
 
48
53
  # Complete one loop of processing all messages in the DB.
54
+ # @return [void]
49
55
  def process_next_messages
50
56
  topics = retrieve_topics
51
57
  @logger.info("Found topics: #{topics}")
@@ -80,6 +86,7 @@ module Deimos
80
86
  end
81
87
 
82
88
  # Process a single batch in a topic.
89
+ # @return [void]
83
90
  def process_topic_batch
84
91
  messages = retrieve_messages
85
92
  return false if messages.empty?
@@ -111,6 +118,7 @@ module Deimos
111
118
  end
112
119
 
113
120
  # @param messages [Array<Deimos::KafkaMessage>]
121
+ # @return [void]
114
122
  def delete_messages(messages)
115
123
  attempts = 1
116
124
  begin
@@ -137,6 +145,7 @@ module Deimos
137
145
  end
138
146
 
139
147
  # @param messages [Array<Deimos::KafkaMessage>]
148
+ # @return [void]
140
149
  def log_messages(messages)
141
150
  return if config.log_topics != :all && !config.log_topics.include?(@current_topic)
142
151
 
@@ -146,7 +155,8 @@ module Deimos
146
155
  end
147
156
  end
148
157
 
149
- # Send metrics to Datadog.
158
+ # Send metrics related to pending messages.
159
+ # @return [void]
150
160
  def send_pending_metrics
151
161
  metrics = Deimos.config.metrics
152
162
  return unless metrics
@@ -185,6 +195,7 @@ module Deimos
185
195
  # Shut down the sync producer if we have to. Phobos will automatically
186
196
  # create a new one. We should call this if the producer can be in a bad
187
197
  # state and e.g. we need to clear the buffer.
198
+ # @return [void]
188
199
  def shutdown_producer
189
200
  if self.class.producer.respond_to?(:sync_producer_shutdown) # Phobos 1.8.3
190
201
  self.class.producer.sync_producer_shutdown
@@ -194,6 +205,7 @@ module Deimos
194
205
  # Produce messages in batches, reducing the size 1/10 if the batch is too
195
206
  # large. Does not retry batches of messages that have already been sent.
196
207
  # @param batch [Array<Hash>]
208
+ # @return [void]
197
209
  def produce_messages(batch)
198
210
  batch_size = batch.size
199
211
  current_index = 0
@@ -7,9 +7,11 @@ module Deimos
7
7
  class DeadlockRetry
8
8
  class << self
9
9
  # Maximum number of times to retry the block after encountering a deadlock
10
+ # @return [Integer]
10
11
  RETRY_COUNT = 2
11
12
 
12
13
  # Need to match on error messages to support older Rails versions
14
+ # @return [Array<String>]
13
15
  DEADLOCK_MESSAGES = [
14
16
  # MySQL
15
17
  'Deadlock found when trying to get lock',
@@ -28,6 +30,7 @@ module Deimos
28
30
  # from retrying at the same time.
29
31
  # @param tags [Array] Tags to attach when logging and reporting metrics.
30
32
  # @yield Yields to the block that may deadlock.
33
+ # @return [void]
31
34
  def wrap(tags=[])
32
35
  count = RETRY_COUNT
33
36
 
@@ -6,10 +6,12 @@ module Deimos
6
6
  module Utils
7
7
  # Listener that can seek to get the last X messages in a topic.
8
8
  class SeekListener < Phobos::Listener
9
+ # @return [Integer]
9
10
  MAX_SEEK_RETRIES = 3
11
+ # @return [Integer]
10
12
  attr_accessor :num_messages
11
13
 
12
- # :nodoc:
14
+ # @return [void]
13
15
  def start_listener
14
16
  @num_messages ||= 10
15
17
  @consumer = create_kafka_consumer
@@ -45,17 +47,20 @@ module Deimos
45
47
 
46
48
  cattr_accessor :total_messages
47
49
 
48
- # @param klass [Class < Deimos::Consumer]
50
+ # @param klass [Class<Deimos::Consumer>]
51
+ # @return [void]
49
52
  def self.config_class=(klass)
50
53
  self.config.merge!(klass.config)
51
54
  end
52
55
 
53
- # :nodoc:
56
+ # @param _kafka_client [Kafka::Client]
57
+ # @return [void]
54
58
  def self.start(_kafka_client)
55
59
  self.total_messages = []
56
60
  end
57
61
 
58
- # :nodoc:
62
+ # @param payload [Hash]
63
+ # @param metadata [Hash]
59
64
  def consume(payload, metadata)
60
65
  self.class.total_messages << {
61
66
  key: metadata[:key],
@@ -66,18 +71,20 @@ module Deimos
66
71
 
67
72
  # Class which can process/consume messages inline.
68
73
  class InlineConsumer
74
+ # @return [Integer]
69
75
  MAX_MESSAGE_WAIT_TIME = 1.second
76
+ # @return [Integer]
70
77
  MAX_TOPIC_WAIT_TIME = 10.seconds
71
78
 
72
79
  # Get the last X messages from a topic. You can specify a subclass of
73
80
  # Deimos::Consumer or Deimos::Producer, or provide the
74
81
  # schema, namespace and key_config directly.
75
82
  # @param topic [String]
76
- # @param config_class [Class < Deimos::Consumer|Deimos::Producer>]
83
+ # @param config_class [Class<Deimos::Consumer>,Class<Deimos::Producer>]
77
84
  # @param schema [String]
78
85
  # @param namespace [String]
79
86
  # @param key_config [Hash]
80
- # @param num_messages [Number]
87
+ # @param num_messages [Integer]
81
88
  # @return [Array<Hash>]
82
89
  def self.get_messages_for(topic:, schema: nil, namespace: nil, key_config: nil,
83
90
  config_class: nil, num_messages: 10)
@@ -106,6 +113,7 @@ module Deimos
106
113
  # @param frk_consumer [Class]
107
114
  # @param num_messages [Integer] If this number is >= the number
108
115
  # of messages in the topic, all messages will be consumed.
116
+ # @return [void]
109
117
  def self.consume(topic:, frk_consumer:, num_messages: 10)
110
118
  listener = SeekListener.new(
111
119
  handler: frk_consumer,
@@ -24,6 +24,7 @@ module Deimos
24
24
 
25
25
  # @param topic [String]
26
26
  # @param partition [Integer]
27
+ # @return [void]
27
28
  def report_lag(topic, partition)
28
29
  self.topics[topic.to_s] ||= Topic.new(topic, self)
29
30
  self.topics[topic.to_s].report_lag(partition)
@@ -32,6 +33,7 @@ module Deimos
32
33
  # @param topic [String]
33
34
  # @param partition [Integer]
34
35
  # @param offset [Integer]
36
+ # @return [void]
35
37
  def assign_current_offset(topic, partition, offset)
36
38
  self.topics[topic.to_s] ||= Topic.new(topic, self)
37
39
  self.topics[topic.to_s].assign_current_offset(partition, offset)
@@ -56,11 +58,15 @@ module Deimos
56
58
  end
57
59
 
58
60
  # @param partition [Integer]
61
+ # @param offset [Integer]
62
+ # @return [void]
59
63
  def assign_current_offset(partition, offset)
60
64
  self.partition_current_offsets[partition.to_i] = offset
61
65
  end
62
66
 
63
67
  # @param partition [Integer]
68
+ # @param offset [Integer]
69
+ # @return [Integer]
64
70
  def compute_lag(partition, offset)
65
71
  begin
66
72
  client = Phobos.create_kafka_client
@@ -74,6 +80,7 @@ module Deimos
74
80
  end
75
81
 
76
82
  # @param partition [Integer]
83
+ # @return [void]
77
84
  def report_lag(partition)
78
85
  current_offset = self.partition_current_offsets[partition.to_i]
79
86
  return unless current_offset
@@ -94,6 +101,7 @@ module Deimos
94
101
 
95
102
  class << self
96
103
  # Reset all group information.
104
+ # @return [void]
97
105
  def reset
98
106
  @groups = {}
99
107
  end
@@ -103,6 +111,7 @@ module Deimos
103
111
  # topic = event.payload.fetch(:topic)
104
112
  # partition = event.payload.fetch(:partition)
105
113
  # @param payload [Hash]
114
+ # @return [void]
106
115
  def message_processed(payload)
107
116
  offset = payload[:offset] || payload[:last_offset]
108
117
  topic = payload[:topic]
@@ -116,6 +125,7 @@ module Deimos
116
125
  end
117
126
 
118
127
  # @param payload [Hash]
128
+ # @return [void]
119
129
  def offset_seek(payload)
120
130
  offset = payload[:offset]
121
131
  topic = payload[:topic]
@@ -129,6 +139,7 @@ module Deimos
129
139
  end
130
140
 
131
141
  # @param payload [Hash]
142
+ # @return [void]
132
143
  def heartbeat(payload)
133
144
  group = payload[:group_id]
134
145
  synchronize do
@@ -31,6 +31,7 @@ module Deimos
31
31
  # @param kwactions [String]
32
32
  # @param request [String]
33
33
  # @param response [String]
34
+ # @return [void]
34
35
  def schemas(*actions, request: nil, response: nil, **kwactions)
35
36
  actions.each do |action|
36
37
  request ||= action.to_s.titleize
@@ -49,6 +50,7 @@ module Deimos
49
50
 
50
51
  # Set the namespace for both requests and responses.
51
52
  # @param name [String]
53
+ # @return [void]
52
54
  def namespace(name)
53
55
  request_namespace(name)
54
56
  response_namespace(name)
@@ -56,12 +58,14 @@ module Deimos
56
58
 
57
59
  # Set the namespace for requests.
58
60
  # @param name [String]
61
+ # @return [void]
59
62
  def request_namespace(name)
60
63
  namespaces[:request] = name
61
64
  end
62
65
 
63
66
  # Set the namespace for repsonses.
64
67
  # @param name [String]
68
+ # @return [void]
65
69
  def response_namespace(name)
66
70
  namespaces[:response] = name
67
71
  end
@@ -94,6 +98,7 @@ module Deimos
94
98
  end
95
99
 
96
100
  # Decode the payload with the parameters.
101
+ # @return [void]
97
102
  def decode_schema
98
103
  namespace, schema = parse_namespace(:request)
99
104
  decoder = Deimos.schema_backend(schema: schema, namespace: namespace)
@@ -109,6 +114,9 @@ module Deimos
109
114
 
110
115
  # Render a hash into a payload as specified by the configured schema and namespace.
111
116
  # @param payload [Hash]
117
+ # @param schema [String]
118
+ # @param namespace [String]
119
+ # @return [void]
112
120
  def render_schema(payload, schema: nil, namespace: nil)
113
121
  namespace, schema = parse_namespace(:response) if !schema && !namespace
114
122
  encoder = Deimos.schema_backend(schema: schema, namespace: namespace)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deimos
4
- VERSION = '1.16.1'
4
+ VERSION = '1.16.4'
5
5
  end
data/lib/deimos.rb CHANGED
@@ -45,7 +45,7 @@ require 'erb'
45
45
  # Parent module.
46
46
  module Deimos
47
47
  class << self
48
- # @return [Class < Deimos::SchemaBackends::Base]
48
+ # @return [Class<Deimos::SchemaBackends::Base>]
49
49
  def schema_backend_class
50
50
  backend = Deimos.config.schema.backend.to_s
51
51
 
@@ -54,7 +54,7 @@ module Deimos
54
54
  "Deimos::SchemaBackends::#{backend.classify}".constantize
55
55
  end
56
56
 
57
- # @param schema [String|Symbol]
57
+ # @param schema [String, Symbol]
58
58
  # @param namespace [String]
59
59
  # @return [Deimos::SchemaBackends::Base]
60
60
  def schema_backend(schema:, namespace:)
@@ -81,6 +81,7 @@ module Deimos
81
81
 
82
82
  # Start the DB producers to send Kafka messages.
83
83
  # @param thread_count [Integer] the number of threads to start.
84
+ # @return [void]
84
85
  def start_db_backend!(thread_count: 1)
85
86
  Sigurd.exit_on_signal = true
86
87
  if self.config.producers.backend != :db
@@ -69,7 +69,7 @@ module Deimos
69
69
  end
70
70
 
71
71
  desc 'Generate migration for a table based on an existing schema.'
72
- # :nodoc:
72
+ # @return [void]
73
73
  def generate
74
74
  migration_template('migration.rb', "db/migrate/create_#{table_name.underscore}.rb")
75
75
  template('model.rb', "app/models/#{table_name.underscore.singularize}.rb")
@@ -34,6 +34,7 @@ module Deimos
34
34
  end
35
35
 
36
36
  # Main method to create all the necessary files
37
+ # @return [void]
37
38
  def generate
38
39
  if Rails.version < '4'
39
40
  migration_template('rails3_migration',
@@ -34,6 +34,7 @@ module Deimos
34
34
  end
35
35
 
36
36
  # Main method to create all the necessary files
37
+ # @return [void]
37
38
  def generate
38
39
  if Rails.version < '4'
39
40
  migration_template('rails3_migration',
@@ -20,7 +20,7 @@
20
20
  <%- if @field_assignments.select{ |h| !h[:is_schema_class] }.any? -%>
21
21
  ### Attribute Accessors ###
22
22
  <%- @field_assignments.select{ |h| !h[:is_schema_class] }.each do |method_definition| -%>
23
- # @param <%= method_definition[:method_argument] %> [<%= method_definition[:deimos_type] %>]
23
+ # @return [<%= method_definition[:deimos_type] %>]
24
24
  attr_accessor :<%= method_definition[:field].name %>
25
25
  <%- end -%>
26
26
 
@@ -28,14 +28,14 @@
28
28
  <%- if @field_assignments.select{ |h| h[:is_schema_class] }.any? -%>
29
29
  ### Attribute Writers ###
30
30
  <%- @field_assignments.select{ |h| h[:is_schema_class] }.each do |method_definition| -%>
31
- # @param <%= method_definition[:method_argument] %> [<%= method_definition[:deimos_type] %>]
31
+ # @return [<%= method_definition[:deimos_type] %>]
32
32
  def <%= method_definition[:field].name %>=(<%= method_definition[:method_argument] %>)
33
33
  <%- if method_definition[:field_type] == :array -%>
34
- @<%= method_definition[:field].name %> = values.map do |value|
34
+ @<%= method_definition[:field].name %> = values&.map do |value|
35
35
  <%= method_definition[:field_initialization] %>
36
36
  end
37
37
  <%- elsif method_definition[:field_type] == :map -%>
38
- @<%= method_definition[:field].name %> = values.transform_values do |value|
38
+ @<%= method_definition[:field].name %> = values&.transform_values do |value|
39
39
  <%= method_definition[:field_initialization] %>
40
40
  end
41
41
  <%- else -%>
@@ -62,7 +62,15 @@
62
62
  def namespace
63
63
  '<%= @current_schema.namespace %>'
64
64
  end
65
-
65
+ <%- if @tombstone_assignment %>
66
+ def self.tombstone(key)
67
+ record = self.allocate
68
+ <%- if @tombstone_assignment.present? -%>
69
+ <%= @tombstone_assignment %>
70
+ <%- end -%>
71
+ record
72
+ end
73
+ <%- end %>
66
74
  # @override
67
75
  def as_json(_opts={})
68
76
  {
@@ -11,11 +11,17 @@ module Deimos
11
11
  # Generator for Schema Classes used for the IDE and consumer/producer interfaces
12
12
  class SchemaClassGenerator < Rails::Generators::Base
13
13
 
14
+ # @return [Array<Symbol>]
14
15
  SPECIAL_TYPES = %i(record enum).freeze
16
+ # @return [String]
15
17
  INITIALIZE_WHITESPACE = "\n#{' ' * 19}"
18
+ # @return [Array<String>]
16
19
  IGNORE_DEFAULTS = %w(message_id timestamp).freeze
20
+ # @return [String]
17
21
  SCHEMA_CLASS_FILE = 'schema_class.rb'
22
+ # @return [String]
18
23
  SCHEMA_RECORD_PATH = File.expand_path('schema_class/templates/schema_record.rb.tt', __dir__).freeze
24
+ # @return [String]
19
25
  SCHEMA_ENUM_PATH = File.expand_path('schema_class/templates/schema_enum.rb.tt', __dir__).freeze
20
26
 
21
27
  source_root File.expand_path('schema_class/templates', __dir__)
@@ -41,16 +47,17 @@ module Deimos
41
47
  # Deimos Consumer or Producer Configuration object
42
48
  # @param schema_name [String]
43
49
  # @param namespace [String]
44
- # @param key_schema_name [String,nil]
45
- def generate_classes(schema_name, namespace, key_schema_name)
50
+ # @param key_config [Hash,nil]
51
+ # @return [void]
52
+ def generate_classes(schema_name, namespace, key_config)
46
53
  schema_base = Deimos.schema_backend(schema: schema_name, namespace: namespace)
47
54
  schema_base.load_schema
48
- if key_schema_name.present?
49
- key_schema_base = Deimos.schema_backend(schema: key_schema_name, namespace: namespace)
55
+ if key_config&.dig(:schema)
56
+ key_schema_base = Deimos.schema_backend(schema: key_config[:schema], namespace: namespace)
50
57
  key_schema_base.load_schema
51
- generate_class_from_schema_base(key_schema_base)
58
+ generate_class_from_schema_base(key_schema_base, key_config: nil)
52
59
  end
53
- generate_class_from_schema_base(schema_base, key_schema_base: key_schema_base)
60
+ generate_class_from_schema_base(schema_base, key_config: key_config)
54
61
  end
55
62
 
56
63
  # @param schema [Avro::Schema::NamedSchema]
@@ -83,8 +90,9 @@ module Deimos
83
90
  end
84
91
 
85
92
  # @param schema_base [Deimos::SchemaBackends::Base]
86
- # @param key_schema_base[Avro::Schema::NamedSchema]
87
- def generate_class_from_schema_base(schema_base, key_schema_base: nil)
93
+ # @param key_config [Hash,nil]
94
+ # @return [void]
95
+ def generate_class_from_schema_base(schema_base, key_config: nil)
88
96
  @discovered_schemas = Set.new
89
97
  @sub_schema_templates = []
90
98
  schemas = collect_all_schemas(schema_base.schema_store.schemas.values)
@@ -93,11 +101,11 @@ module Deimos
93
101
  sub_schemas = schemas.reject { |s| s.name == schema_base.schema }.sort_by(&:name)
94
102
  if Deimos.config.schema.nest_child_schemas
95
103
  @sub_schema_templates = sub_schemas.map do |schema|
96
- _generate_class_template_from_schema(schema)
104
+ _generate_class_template_from_schema(schema, nil)
97
105
  end
98
- write_file(main_schema, key_schema_base)
106
+ write_file(main_schema, key_config)
99
107
  else
100
- write_file(main_schema, key_schema_base)
108
+ write_file(main_schema, key_config)
101
109
  sub_schemas.each do |schema|
102
110
  write_file(schema, nil)
103
111
  end
@@ -105,9 +113,10 @@ module Deimos
105
113
  end
106
114
 
107
115
  # @param schema [Avro::Schema::NamedSchema]
108
- # @param key_schema_base [Avro::Schema::NamedSchema, nil]
109
- def write_file(schema, key_schema_base)
110
- class_template = _generate_class_template_from_schema(schema, key_schema_base)
116
+ # @param key_config [Hash,nil]
117
+ # @return [void]
118
+ def write_file(schema, key_config)
119
+ class_template = _generate_class_template_from_schema(schema, key_config)
111
120
  @modules = Utils::SchemaClass.modules_for(schema.namespace)
112
121
  @main_class_definition = class_template
113
122
 
@@ -143,7 +152,7 @@ module Deimos
143
152
  end
144
153
 
145
154
  desc 'Generate a class based on configured consumer and producers.'
146
- # :nodoc:
155
+ # @return [void]
147
156
  def generate
148
157
  _validate
149
158
  Rails.logger.info("Generating schemas from Deimos.config to #{Deimos.config.schema.generated_class_path}")
@@ -154,7 +163,7 @@ module Deimos
154
163
  key_schema_name = config.key_config[:schema]
155
164
  found_schemas.add("#{namespace}.#{schema_name}")
156
165
  found_schemas.add("#{namespace}.#{key_schema_name}") if key_schema_name
157
- generate_classes(schema_name, namespace, key_schema_name)
166
+ generate_classes(schema_name, namespace, config.key_config)
158
167
  end
159
168
 
160
169
  Deimos.config.consumer_objects.each do |config|
@@ -163,7 +172,7 @@ module Deimos
163
172
  key_schema_name = config.key_config[:schema]
164
173
  found_schemas.add("#{namespace}.#{schema_name}")
165
174
  found_schemas.add("#{namespace}.#{key_schema_name}") if key_schema_name
166
- generate_classes(schema_name, namespace, key_schema_name)
175
+ generate_classes(schema_name, namespace, config.key_config)
167
176
  end
168
177
 
169
178
  generate_from_schema_files(found_schemas)
@@ -191,10 +200,10 @@ module Deimos
191
200
  end
192
201
 
193
202
  # @param schema[Avro::Schema::NamedSchema]
194
- # @param key_schema_base[Avro::Schema::NamedSchema]
203
+ # @param key_config[Hash,nil]
195
204
  # @return [String]
196
- def _generate_class_template_from_schema(schema, key_schema_base=nil)
197
- _set_instance_variables(schema, key_schema_base)
205
+ def _generate_class_template_from_schema(schema, key_config)
206
+ _set_instance_variables(schema, key_config)
198
207
 
199
208
  temp = schema.is_a?(Avro::Schema::RecordSchema) ? _record_class_template : _enum_class_template
200
209
  res = ERB.new(temp, nil, '-')
@@ -202,20 +211,38 @@ module Deimos
202
211
  end
203
212
 
204
213
  # @param schema[Avro::Schema::NamedSchema]
205
- # @param key_schema_base[Avro::Schema::NamedSchema]
206
- def _set_instance_variables(schema, key_schema_base=nil)
214
+ # @param key_config [Hash,nil]
215
+ def _set_instance_variables(schema, key_config)
207
216
  schema_is_record = schema.is_a?(Avro::Schema::RecordSchema)
208
217
  @current_schema = schema
209
218
  return unless schema_is_record
210
219
 
211
220
  @fields = fields(schema)
212
- if key_schema_base.present?
221
+ key_schema = nil
222
+ if key_config&.dig(:schema)
223
+ key_schema_base = Deimos.schema_backend(schema: key_config[:schema], namespace: schema.namespace)
213
224
  key_schema_base.load_schema
214
225
  key_schema = key_schema_base.schema_store.schemas.values.first
215
226
  @fields << Deimos::SchemaField.new('payload_key', key_schema, [], nil)
216
227
  end
217
228
  @initialization_definition = _initialization_definition
218
229
  @field_assignments = _field_assignments
230
+ @tombstone_assignment = _tombstone_assignment(key_config, key_schema)
231
+ end
232
+
233
+ def _tombstone_assignment(key_config, key_schema)
234
+ return nil unless key_config
235
+
236
+ if key_config[:plain]
237
+ "record.tombstone_key = key"
238
+ elsif key_config[:field]
239
+ "record.tombstone_key = key\n record.#{key_config[:field]} = key"
240
+ elsif key_schema
241
+ field_base_type = _field_type(key_schema)
242
+ "record.tombstone_key = #{field_base_type}.initialize_from_value(key)\n record.payload_key = key"
243
+ else
244
+ ''
245
+ end
219
246
  end
220
247
 
221
248
  # Defines the initialization method for Schema Records with one keyword argument per line
@@ -234,7 +261,7 @@ module Deimos
234
261
  "#{result})"
235
262
  end
236
263
 
237
- # @param [SchemaField]
264
+ # @param field [SchemaField]
238
265
  # @return [String]
239
266
  def _field_default(field)
240
267
  default = field.default