deimos-ruby 1.6.2 → 1.8.0.pre.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +9 -0
  3. data/.rubocop.yml +15 -13
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +31 -0
  6. data/Gemfile.lock +43 -36
  7. data/README.md +141 -16
  8. data/Rakefile +1 -1
  9. data/deimos-ruby.gemspec +2 -1
  10. data/docs/ARCHITECTURE.md +144 -0
  11. data/docs/CONFIGURATION.md +27 -0
  12. data/lib/deimos.rb +7 -6
  13. data/lib/deimos/active_record_consume/batch_consumption.rb +159 -0
  14. data/lib/deimos/active_record_consume/batch_slicer.rb +27 -0
  15. data/lib/deimos/active_record_consume/message_consumption.rb +58 -0
  16. data/lib/deimos/active_record_consume/schema_model_converter.rb +52 -0
  17. data/lib/deimos/active_record_consumer.rb +33 -75
  18. data/lib/deimos/active_record_producer.rb +23 -0
  19. data/lib/deimos/batch_consumer.rb +2 -140
  20. data/lib/deimos/config/configuration.rb +28 -10
  21. data/lib/deimos/consume/batch_consumption.rb +150 -0
  22. data/lib/deimos/consume/message_consumption.rb +94 -0
  23. data/lib/deimos/consumer.rb +79 -69
  24. data/lib/deimos/kafka_message.rb +1 -1
  25. data/lib/deimos/kafka_topic_info.rb +1 -1
  26. data/lib/deimos/message.rb +6 -1
  27. data/lib/deimos/metrics/provider.rb +0 -2
  28. data/lib/deimos/poll_info.rb +9 -0
  29. data/lib/deimos/tracing/provider.rb +0 -2
  30. data/lib/deimos/utils/db_poller.rb +149 -0
  31. data/lib/deimos/utils/db_producer.rb +8 -3
  32. data/lib/deimos/utils/deadlock_retry.rb +68 -0
  33. data/lib/deimos/utils/lag_reporter.rb +19 -26
  34. data/lib/deimos/version.rb +1 -1
  35. data/lib/generators/deimos/db_poller/templates/migration +11 -0
  36. data/lib/generators/deimos/db_poller/templates/rails3_migration +16 -0
  37. data/lib/generators/deimos/db_poller_generator.rb +48 -0
  38. data/lib/tasks/deimos.rake +7 -0
  39. data/spec/active_record_batch_consumer_spec.rb +481 -0
  40. data/spec/active_record_consume/batch_slicer_spec.rb +42 -0
  41. data/spec/active_record_consume/schema_model_converter_spec.rb +105 -0
  42. data/spec/active_record_consumer_spec.rb +3 -11
  43. data/spec/active_record_producer_spec.rb +66 -88
  44. data/spec/batch_consumer_spec.rb +24 -7
  45. data/spec/config/configuration_spec.rb +4 -0
  46. data/spec/consumer_spec.rb +8 -8
  47. data/spec/deimos_spec.rb +57 -49
  48. data/spec/handlers/my_batch_consumer.rb +6 -1
  49. data/spec/handlers/my_consumer.rb +6 -1
  50. data/spec/message_spec.rb +19 -0
  51. data/spec/producer_spec.rb +3 -3
  52. data/spec/rake_spec.rb +1 -1
  53. data/spec/schemas/com/my-namespace/MySchemaCompound-key.avsc +18 -0
  54. data/spec/schemas/com/my-namespace/Wibble.avsc +43 -0
  55. data/spec/spec_helper.rb +61 -6
  56. data/spec/utils/db_poller_spec.rb +320 -0
  57. data/spec/utils/deadlock_retry_spec.rb +74 -0
  58. data/spec/utils/lag_reporter_spec.rb +29 -22
  59. metadata +55 -20
  60. data/lib/deimos/base_consumer.rb +0 -104
  61. data/lib/deimos/utils/executor.rb +0 -124
  62. data/lib/deimos/utils/platform_schema_validation.rb +0 -0
  63. data/lib/deimos/utils/signal_handler.rb +0 -68
  64. data/spec/utils/executor_spec.rb +0 -53
  65. data/spec/utils/signal_handler_spec.rb +0 -16
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Deimos::ActiveRecordConsume::BatchSlicer do
4
+ describe '#slice' do
5
+ let(:batch) do
6
+ [
7
+ Deimos::Message.new({ v: 1 }, nil, key: 'C'),
8
+ Deimos::Message.new({ v: 123 }, nil, key: 'A'),
9
+ Deimos::Message.new({ v: 999 }, nil, key: 'B'),
10
+ Deimos::Message.new({ v: 456 }, nil, key: 'A'),
11
+ Deimos::Message.new({ v: 2 }, nil, key: 'C'),
12
+ Deimos::Message.new({ v: 3 }, nil, key: 'C')
13
+ ]
14
+ end
15
+
16
+ it 'should slice a batch by key' do
17
+ slices = described_class.slice(batch)
18
+
19
+ expect(slices).
20
+ to match([
21
+ match_array([
22
+ Deimos::Message.new({ v: 1 }, nil, key: 'C'),
23
+ Deimos::Message.new({ v: 123 }, nil, key: 'A'),
24
+ Deimos::Message.new({ v: 999 }, nil, key: 'B')
25
+ ]),
26
+ match_array([
27
+ Deimos::Message.new({ v: 456 }, nil, key: 'A'),
28
+ Deimos::Message.new({ v: 2 }, nil, key: 'C')
29
+ ]),
30
+ match_array([
31
+ Deimos::Message.new({ v: 3 }, nil, key: 'C')
32
+ ])
33
+ ])
34
+ end
35
+
36
+ it 'should handle empty batches' do
37
+ slices = described_class.slice([])
38
+
39
+ expect(slices).to be_empty
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'deimos/active_record_consume/schema_model_converter'
4
+ require 'deimos/schema_backends/avro_local'
5
+
6
+ # Wrapped in a module to prevent class leakage
7
+ module SchemaModelConverterTest
8
+ describe Deimos::ActiveRecordConsume::SchemaModelConverter do
9
+ # Create ActiveRecord table and model
10
+ before(:all) do
11
+ ActiveRecord::Base.connection.create_table(:wibbles, force: true) do |t|
12
+ t.integer(:wibble_id)
13
+ t.string(:name)
14
+ t.integer(:bar)
15
+ t.datetime(:birthday_int)
16
+ t.datetime(:birthday_long)
17
+ t.datetime(:birthday_optional)
18
+ t.timestamps
19
+ end
20
+
21
+ # :nodoc:
22
+ class Wibble < ActiveRecord::Base
23
+ end
24
+ Wibble.reset_column_information
25
+ end
26
+
27
+ after(:all) do
28
+ ActiveRecord::Base.connection.drop_table(:wibbles)
29
+ end
30
+
31
+ let(:schema) { Deimos::SchemaBackends::AvroLocal.new(schema: 'Wibble', namespace: 'com.my-namespace') }
32
+ let(:inst) { described_class.new(schema, Wibble) }
33
+
34
+ describe '#convert' do
35
+ it 'should extract attributes from the payload' do
36
+ payload = { 'id' => 123, 'wibble_id' => 456, 'name' => 'wibble' }
37
+
38
+ expect(inst.convert(payload)).to include('id' => 123, 'wibble_id' => 456, 'name' => 'wibble')
39
+ end
40
+
41
+ it 'should ignore payload fields that don\'t exist' do
42
+ payload = { 'foo' => 'abc' }
43
+
44
+ expect(inst.convert(payload)).not_to include('foo')
45
+ end
46
+
47
+ it 'should ignore fields in the schema but not in the model' do
48
+ payload = { 'floop' => 'def' }
49
+
50
+ expect(inst.convert(payload)).not_to include('floop')
51
+ end
52
+
53
+ it 'should ignore model fields not in the schema' do
54
+ payload = { 'bar' => 'xyz' }
55
+
56
+ expect(inst.convert(payload)).not_to include('bar')
57
+ end
58
+
59
+ it 'should handle nils' do
60
+ payload = { 'name' => nil }
61
+
62
+ expect(inst.convert(payload)['name']).to be_nil
63
+ end
64
+
65
+ describe 'timestamps' do
66
+ it 'should ignore AR timestamp fields' do
67
+ payload = { 'updated_at' => '1234567890', 'created_at' => '2345678901' }
68
+
69
+ expect(inst.convert(payload)).not_to include('updated_at', 'created_at')
70
+ end
71
+
72
+ it 'should parse timestamps' do
73
+ first = Time.zone.local(2019, 1, 1, 11, 12, 13)
74
+ second = Time.zone.local(2019, 2, 2, 12, 13, 14)
75
+
76
+ payload = { 'birthday_int' => first.to_i, 'birthday_long' => second.to_i }
77
+
78
+ expect(inst.convert(payload)).to include('birthday_int' => first, 'birthday_long' => second)
79
+ end
80
+
81
+ it 'should parse integer-string timestamps' do
82
+ date = Time.zone.local(2019, 1, 1, 11, 12, 13)
83
+
84
+ payload = { 'birthday_int' => '1546359133' }
85
+
86
+ expect(inst.convert(payload)).to include('birthday_int' => date)
87
+ end
88
+
89
+ it 'should ignore other strings for timestamps' do
90
+ payload = { 'birthday_int' => 'some-other-val' }
91
+
92
+ expect(inst.convert(payload)).to include('birthday_int' => 'some-other-val')
93
+ end
94
+ end
95
+
96
+ it 'should coerce nullable unions' do
97
+ date = Time.zone.local(2019, 1, 1, 11, 12, 13)
98
+
99
+ payload = { 'birthday_optional' => date.to_i }
100
+
101
+ expect(inst.convert(payload)).to include('birthday_optional' => date)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'date'
4
4
 
