deimos-ruby 1.18.2 → 1.19.beta1

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
  SHA256:
3
- metadata.gz: a3f5f0c1fdba2cf641706b6c18733f433c04f42e3e06f5871ab0050a0215cc61
4
- data.tar.gz: 9c758f1d7e62dd9cd91e1631b3211cc9388adb0bb7b3dfea7b29099b46224bc7
3
+ metadata.gz: e73c9ad8e132353f906ffd5ffe91820e8ad9d5225dbf091c7889752c8d699703
4
+ data.tar.gz: ac5d117e07aa5881bfd484ce74bd1715cb1f483004b2a0609b2b9e45a1a42937
5
5
  SHA512:
6
- metadata.gz: 30b074497e6efe25e87d6e4736ecba0725b5bb4b20dcfba9bc82a655c9ee246986b9a655ceeec05b6bfa57b0633fecafb3cf7e1335322bd40a4254b3d973944f
7
- data.tar.gz: 798513006a509d7439e4d09c18a6a547bac683bcbb4a56013447fad59ba941702072f828d6ec8966da6fec8096659a686de7f796d858923e17fdcf7f21235318
6
+ metadata.gz: 1762491acddff361c0290837911eaf862697c9c1a1f2cb054636fdbc097290efb5babe43c9b6e85217a4c43892cf34f899011e5c22485b568bed1a690fd2fcc3
7
+ data.tar.gz: 56ce518a0b9d8bb62ebb8e799fa51f95ccf5f275e4e3231d7b01084d8a35c73c1d08998035694dde72ff530e6b72c001b6c99ab44ebde82357aee0d456ba6575
data/CHANGELOG.md CHANGED
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## UNRELEASED
9
9
 
10
+ - Feature: When consuming data in batch mode, this adds ability to save data to multiple tables with `association_list`.
11
+
10
12
  # 1.18.2 - 2022-11-14
11
13
 
12
14
  - Fixes bug related to wait between runs when no records are fetched by updating poll_info
data/README.md CHANGED
@@ -353,6 +353,54 @@ class MyBatchConsumer < Deimos::Consumer
353
353
  end
354
354
  end
