deimos-ruby 1.18.2 → 1.19.beta1

Sign up to get free protection for your applications and to get access to all the features.
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