5
- # :nodoc:
6
- module ActiveRecordProducerTest
7
- describe Deimos::ActiveRecordConsumer do
5
+ # Wrapped in a module to prevent class leakage
6
+ module ActiveRecordConsumerTest
7
+ describe Deimos::ActiveRecordConsumer, 'Message Consumer' do
8
8
 
9
9
  before(:all) do
10
10
  ActiveRecord::Base.connection.create_table(:widgets, force: true) do |t|
@@ -137,13 +137,5 @@ module ActiveRecordProducerTest
137
137
  expect(Widget.find_by_test_id('id1').some_int).to eq(3)
138
138
  expect(Widget.find_by_test_id('id2').some_int).to eq(4)
139
139
  end
140
-
141
- it 'should coerce int values to datetimes' do
142
- column = Widget.columns.find { |c| c.name == 'some_datetime_int' }
143
- expect(MyConsumer.new.send(:_coerce_field, column, 1_579_046_400)).to eq('2020-01-14 19:00:00 -0500')
144
- expect(MyConsumer.new.send(:_coerce_field, column, '1579046400')).to eq('2020-01-14 19:00:00 -0500')
145
- expect(MyConsumer.new.send(:_coerce_field, column, 'some-other-val')).to eq('some-other-val')
146
- end
147
-
148
140
  end