355
355
  ```
356
+ #### Saving data to Multiple Database tables
357
+
358
+ > This feature is implemented and tested with MySQL database ONLY.
359
+
360
+ Sometimes, the Kafka message needs to be saved to multiple database tables. For example, if a `User` topic provides you metadata and profile image for users, we might want to save it to multiple tables: `User` and `Image`.
361
+
362
+ - The `association_list` configuration allows you to achieve this use case.
363
+ - The optional `bulk_import_id_column` config allows you to specify column_name on `record_class` which can be used to retrieve IDs after save. Defaults to `bulk_import_id`
364
+
365
+ You must override the `build_records` and `bulk_import_columns` methods on your ActiveRecord class for this feature to work.
366
+ - `build_records` - This method is required to set the value of the `bulk_import_id` column and map Kafka messages to ActiveRecord model objects.
367
+ - `columns(klass)` - Should return an array of column names that should be used by ActiveRecord klass during SQL insert operation.
368
+ - `key_columns(messages, klass)` - Should return an array of column name(s) that makes a row unique.
369
+ ```ruby
370
+ class MyBatchConsumer < Deimos::ActiveRecordConsumer
371
+
372
+ record_class User
373
+ association_list :images
374
+
375
+ def build_records(messages)
376
+ # Initialise bulk_import_id and build ActiveRecord objects out of Kafka message attributes
377
+ messages.each do |m|
378
+ u = User.new(first_name: m.first_name, bulk_import_id: SecureRandom.uuid)
379
+ i = Image.new(attr1: m.image_url)
380
+ u.images << i
381
+ u
382
+ end
383
+ end
384
+
385
+ def key_columns(_records, klass)
386
+ case klass
387
+ when User
388
+ super
389
+ when Image
390
+ ["image_url", "image_name"]
391
+ end
392
+ end
393
+
394
+ def columns(klass)
395
+ case klass
396
+ when User
397
+ super
398
+ when Image
399
+ klass.columns.map(&:name) - [:created_at, :updated_at, :id]
400
+ end
401
+ end
402
+ end
403
+ ```
356
404
 
357
405
  # Rails Integration
358
406
 
@@ -3,6 +3,7 @@
3
3
  require 'deimos/active_record_consume/batch_slicer'
4
4
  require 'deimos/utils/deadlock_retry'
5
5
  require 'deimos/message'
6
+ require 'deimos/exceptions'
6
7
 
7
8
  module Deimos
8
9
  module ActiveRecordConsume
@@ -86,38 +87,63 @@ module Deimos
86
87
  # records to either be updated or inserted.
87
88
  # @return [void]
88
89
  def upsert_records(messages)
89
- key_cols = key_columns(messages)
90
-
91
- # Create payloads with payload + key attributes
92
- upserts = messages.map do |m|
93
- attrs = if self.method(:record_attributes).parameters.size == 2
94
- record_attributes(m.payload, m.key)
95
- else
96
- record_attributes(m.payload)
97
- end
98
-
99
- attrs&.merge(record_key(m.key))
100
- end
90
+ key_cols = key_columns(messages, nil)
101
91
 
92
+ # Create ActiveRecord Models with payload + key attributes
93
+ upserts = build_records(messages)
102
94
  # If overridden record_attributes indicated no record, skip
103
95
  upserts.compact!
96
+ # apply ActiveRecord validations and fetch valid Records
97
+ valid_upserts = filter_records(upserts)
98
+
99
+ return if valid_upserts.empty?
100
+
101
+ save_records_to_database(@klass, key_cols, valid_upserts)
102
+ import_associations(valid_upserts) unless @association_list.blank?
103
+ end
104
+
105
+ def save_records_to_database(record_class, key_cols, records)
106
+ columns = columns(record_class)
104
107
 
105
108
  options = if key_cols.empty?
106
109
  {} # Can't upsert with no key, just do regular insert
107
- elsif ActiveRecord::Base.connection.adapter_name.downcase =~ /mysql/
110
+ elsif mysql_adapter?
108
111
  {
109
- on_duplicate_key_update: :all
112
+ on_duplicate_key_update: columns
110
113
  }
111
114
  else
112
115
  {
113
116
  on_duplicate_key_update: {
114
117
  conflict_target: key_cols,
115
- columns: :all
118
+ columns: columns
116
119
  }
117
120
  }
118
121
  end
122
+ record_class.import!(columns, records, options)
123
+ end
124
+
125
+ # Imports associated objects and import them to database table
126
+ # The base table is expected to contain bulk_import_id column for indexing associated objects with id
127
+ # @association_list configured on the consumer helps identify the ones required to be saved.
128
+ def import_associations(entities)
129
+ _validate_associations(entities)
130
+ _fill_primary_key_on_entities(entities)
131
+
132
+ # Select associations from config parameter association_list and
133
+ # fill id to associated_objects foreign_key column
134
+ @klass.reflect_on_all_associations.select { |assoc| @association_list.include?(assoc.name) }.
135
+ each do |assoc|
136
+ sub_records = entities.map { |entity|
137
+ # Get associated `has_one` or `has_many` records for each entity
138
+ sub_records = Array(entity.send(assoc.name))
139
+ # Set IDS from master to each of the records in `has_one` or `has_many` relation
140
+ sub_records.each { |d| d.send("#{assoc.send(:foreign_key)}=", entity.id) }
141
+ sub_records
142
+ }.flatten
119
143
 
120
- @klass.import!(upserts, options)
144
+ columns = key_columns(nil, assoc.klass)
145
+ save_records_to_database(assoc.klass, columns, sub_records) if sub_records.any?
146
+ end
121
147
  end
122
148
 
123
149
  # Delete any records with a tombstone.
@@ -144,16 +170,31 @@ module Deimos
144
170
 
145
171
  # Get the set of attribute names that uniquely identify messages in the
146
172
  # batch. Requires at least one record.
173
+ # The parameters are mutually exclusive. records is used by default implementation.
147
174
  # @param records [Array<Message>] Non-empty list of messages.
175
+ # @param klass [ActiveRecord::Class] Class Name can be used to fetch columns
148
176
  # @return [Array<String>] List of attribute names.
149
177
  # @raise If records is empty.
150
- def key_columns(records)
178
+ def key_columns(records, klass)
179
+ raise 'Must implement key_columns method for associations!' unless klass.nil?
180
+
151
181
  raise 'Cannot determine key from empty batch' if records.empty?
152
182
 
153
183
  first_key = records.first.key
154
184
  record_key(first_key).keys
155
185
  end
156
186
 
187
+ # Get the list of database table column names that should be saved to the database
188
+ # @param record_class [Class] ActiveRecord class associated to the Entity Object
189
+ # @return Array[String] list of table columns
190
+ def columns(record_class)
191
+ # In-memory records contain created_at and updated_at as nil
192
+ # which messes up ActiveRecord-Import bulk_import.
193
+ # It is necessary to ignore timestamp columns when using ActiveRecord objects
194
+ ignored_columns = %w(created_at updated_at)
195
+ record_class.columns.map(&:name) - ignored_columns
196
+ end
197
+
157
198
  # Compact a batch of messages, taking only the last message for each
158
199
  # unique key.
159
200
  # @param batch [Array<Message>] Batch of messages.
@@ -163,6 +204,61 @@ module Deimos
163
204
 
164
205
  batch.reverse.uniq(&:key).reverse!
165
206
  end
207
+
208
+ # Turns Kafka payload into ActiveRecord Objects by mapping relevant fields
209
+ # Override this method to build object and associations with message payload
210
+ # @param messages [Array<Deimos::Message>] the array of deimos messages in batch mode
211
+ # @return [Array<ActiveRecord>] Array of ActiveRecord objects
212
+ def build_records(messages)
213
+ messages.map do |m|
214
+ attrs = if self.method(:record_attributes).parameters.size == 2
215
+ record_attributes(m.payload, m.key)
216
+ else
217
+ record_attributes(m.payload)
218
+ end
219
+
220
+ attrs = attrs&.merge(record_key(m.key))
221
+ @klass.new(attrs) unless attrs.nil?
222
+ end
223
+ end
224
+
225
+ # Filters list of Active Records by applying active record validations.
226
+ # Tip: Add validates_associated in ActiveRecord model to validate associated models
227
+ # Optionally inherit this method and apply more filters in the application code
228
+ # The default implementation throws ActiveRecord::RecordInvalid by default
229
+ # @param records Array<ActiveRecord> - List of active records which will be subjected to model validations
230
+ # @return valid Array<ActiveRecord> - Subset of records that passed the model validations
231
+ def filter_records(records)
232
+ records.each(&:validate!)
233
+ end
234
+
235
+ # Returns true if MySQL Adapter is currently used
236
+ def mysql_adapter?
237
+ ActiveRecord::Base.connection.adapter_name.downcase =~ /mysql/
238
+ end
239
+
240
+ # Checks whether the entities has necessary columns for `association_list` to work
241
+ # @return void
242
+ def _validate_associations(entities)
243
+ raise Deimos::MissingImplementationError unless mysql_adapter?
244
+
245
+ return if entities.first.respond_to?(@bulk_import_id_column)
246
+
247
+ raise "Create bulk_import_id on #{entities.first.class} and set it in `build_records` for associations." \
248
+ ' Run rails g deimos:bulk_import_id:setup to create the migration.'
249
+ end
250
+
251
+ # Fills Primary Key ID on in-memory objects.
252
+ # Uses @bulk_import_id_column on in-memory records to fetch saved records in database.
253
+ # @return void
254
+ def _fill_primary_key_on_entities(entities)
255
+ table_by_bulk_import_id = @klass.
256
+ where(@bulk_import_id_column => entities.map { |e| e[@bulk_import_id_column] }).
257
+ select(:id, @bulk_import_id_column).
258
+ index_by { |e| e[@bulk_import_id_column] }
259
+ # update IDs in upsert entity
260
+ entities.each { |entity| entity.id = table_by_bulk_import_id[entity[@bulk_import_id_column]].id }
261
+ end
166
262
  end
167
263
  end
168
264
  end
@@ -30,6 +30,18 @@ module Deimos
30
30
  config[:record_class] = klass
31
31
  end
32
32
 
33
+ # @param associations [List<String>] Optional list of associations that the consumer
34
+ # should save in addition to @klass
35
+ # @return [void]
36
+ def association_list(associations)
37
+ config[:association_list] = Array(associations)
38
+ end
39
+
40
+ # @param
41
+ def bulk_import_id_column(name)
42
+ config[:bulk_import_id_column] = name
43
+ end
44
+
33
45
  # @param val [Boolean] Turn pre-compaction of the batch on or off. If true,
34
46
  # only the last message for each unique key in a batch is processed.
35
47
  # @return [void]
@@ -41,6 +53,8 @@ module Deimos
41
53
  # Setup
42
54
  def initialize
43
55
  @klass = self.class.config[:record_class]
56
+ @association_list = self.class.config[:association_list]
57
+ @bulk_import_id_column = self.class.config[:bulk_import_id_column] || :bulk_import_id
44
58
  @converter = ActiveRecordConsume::SchemaModelConverter.new(self.class.decoder, @klass)
45
59
 
46
60
  if self.class.config[:key_schema]
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deimos
4
+ class MissingImplementationError < StandardError; end
5
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deimos
4
- VERSION = '1.18.2'
4
+ VERSION = '1.19.beta1'
5
5
  end
@@ -0,0 +1,7 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
+
3
+ def change
4
+ add_column :<%= table_name %>, :<%= column_name %>, :string, index: true
5
+ end
6
+
7
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record/migration'
5
+ require 'rails/version'
6
+
7
+ # Generates a migration for bulk import ID in consumer.
8
+ module Deimos
9
+ module Generators
10
+ # Generator for ActiveRecord model and migration.
11
+ class BulkImportIdGenerator < Rails::Generators::Base
12
+ include Rails::Generators::Migration
13
+ include ActiveRecord::Generators::Migration
14
+
15
+ namespace 'deimos:bulk_import_id:setup'
16
+
17
+ argument :table_name, desc: 'The table to add bulk import column.', required: true
18
+ argument :column_name, desc: 'The bulk import ID column name.', default: 'bulk_import_id'
19
+
20
+ source_root File.expand_path('bulk_import_id/templates', __dir__)
21
+ desc 'Add column migration to the given table and name'
22
+
23
+ no_commands do
24
+ # @return [String]
25
+ def db_migrate_path
26
+ if defined?(Rails.application) && Rails.application
27
+ paths = Rails.application.config.paths['db/migrate']
28
+ paths.respond_to?(:to_ary) ? paths.to_ary.first : paths.to_a.first
29
+ else
30
+ 'db/migrate'
31
+ end
32
+ end
33
+
34
+ # @return [String]
35
+ def migration_version
36
+ "[#{ActiveRecord::Migration.current_version}]"
37
+ rescue StandardError
38
+ ''
39
+ end
40
+ end
41
+
42
+ # For a given table_name and column_name, create a migration to add the column
43
+ # column_name defaults to bulk_import_id
44
+ def generate
45
+ Rails.logger.info("Arguments: #{table_name},#{column_name}")
46
+ migration_template('migration.rb',
47
+ "#{db_migrate_path}/add_#{column_name}_column_to_#{table_name}.rb")
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordBatchConsumerTest
4
+ describe Deimos::ActiveRecordConsumer,
5
+ 'Batch Consumer with MySQL handling associations',
6
+ :integration,
7
+ db_config: DbConfigs::DB_OPTIONS.second do
8
+ include_context('with DB')
9
+
10
+ before(:all) do
11
+ ActiveRecord::Base.connection.create_table(:widgets, force: true) do |t|
12
+ t.string(:test_id)
13
+ t.string(:part_one)
14
+ t.string(:part_two)
15
+ t.integer(:some_int)
16
+ t.boolean(:deleted, default: false)
17
+ t.timestamps
18
+
19
+ t.index(%i(part_one part_two), unique: true)
20
+ end
21
+
22
+ # create one-to-one association -- Details
23
+ ActiveRecord::Base.connection.create_table(:details, force: true) do |t|
24
+ t.string(:title)
25
+ t.belongs_to(:widget)
26
+
27
+ t.index(%i(title), unique: true)
28
+ end
29
+
30
+ # Create one-to-many association Locales
31
+ ActiveRecord::Base.connection.create_table(:locales, force: true) do |t|
32
+ t.string(:title)
33
+ t.string(:language)
34
+ t.belongs_to(:widget)
35
+
36
+ t.index(%i(title language), unique: true)
37
+ end
38
+
39
+ class Detail < ActiveRecord::Base
40
+ validates :title, presence: true
41
+ end
42
+
43
+ class Locale < ActiveRecord::Base
44
+ validates :title, presence: true
45
+ validates :language, presence: true
46
+ end
47
+
48
+ # Sample model
49
+ class Widget < ActiveRecord::Base
50
+ has_one :detail
51
+ has_many :locales
52
+ validates :test_id, presence: true
53
+
54
+ default_scope -> { where(deleted: false) }
55
+ end
56
+
57
+ Widget.reset_column_information
58
+ Detail.reset_column_information
59
+ Locale.reset_column_information
60
+ end
61
+
62
+ after(:all) do
63
+ ActiveRecord::Base.connection.drop_table(:widgets)
64
+ ActiveRecord::Base.connection.drop_table(:details)
65
+ ActiveRecord::Base.connection.drop_table(:locales)
66
+ end
67
+
68
+ before(:each) do
69
+ ActiveRecord::Base.connection.truncate_tables(%i(widgets details locales))
70
+ end
71
+
72
+ prepend_before(:each) do
73
+ stub_const('MyBatchConsumer', consumer_class)
74
+ end
75
+
76
+ # Helper to publish a list of messages and call the consumer
77
+ def publish_batch(messages)
78
+ keys = messages.map { |m| m[:key] }
79
+ payloads = messages.map { |m| m[:payload] }
80
+
81
+ test_consume_batch(MyBatchConsumer, payloads, keys: keys, call_original: true)
82
+ end
83
+
84
+ context 'when association_list configured in consumer without model changes' do
85
+ let(:consumer_class) do
86
+ Class.new(described_class) do
87
+ schema 'MySchema'
88
+ namespace 'com.my-namespace'
89
+ key_config plain: true
90
+ record_class Widget
91
+ association_list :detail
92
+
93
+ def build_records(messages)
94
+ messages.map do |m|
95
+ payload = m.payload
96
+ w = Widget.new(test_id: payload['test_id'], some_int: payload['some_int'])
97
+ d = Detail.new(title: payload['title'])
98
+ w.detail = d
99
+ w
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ it 'should raise error when bulk_import_id is not found' do
106
+ stub_const('MyBatchConsumer', consumer_class)
107
+ expect {
108
+ publish_batch([{ key: 2,
109
+ payload: { test_id: 'xyz', some_int: 5, title: 'Widget Title' } }])
110
+ }.to raise_error('Create bulk_import_id on ActiveRecordBatchConsumerTest::Widget'\
111
+ ' and set it in `build_records` for associations. Run rails g deimos:bulk_import_id:setup'\
112
+ ' to create the migration.')
113
+ end
114
+ end
115
+
116
+ context 'with one-to-one relation in association_list and custom bulk_import_id' do
117
+ let(:consumer_class) do
118
+ Class.new(described_class) do
119
+ schema 'MySchema'
120
+ namespace 'com.my-namespace'
121
+ key_config plain: true
122
+ record_class Widget
123
+ association_list :detail
124
+ bulk_import_id_column :custom_id
125
+
126
+ def build_records(messages)
127
+ messages.map do |m|
128
+ payload = m.payload
129
+ w = Widget.new(test_id: payload['test_id'],
130
+ some_int: payload['some_int'],
131
+ custom_id: SecureRandom.uuid)
132
+ d = Detail.new(title: payload['title'])
133
+ w.detail = d
134
+ w
135
+ end
136
+ end
137
+
138
+ def key_columns(messages, klass)
139
+ case klass.to_s
140
+ when Widget.to_s
141
+ super
142
+ when Detail.to_s
143
+ %w(title widget_id)
144
+ else
145
+ []
146
+ end
147
+ end
148
+
149
+ def columns(record_class)
150
+ all_cols = record_class.columns.map(&:name)
151
+
152
+ case record_class.to_s
153
+ when Widget.to_s
154
+ super
155
+ when Detail.to_s
156
+ all_cols - ['id']
157
+ else
158
+ []
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ before(:all) do
165
+ ActiveRecord::Base.connection.add_column(:widgets, :custom_id, :string, if_not_exists: true)
166
+ Widget.reset_column_information
167
+ end
168
+
169
+ it 'should save item to widget and associated detail' do
170
+ stub_const('MyBatchConsumer', consumer_class)
171
+ publish_batch([{ key: 2,
172
+ payload: { test_id: 'xyz', some_int: 5, title: 'Widget Title' } }])
173
+ expect(Widget.count).to eq(1)
174
+ expect(Detail.count).to eq(1)
175
+ expect(Widget.first.id).to eq(Detail.first.widget_id)
176
+ end
177
+ end
178
+
179
+ context 'with one-to-many relationship in association_list and default bulk_import_id' do
180
+ let(:consumer_class) do
181
+ Class.new(described_class) do
182
+ schema 'MySchema'
183
+ namespace 'com.my-namespace'
184
+ key_config plain: true
185
+ record_class Widget
186
+ association_list :locales
187
+
188
+ def build_records(messages)
189
+ messages.map do |m|
190
+ payload = m.payload
191
+ w = Widget.new(test_id: payload['test_id'],
192
+ some_int: payload['some_int'],
193
+ bulk_import_id: SecureRandom.uuid)
194
+ w.locales << Locale.new(title: payload['title'], language: 'en')
195
+ w.locales << Locale.new(title: payload['title'], language: 'fr')
196
+ w
197
+ end
198
+ end
199
+
200
+ def key_columns(messages, klass)
201
+ case klass.to_s
202
+ when Widget.to_s
203
+ super
204
+ when Detail.to_s
205
+ %w(title widget_id)
206
+ when Locale.to_s
207
+ %w(title language)
208
+ else
209
+ []
210
+ end
211
+ end
212
+
213
+ def columns(record_class)
214
+ all_cols = record_class.columns.map(&:name)
215
+
216
+ case record_class.to_s
217
+ when Widget.to_s
218
+ super
219
+ when Detail.to_s, Locale.to_s
220
+ all_cols - ['id']
221
+ else
222
+ []
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ before(:all) do
229
+ ActiveRecord::Base.connection.add_column(:widgets, :bulk_import_id, :string, if_not_exists: true)
230
+ Widget.reset_column_information
231
+ end
232
+
233
+ it 'should save item to widget and associated details' do
234
+ stub_const('MyBatchConsumer', consumer_class)
235
+ publish_batch([{ key: 2,
236
+ payload: { test_id: 'xyz', some_int: 5, title: 'Widget Title' } }])
237
+ expect(Widget.count).to eq(1)
238
+ expect(Locale.count).to eq(2)
239
+ expect(Widget.first.id).to eq(Locale.first.widget_id)
240
+ expect(Widget.first.id).to eq(Locale.second.widget_id)
241
+ end
242
+ end
243
+ end
244
+ end
@@ -487,5 +487,25 @@ module ActiveRecordBatchConsumerTest
487
487
  to match_array([have_attributes(id: 2, test_id: 'abc123')])
488
488
  end
489
489
  end
490
+
491
+ describe 'association_list feature for SQLite database' do
492
+ let(:consumer_class) do
493
+ Class.new(described_class) do
494
+ schema 'MySchema'
495
+ namespace 'com.my-namespace'
496
+ key_config plain: true
497
+ record_class Widget
498
+ association_list :locales
499
+ end
500
+ end
501
+
502
+ it 'should throw NotImplemented error' do
503
+ stub_const('MyBatchConsumer', consumer_class)
504
+ expect {
505
+ publish_batch([{ key: 2, payload: { test_id: 'xyz', some_int: 5, title: 'Widget Title' } }])
506
+ }.to raise_error(Deimos::MissingImplementationError)
507
+ end
508
+ end
509
+
490
510
  end
491
511
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deimos-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.18.2
4
+ version: 1.19.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Orner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-14 00:00:00.000000000 Z
11
+ date: 2023-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro_turf
@@ -446,6 +446,7 @@ files:
446
446
  - lib/deimos/consume/batch_consumption.rb
447
447
  - lib/deimos/consume/message_consumption.rb
448
448
  - lib/deimos/consumer.rb
449
+ - lib/deimos/exceptions.rb
449
450
  - lib/deimos/instrumentation.rb
450
451
  - lib/deimos/kafka_message.rb
451
452
  - lib/deimos/kafka_source.rb
@@ -488,6 +489,8 @@ files:
488
489
  - lib/generators/deimos/active_record/templates/migration.rb.tt
489
490
  - lib/generators/deimos/active_record/templates/model.rb.tt
490
491
  - lib/generators/deimos/active_record_generator.rb
492
+ - lib/generators/deimos/bulk_import_id/templates/migration.rb.tt
493
+ - lib/generators/deimos/bulk_import_id_generator.rb
491
494
  - lib/generators/deimos/db_backend/templates/migration
492
495
  - lib/generators/deimos/db_backend/templates/rails3_migration
493
496
  - lib/generators/deimos/db_backend_generator.rb
@@ -505,6 +508,7 @@ files:
505
508
  - sig/avro.rbs
506
509
  - sig/defs.rbs
507
510
  - sig/fig_tree.rbs
511
+ - spec/active_record_batch_consumer_mysql_spec.rb
508
512
  - spec/active_record_batch_consumer_spec.rb
509
513
  - spec/active_record_consume/batch_slicer_spec.rb
510
514
  - spec/active_record_consume/schema_model_converter_spec.rb
@@ -620,15 +624,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
620
624
  version: '0'
621
625
  required_rubygems_version: !ruby/object:Gem::Requirement
622
626
  requirements:
623
- - - ">="
627
+ - - ">"
624
628
  - !ruby/object:Gem::Version
625
- version: '0'
629
+ version: 1.3.1
626
630
  requirements: []
627
631
  rubygems_version: 3.3.20
628
632
  signing_key:
629
633
  specification_version: 4
630
634
  summary: Kafka libraries for Ruby.
631
635
  test_files:
636
+ - spec/active_record_batch_consumer_mysql_spec.rb
632
637
  - spec/active_record_batch_consumer_spec.rb
633
638
  - spec/active_record_consume/batch_slicer_spec.rb
634
639
  - spec/active_record_consume/schema_model_converter_spec.rb