149
141
  end
@@ -1,107 +1,85 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # :nodoc:
4
- module ActiveRecordProducerTest
5
- describe Deimos::ActiveRecordProducer do
3
+ describe Deimos::ActiveRecordProducer do
6
4
 
7
- before(:all) do
8
- ActiveRecord::Base.connection.create_table(:widgets) do |t|
9
- t.string(:test_id)
10
- t.integer(:some_int)
11
- t.boolean(:some_bool)
12
- t.timestamps
13
- end
5
+ include_context 'with widgets'
14
6
 
15
- # :nodoc:
16
- class Widget < ActiveRecord::Base
17
- # @return [String]
18
- def generated_id
19
- 'generated_id'
20
- end
21
- end
22
- end
7
+ prepend_before(:each) do
23
8
 
24
- after(:all) do
25
- ActiveRecord::Base.connection.drop_table(:widgets)
9
+ producer_class = Class.new(Deimos::ActiveRecordProducer) do
10
+ schema 'MySchema'
11
+ namespace 'com.my-namespace'
12
+ topic 'my-topic'
13
+ key_config none: true
26
14
  end
15
+ stub_const('MyProducer', producer_class)
27
16
 
28
- prepend_before(:each) do
29
-
30
- producer_class = Class.new(Deimos::ActiveRecordProducer) do
31
- schema 'MySchema'
32
- namespace 'com.my-namespace'
33
- topic 'my-topic'
34
- key_config none: true
35
- end
36
- stub_const('MyProducer', producer_class)
37
-
38
- producer_class = Class.new(Deimos::ActiveRecordProducer) do
39
- schema 'MySchemaWithBooleans'
40
- namespace 'com.my-namespace'
41
- topic 'my-topic-with-boolean'
42
- key_config none: true
43
- end
44
- stub_const('MyBooleanProducer', producer_class)
45
-
46
- producer_class = Class.new(Deimos::ActiveRecordProducer) do
47
- schema 'MySchemaWithId'
48
- namespace 'com.my-namespace'
49
- topic 'my-topic-with-id'
50
- key_config none: true
51
- record_class Widget
17
+ producer_class = Class.new(Deimos::ActiveRecordProducer) do
18
+ schema 'MySchemaWithBooleans'
19
+ namespace 'com.my-namespace'
20
+ topic 'my-topic-with-boolean'
21
+ key_config none: true
22
+ end
23
+ stub_const('MyBooleanProducer', producer_class)
52
24
 
53
- # :nodoc:
54
- def self.generate_payload(attrs, widget)
55
- super.merge(message_id: widget.generated_id)
56
- end
25
+ producer_class = Class.new(Deimos::ActiveRecordProducer) do
26
+ schema 'MySchemaWithId'
27
+ namespace 'com.my-namespace'
28
+ topic 'my-topic-with-id'
29
+ key_config none: true
30
+ record_class Widget
57
31
 
32
+ # :nodoc:
33
+ def self.generate_payload(attrs, widget)
34
+ super.merge(message_id: widget.generated_id)
58
35
  end
59
- stub_const('MyProducerWithID', producer_class)
60
36
 
61
- producer_class = Class.new(Deimos::ActiveRecordProducer) do
62
- schema 'MySchemaWithUniqueId'
63
- namespace 'com.my-namespace'
64
- topic 'my-topic-with-unique-id'
65
- key_config field: :id
66
- record_class Widget
67
- end
68
- stub_const('MyProducerWithUniqueID', producer_class)
69
37
  end
70
-
71
- it 'should send events correctly' do
72
- MyProducer.send_event(Widget.new(test_id: 'abc', some_int: 3))
73
- expect('my-topic').to have_sent(test_id: 'abc', some_int: 3)
38
+ stub_const('MyProducerWithID', producer_class)
39
+
40
+ producer_class = Class.new(Deimos::ActiveRecordProducer) do
41
+ schema 'MySchemaWithUniqueId'
42
+ namespace 'com.my-namespace'
43
+ topic 'my-topic-with-unique-id'
44
+ key_config field: :id
45
+ record_class Widget
74
46
  end
47
+ stub_const('MyProducerWithUniqueID', producer_class)
48
+ end
75
49
 
76
- it 'should coerce values' do
77
- MyProducer.send_event(Widget.new(test_id: 'abc', some_int: '3'))
78
- MyProducer.send_event(Widget.new(test_id: 'abc', some_int: 4.5))
79
- expect('my-topic').to have_sent(test_id: 'abc', some_int: 3)
80
- expect('my-topic').to have_sent(test_id: 'abc', some_int: 4)
81
- expect {
82
- MyProducer.send_event(Widget.new(test_id: 'abc', some_int: nil))
83
- }.to raise_error(Avro::SchemaValidator::ValidationError)
84
-
85
- MyBooleanProducer.send_event(Widget.new(test_id: 'abc', some_bool: nil))
86
- MyBooleanProducer.send_event(Widget.new(test_id: 'abc', some_bool: true))
87
- expect('my-topic-with-boolean').to have_sent(test_id: 'abc', some_bool: false)
88
- expect('my-topic-with-boolean').to have_sent(test_id: 'abc', some_bool: true)
89
- end
50
+ it 'should send events correctly' do
51
+ MyProducer.send_event(Widget.new(test_id: 'abc', some_int: 3))
52
+ expect('my-topic').to have_sent(test_id: 'abc', some_int: 3)
53
+ end
90
54
 
91
- it 'should be able to call the record' do
92
- widget = Widget.create!(test_id: 'abc2', some_int: 3)
93
- MyProducerWithID.send_event(id: widget.id, test_id: 'abc2', some_int: 3)
94
- expect('my-topic-with-id').to have_sent(
95
- test_id: 'abc2',
96
- some_int: 3,
97
- message_id: 'generated_id',
98
- timestamp: anything
99
- )
100
- end
55
+ it 'should coerce values' do
56
+ MyProducer.send_event(Widget.new(test_id: 'abc', some_int: '3'))
57
+ MyProducer.send_event(Widget.new(test_id: 'abc', some_int: 4.5))
58
+ expect('my-topic').to have_sent(test_id: 'abc', some_int: 3)
59
+ expect('my-topic').to have_sent(test_id: 'abc', some_int: 4)
60
+ expect {
61
+ MyProducer.send_event(Widget.new(test_id: 'abc', some_int: nil))
62
+ }.to raise_error(Avro::SchemaValidator::ValidationError)
63
+
64
+ MyBooleanProducer.send_event(Widget.new(test_id: 'abc', some_bool: nil))
65
+ MyBooleanProducer.send_event(Widget.new(test_id: 'abc', some_bool: true))
66
+ expect('my-topic-with-boolean').to have_sent(test_id: 'abc', some_bool: false)
67
+ expect('my-topic-with-boolean').to have_sent(test_id: 'abc', some_bool: true)
68
+ end
101
69
 
102
- specify '#watched_attributes' do
103
- expect(MyProducer.watched_attributes).to eq(%w(test_id some_int))
104
- end
70
+ it 'should be able to call the record' do
71
+ widget = Widget.create!(test_id: 'abc2', some_int: 3)
72
+ MyProducerWithID.send_event(id: widget.id, test_id: 'abc2', some_int: 3)
73
+ expect('my-topic-with-id').to have_sent(
74
+ test_id: 'abc2',
75
+ some_int: 3,
76
+ message_id: 'generated_id',
77
+ timestamp: anything
78
+ )
79
+ end
105
80
 
81
+ specify '#watched_attributes' do
82
+ expect(MyProducer.watched_attributes).to eq(%w(test_id some_int))
106
83
  end
84
+
107
85
  end
@@ -1,14 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'deimos/batch_consumer'
4
-
5
3
  # :nodoc:
6
4
  module ConsumerTest
7
- describe Deimos::BatchConsumer do
5
+ describe Deimos::Consumer, 'Batch Consumer' do
8
6
 
9
7
  prepend_before(:each) do
10
8
  # :nodoc:
11
- consumer_class = Class.new(Deimos::BatchConsumer) do
9
+ consumer_class = Class.new(described_class) do
12
10
  schema 'MySchema'
13
11
  namespace 'com.my-namespace'
14
12
  key_config field: 'test_id'
@@ -32,6 +30,24 @@ module ConsumerTest
32
30
  batch.concat([{ 'invalid' => 'key' }])
33
31
  end
34
32
 
33
+ it 'should provide backwards compatibility for BatchConsumer class' do
34
+ consumer_class = Class.new(Deimos::BatchConsumer) do
35
+ schema 'MySchema'
36
+ namespace 'com.my-namespace'
37
+ key_config field: 'test_id'
38
+
39
+ # :nodoc:
40
+ def consume_batch(_payloads, _metadata)
41
+ raise 'This should not be called unless call_original is set'
42
+ end
43
+ end
44
+ stub_const('ConsumerTest::MyOldBatchConsumer', consumer_class)
45
+
46
+ test_consume_batch(MyOldBatchConsumer, batch) do |received, _metadata|
47
+ expect(received).to eq(batch)
48
+ end
49
+ end
50
+
35
51
  it 'should consume a batch of messages' do
36
52
  test_consume_batch(MyBatchConsumer, batch) do |received, _metadata|
37
53
  expect(received).to eq(batch)
@@ -95,11 +111,12 @@ module ConsumerTest
95
111
  test_consume_batch('my_batch_consume_topic', batch, keys: keys) do |_received, metadata|
96
112
  # Mock decode_key extracts the value of the first field as the key
97
113
  expect(metadata[:keys]).to eq(%w(foo bar))
114
+ expect(metadata[:first_offset]).to eq(1)
98
115
  end
99
116
  end
100
117
 
101
118
  it 'should decode plain keys for all messages in the batch' do
102
- consumer_class = Class.new(Deimos::BatchConsumer) do
119
+ consumer_class = Class.new(described_class) do
103
120
  schema 'MySchema'
104
121
  namespace 'com.my-namespace'
105
122
  key_config plain: true
@@ -115,7 +132,7 @@ module ConsumerTest
115
132
  describe 'timestamps' do
116
133
  before(:each) do
117
134
  # :nodoc:
118
- consumer_class = Class.new(Deimos::BatchConsumer) do
135
+ consumer_class = Class.new(described_class) do
119
136
  schema 'MySchemaWithDateTimes'
120
137
  namespace 'com.my-namespace'
121
138
  key_config plain: true
@@ -197,7 +214,7 @@ module ConsumerTest
197
214
  describe 'logging' do
198
215
  before(:each) do
199
216
  # :nodoc:
200
- consumer_class = Class.new(Deimos::BatchConsumer) do
217
+ consumer_class = Class.new(described_class) do
201
218
  schema 'MySchemaWithUniqueId'
202
219
  namespace 'com.my-namespace'
203
220
  key_config plain: true
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Mock consumer
3
4
  class MyConfigConsumer < Deimos::Consumer
5
+ # :no-doc:
6
+ def consume
7
+ end
4
8
  end
5
9
  describe Deimos, 'configuration' do
6
10
  it 'should configure with deprecated fields' do
@@ -2,11 +2,11 @@
2
2
 
3
3
  # :nodoc:
4
4
  module ConsumerTest
5
- describe Deimos::Consumer do
5
+ describe Deimos::Consumer, 'Message Consumer' do
6
6
 
7
7
  prepend_before(:each) do
8
8
  # :nodoc:
9
- consumer_class = Class.new(Deimos::Consumer) do
9
+ consumer_class = Class.new(described_class) do
10
10
  schema 'MySchema'
11
11
  namespace 'com.my-namespace'
12
12
  key_config field: 'test_id'
@@ -90,7 +90,7 @@ module ConsumerTest
90
90
  'some_int' => 123 },
91
91
  { skip_expectation: true }
92
92
  ) { raise 'OH NOES' }
93
- } .not_to raise_error
93
+ }.not_to raise_error
94
94
  end
95
95
 
96
96
  it 'should not fail when consume fails without reraising errors' do
@@ -101,7 +101,7 @@ module ConsumerTest
101
101
  { 'invalid' => 'key' },
102
102
  { skip_expectation: true }
103
103
  )
104
- } .not_to raise_error
104
+ }.not_to raise_error
105
105
  end
106
106
 
107
107
  it 'should call original' do
@@ -121,7 +121,7 @@ module ConsumerTest
121
121
  end
122
122
 
123
123
  it 'should use the key schema if set' do
124
- consumer_class = Class.new(Deimos::Consumer) do
124
+ consumer_class = Class.new(described_class) do
125
125
  schema 'MySchema'
126
126
  namespace 'com.my-namespace'
127
127
  key_config schema: 'MySchema_key'
@@ -132,7 +132,7 @@ module ConsumerTest
132
132
  end
133
133
 
134
134
  it 'should not decode if plain is set' do
135
- consumer_class = Class.new(Deimos::Consumer) do
135
+ consumer_class = Class.new(described_class) do
136
136
  schema 'MySchema'
137
137
  namespace 'com.my-namespace'
138
138
  key_config plain: true
@@ -142,7 +142,7 @@ module ConsumerTest
142
142
  end
143
143
 
144
144
  it 'should error with nothing set' do
145
- consumer_class = Class.new(Deimos::Consumer) do
145
+ consumer_class = Class.new(described_class) do
146
146
  schema 'MySchema'
147
147
  namespace 'com.my-namespace'
148
148
  end
@@ -156,7 +156,7 @@ module ConsumerTest
156
156
  describe 'timestamps' do
157
157
  before(:each) do
158
158
  # :nodoc:
159
- consumer_class = Class.new(Deimos::Consumer) do
159
+ consumer_class = Class.new(described_class) do
160
160
  schema 'MySchemaWithDateTimes'
161
161
  namespace 'com.my-namespace'
162
162
  key_config plain: true