deimos-ruby 2.1.7 → 2.1.9

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: 3890d253c7d9b137c55d02e1968697b9a2921a8cd57dec3be49b6eb3ea50d670
4
- data.tar.gz: 4538ffbef398ea60d5fe1f3cd20e30a1fe4e45c00e2369717ea160491554d687
3
+ metadata.gz: 52b13a01b0356e08ceb56ae75eb55f39d6578070085184521e063d7fb3714f1f
4
+ data.tar.gz: 36f972a37d52c3c8fb3ea5e53c21c1f39caad58fa8411f0dc380b5a8b7f1ed9e
5
5
  SHA512:
6
- metadata.gz: 221bf0e05c603dda8f6451d4205ed4ea86f209fe8bdc4d6c43c5ad1a19aff57c9df80f9a82a3c134a31e78b6072eab076d6f3e8594fb8a01f4892857068f9aa0
7
- data.tar.gz: 41cec49b3a6913617398bf194fd1d8fb65304231b34aca051fad5c37307b2e617b571c220ff95aefec57b2200564a9827bddd37ae80553a8091d784561f0c6d5
6
+ metadata.gz: e5d2c15d11756bc133f1ea57e9c112023c557aa6200a4ac7469f9b07f23835d16593ae6ef9d4e20c76dcb4a6acff9d4723355e14ec6e28d1887e09958a6f3283
7
+ data.tar.gz: d9f1e82068b7f5c7424590fda13a4d59fa9345dc3841c9b1f8413248f031548b276fa5660d39c9fe4f5621bc47089049f70af97d207e3f02af5122d3971c10a9
data/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## UNRELEASED
9
9
 
10
+ # 2.1.9 - 2025-08-13
11
+
12
+ - Fix: When a model uses multiple producers, `send_kafka_event_on_update` no longer aggregates `watched_attributes` across all producers and publishes to all of them if any field changed. It now evaluates each producer’s `watched_attributes` independently and publishes only to the producers whose fields changed (per‑producer isolation).
13
+
14
+ # 2.1.8 - 2025-08-11
15
+
16
+ - Feature: Producers can now customize the deletion payload, so different producers using the same model can send different delete messages.
17
+
10
18
  # 2.1.7 - 2025-07-23
11
19
 
12
20
  - Feature: Skip batch_record_list fill_primary_keys! if the primary key has already been filled during consumption
@@ -320,7 +328,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
320
328
  # 1.12.2 - 2021-12-10
321
329
 
322
330
  ### Features :star:
323
-
324
331
  - Added `Deimos.encode` and `Deimos.decode` for non-topic-related encoding and decoding.
325
332
 
326
333
  # 1.12.1 - 2021-11-02
@@ -331,7 +338,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
331
338
  # 1.12.0 - 2021-11-01
332
339
 
333
340
  ### Features :star:
334
-
335
341
  - Generate Schema classes from Avro Schemas
336
342
  - Use Schema Classes in your consumer and producer
337
343
 
@@ -365,7 +371,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
365
371
  - Added a `save_record` method to ActiveRecordConsumer in case calling code wants to work with the record before saving.
366
372
 
367
373
  - ### Fixes :wrench:
368
-
369
374
  - Fixed a regression where the default values for consumer / Phobos listener configs were not correct (they were all nil). This is technically a breaking change, but it puts the configs back the way they were at version 1.4 and matches the documentation.
370
375
 
371
376
  ## 1.9.2 - 2021-01-29
@@ -382,7 +387,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
382
387
  - Bumped the version of ruby-kafka to latest
383
388
 
384
389
  - ### Fixes :wrench:
385
-
386
390
  - Prevents DB Poller from reconnecting to DB if there is an open transaction
387
391
  - Replaces `before` by `prepend_before` for more consistent test setups.
388
392
  - Adds validation in the `kafka_producers` method (fixes [#90](https://github.com/flipp-oss/deimos/issues/90))
@@ -76,6 +76,15 @@ module Deimos
76
76
  Utils::SchemaClass.instance(payload, encoder.schema, encoder.namespace)
77
77
  end
78
78
 
79
+ # Deletion payload for a record by default, delegate to the
80
+ # model's deletion_payload. Producers may override this method to
81
+ # customize the deletion key/payload per producer.
82
+ # @param record [ActiveRecord::Base]
83
+ # @return [Hash]
84
+ def generate_deletion_payload(record)
85
+ record.deletion_payload
86
+ end
87
+
79
88
  # Query to use when polling the database with the DbPoller. Add
80
89
  # includes, joins, or wheres as necessary, or replace entirely.
81
90
  # @param time_from [Time] the time to start the query from.
@@ -100,7 +109,7 @@ module Deimos
100
109
  end
101
110
 
102
111
  # Post process records after publishing
103
- # @param records [Array<ActiveRecord::Base>]
112
+ # @param _records [Array<ActiveRecord::Base>]
104
113
  def post_process(_records)
105
114
  end
106
115
 
@@ -27,18 +27,21 @@ module Deimos
27
27
  def send_kafka_event_on_update
28
28
  return unless self.class.kafka_config[:update]
29
29
 
30
- producers = self.class.kafka_producers
31
- fields = producers.flat_map { |p| p.watched_attributes(self) }.uniq
32
- fields -= ['updated_at']
33
- # Only send an event if a field we care about was changed.
34
- any_changes = fields.any? do |field|
35
- field_change = self.previous_changes[field]
36
- field_change.present? && field_change[0] != field_change[1]
37
- end
38
- return unless any_changes
39
30
  self.truncate_columns if Deimos.config.producers.truncate_columns
40
31
 
41
- producers.each { |p| p.send_event(self) }
32
+ producers = self.class.kafka_producers
33
+ producers.each do |producer|
34
+ fields = producer.watched_attributes(self)
35
+ fields -= ['updated_at']
36
+ # Only send an event if a field we care about was changed.
37
+ any_changes = fields.any? do |field|
38
+ field_change = self.previous_changes[field]
39
+ field_change.present? && field_change[0] != field_change[1]
40
+ end
41
+ next unless any_changes
42
+
43
+ producer.send_event(self)
44
+ end
42
45
  end
43
46
 
44
47
  # Send a deletion (null payload) event to Kafka.
@@ -46,7 +49,9 @@ module Deimos
46
49
  def send_kafka_event_on_destroy
47
50
  return unless self.class.kafka_config[:delete]
48
51
 
49
- self.class.kafka_producers.each { |p| p.publish_list([self.deletion_payload]) }
52
+ self.class.kafka_producers.each do |p|
53
+ p.publish_list([p.generate_deletion_payload(self)])
54
+ end
50
55
  end
51
56
 
52
57
  # Payload to send after we are destroyed.
@@ -80,7 +85,7 @@ module Deimos
80
85
  # @!visibility private
81
86
  def import_without_validations_or_callbacks(column_names,
82
87
  array_of_attributes,
83
- options={})
88
+ options = {})
84
89
  results = super
85
90
  if !self.kafka_config[:import] || array_of_attributes.empty?
86
91
  return results
@@ -110,7 +115,8 @@ module Deimos
110
115
  last_id = if self.connection.adapter_name.downcase =~ /sqlite/
111
116
  self.connection.select_value('select last_insert_rowid()') -
112
117
  hashes_without_id.size + 1
113
- else # mysql
118
+ else
119
+ # mysql
114
120
  self.connection.select_value('select LAST_INSERT_ID()')
115
121
  end
116
122
  hashes_without_id.each_with_index do |attrs, i|
@@ -128,6 +134,7 @@ module Deimos
128
134
  self.class.columns.each do |col|
129
135
  next unless col.type == :string
130
136
  next if self[col.name].blank?
137
+
131
138
  if self[col.name].to_s.length > col.limit
132
139
  self[col.name] = self[col.name][0..col.limit - 1]
133
140
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deimos
4
- VERSION = '2.1.7'
4
+ VERSION = '2.1.9'
5
5
  end
@@ -17,10 +17,16 @@ module KafkaSourceSpec
17
17
 
18
18
  # Dummy producer which mimicks the behavior of a real producer
19
19
  class WidgetProducer < Deimos::ActiveRecordProducer
20
+ def self.watched_attributes(_record)
21
+ %w(name widget_id)
22
+ end
20
23
  end
21
24
 
22
25
  # Dummy producer which mimicks the behavior of a real producer
23
26
  class WidgetProducerTheSecond < Deimos::ActiveRecordProducer
27
+ def self.watched_attributes(_record)
28
+ %w(description widget_id)
29
+ end
24
30
  end
25
31
 
26
32
  # Dummy class we can include the mixin in. Has a backing table created
@@ -105,23 +111,96 @@ module KafkaSourceSpec
105
111
  expect('my-topic-the-second').to have_sent(nil, widget.id)
106
112
  end
107
113
 
114
+ context 'multi-producer model using two different key fields' do
115
+ before(:each) do
116
+ class MultiKeyWidget < ActiveRecord::Base
117
+ include Deimos::KafkaSource
118
+ self.table_name = 'widgets'
119
+
120
+ def self.kafka_producers
121
+ [WidgetProducer, WidgetStringKeyProducer]
122
+ end
123
+ end
124
+
125
+ class WidgetStringKeyProducer < Deimos::ActiveRecordProducer
126
+ class << self
127
+
128
+ def generate_payload(attributes, record)
129
+ payload = super(attributes, record)
130
+ payload.merge('id' => record.model_id)
131
+ end
132
+
133
+ def generate_deletion_payload(record)
134
+ { payload_key: record.model_id }
135
+ end
136
+ end
137
+ end
138
+
139
+ Karafka::App.routes.redraw do
140
+ topic 'my-topic' do
141
+ namespace 'com.my-namespace'
142
+ schema 'Widget'
143
+ key_config field: :id
144
+ producer_class WidgetProducer
145
+ end
146
+
147
+ topic 'my-topic-the-Third' do
148
+ namespace 'com.my-namespace'
149
+ schema 'WidgetTheThird'
150
+ key_config field: :model_id
151
+ producer_class WidgetStringKeyProducer
152
+ end
153
+
154
+ end
155
+ end
156
+
157
+ it 'publishes create/delete with correct keys per producer (primary: id, secondary: model_id)' do
158
+ multi_key_widget = MultiKeyWidget.create!(widget_id: 7, model_id: 'model-id-123', name: 'name-123')
159
+
160
+ expect('my-topic').to have_sent({
161
+ widget_id: 7,
162
+ name: 'name-123',
163
+ id: multi_key_widget.id,
164
+ created_at: anything,
165
+ updated_at: anything
166
+ }, multi_key_widget.id)
167
+
168
+ expect('my-topic-the-Third').to have_sent({
169
+ widget_id: 7,
170
+ model_id: multi_key_widget.model_id,
171
+ id: multi_key_widget.model_id,
172
+ created_at: anything,
173
+ updated_at: anything
174
+ }, multi_key_widget.model_id)
175
+
176
+ expect(WidgetProducer).to receive(:generate_deletion_payload).and_call_original
177
+ expect(WidgetStringKeyProducer).to receive(:generate_deletion_payload).and_call_original
178
+
179
+ multi_key_widget.destroy
180
+
181
+ expect('my-topic').to have_sent(nil, multi_key_widget.id)
182
+ expect('my-topic-the-Third').to have_sent(nil, multi_key_widget.model_id)
183
+ end
184
+ end
185
+
108
186
  context 'with truncation off' do
109
187
  before(:each) do
110
188
  Deimos.config.producers.truncate_columns = false
111
189
  end
190
+
112
191
  it 'should not truncate values' do
113
- widget = Widget.create!(widget_id: 1, name: 'a'*500)
192
+ widget = Widget.create!(widget_id: 1, name: 'a' * 500)
114
193
  expect('my-topic').to have_sent({
115
194
  widget_id: 1,
116
- name: 'a'*500,
195
+ name: 'a' * 500,
117
196
  id: widget.id,
118
197
  created_at: anything,
119
198
  updated_at: anything
120
199
  }, 1)
121
- widget.update_attribute(:name, 'b'*500)
200
+ widget.update_attribute(:name, 'b' * 500)
122
201
  expect('my-topic').to have_sent({
123
202
  widget_id: 1,
124
- name: 'b'*500,
203
+ name: 'b' * 500,
125
204
  id: widget.id,
126
205
  created_at: anything,
127
206
  updated_at: anything
@@ -133,19 +212,20 @@ module KafkaSourceSpec
133
212
  before(:each) do
134
213
  Deimos.config.producers.truncate_columns = true
135
214
  end
215
+
136
216
  it 'should truncate values' do
137
- widget = Widget.create!(widget_id: 1, name: 'a'*500)
217
+ widget = Widget.create!(widget_id: 1, name: 'a' * 500)
138
218
  expect('my-topic').to have_sent({
139
219
  widget_id: 1,
140
- name: 'a'*100,
220
+ name: 'a' * 100,
141
221
  id: widget.id,
142
222
  created_at: anything,
143
223
  updated_at: anything
144
224
  }, 1)
145
- widget.update_attribute(:name, 'b'*500)
225
+ widget.update_attribute(:name, 'b' * 500)
146
226
  expect('my-topic').to have_sent({
147
227
  widget_id: 1,
148
- name: 'b'*100,
228
+ name: 'b' * 100,
149
229
  id: widget.id,
150
230
  created_at: anything,
151
231
  updated_at: anything
@@ -304,6 +384,7 @@ module KafkaSourceSpec
304
384
  [WidgetProducer]
305
385
  end
306
386
  end
387
+
307
388
  WidgetNoImportHook.reset_column_information
308
389
  end
309
390
 
@@ -392,5 +473,41 @@ module KafkaSourceSpec
392
473
  }.to raise_error(Deimos::MissingImplementationError)
393
474
  end
394
475
  end
476
+
477
+ describe 'Isolated watched‑attribute per producer when send_kafka_event_on_update' do
478
+ it 'should only send events to producers that watch the name field' do
479
+ widget = Widget.create!(widget_id: 1, name: 'initial', description: 'initial desc')
480
+ clear_kafka_messages!
481
+
482
+ widget.update_attribute(:name, 'updated name')
483
+
484
+ expect('my-topic').to have_sent(hash_including(name: 'updated name'))
485
+ expect('my-topic-the-second').not_to have_sent(anything)
486
+ end
487
+
488
+ it 'should only send events to producers that watch the description field' do
489
+ widget = Widget.create!(widget_id: 1, name: 'test', description: 'initial desc')
490
+ clear_kafka_messages!
491
+
492
+ widget.update_attribute(:description, 'updated description')
493
+
494
+ expect('my-topic-the-second').to have_sent(anything)
495
+ expect('my-topic').not_to have_sent(anything)
496
+ end
497
+
498
+ it 'should send events to all producers when a commonly watched field changes' do
499
+ allow(WidgetProducer).to receive(:watched_attributes).and_return(%w(name widget_id))
500
+ allow(WidgetProducerTheSecond).to receive(:watched_attributes).and_return(%w(description widget_id))
501
+
502
+ widget = Widget.create!(widget_id: 1, name: 'test', description: 'test desc')
503
+ clear_kafka_messages!
504
+
505
+ widget.update_attribute(:widget_id, 999)
506
+
507
+ expect('my-topic').to have_sent(hash_including(widget_id: 999))
508
+ expect('my-topic-the-second').to have_sent(hash_including(widget_id: 999))
509
+ end
510
+ end
511
+
395
512
  end
396
513
  end
@@ -0,0 +1,27 @@
1
+ {
2
+ "namespace": "com.my-namespace",
3
+ "name": "WidgetTheThird",
4
+ "type": "record",
5
+ "fields": [
6
+ {
7
+ "name": "id",
8
+ "type": "string"
9
+ },
10
+ {
11
+ "name": "widget_id",
12
+ "type": "long"
13
+ },
14
+ {
15
+ "name": "model_id",
16
+ "type": "string"
17
+ },
18
+ {
19
+ "name": "updated_at",
20
+ "type": "long"
21
+ },
22
+ {
23
+ "name": "created_at",
24
+ "type": "long"
25
+ }
26
+ ]
27
+ }
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is autogenerated by Deimos, Do NOT modify
4
+ module Schemas; module MyNamespace
5
+ ### Primary Schema Class ###
6
+ # Autogenerated Schema for Record at com.my-namespace.WidgetTheThird
7
+ class WidgetTheThird < Deimos::SchemaClass::Record
8
+
9
+ ### Attribute Accessors ###
10
+ # @return [String]
11
+ attr_accessor :id
12
+ # @return [Integer]
13
+ attr_accessor :widget_id
14
+ # @return [String]
15
+ attr_accessor :model_id
16
+ # @return [Integer]
17
+ attr_accessor :updated_at
18
+ # @return [Integer]
19
+ attr_accessor :created_at
20
+
21
+ # @override
22
+ def initialize(_from_message: false, id: nil,
23
+ widget_id: nil,
24
+ model_id: nil,
25
+ updated_at: nil,
26
+ created_at: nil)
27
+ @_from_message = _from_message
28
+ super
29
+ self.id = id
30
+ self.widget_id = widget_id
31
+ self.model_id = model_id
32
+ self.updated_at = updated_at
33
+ self.created_at = created_at
34
+ end
35
+
36
+ # @override
37
+ def schema
38
+ 'WidgetTheThird'
39
+ end
40
+
41
+ # @override
42
+ def namespace
43
+ 'com.my-namespace'
44
+ end
45
+
46
+ # @override
47
+ def as_json(_opts={})
48
+ {
49
+ 'id' => @id,
50
+ 'widget_id' => @widget_id,
51
+ 'model_id' => @model_id,
52
+ 'updated_at' => @updated_at,
53
+ 'created_at' => @created_at
54
+ }
55
+ end
56
+ end
57
+ end; end
@@ -1648,6 +1648,66 @@ module Schemas
1648
1648
  end
1649
1649
 
1650
1650
 
1651
+ spec/app/lib/schema_classes/widget_the_third.rb:
1652
+ # frozen_string_literal: true
1653
+
1654
+ # This file is autogenerated by Deimos, Do NOT modify
1655
+ module Schemas
1656
+ ### Primary Schema Class ###
1657
+ # Autogenerated Schema for Record at com.my-namespace.WidgetTheThird
1658
+ class WidgetTheThird < Deimos::SchemaClass::Record
1659
+
1660
+ ### Attribute Accessors ###
1661
+ # @return [String]
1662
+ attr_accessor :id
1663
+ # @return [Integer]
1664
+ attr_accessor :widget_id
1665
+ # @return [String]
1666
+ attr_accessor :model_id
1667
+ # @return [Integer]
1668
+ attr_accessor :updated_at
1669
+ # @return [Integer]
1670
+ attr_accessor :created_at
1671
+
1672
+ # @override
1673
+ def initialize(_from_message: false, id: nil,
1674
+ widget_id: nil,
1675
+ model_id: nil,
1676
+ updated_at: nil,
1677
+ created_at: nil)
1678
+ @_from_message = _from_message
1679
+ super
1680
+ self.id = id
1681
+ self.widget_id = widget_id
1682
+ self.model_id = model_id
1683
+ self.updated_at = updated_at
1684
+ self.created_at = created_at
1685
+ end
1686
+
1687
+ # @override
1688
+ def schema
1689
+ 'WidgetTheThird'
1690
+ end
1691
+
1692
+ # @override
1693
+ def namespace
1694
+ 'com.my-namespace'
1695
+ end
1696
+
1697
+ # @override
1698
+ def as_json(_opts={})
1699
+ {
1700
+ 'id' => @id,
1701
+ 'widget_id' => @widget_id,
1702
+ 'model_id' => @model_id,
1703
+ 'updated_at' => @updated_at,
1704
+ 'created_at' => @created_at
1705
+ }
1706
+ end
1707
+ end
1708
+ end
1709
+
1710
+
1651
1711
  spec/app/lib/schema_classes/yet_another_enum.rb:
1652
1712
  # frozen_string_literal: true
1653
1713
 
@@ -1653,3 +1653,63 @@ module Schemas
1653
1653
  end
1654
1654
  end
1655
1655
 
1656
+
1657
+ spec/app/lib/schema_classes/widget_the_third.rb:
1658
+ # frozen_string_literal: true
1659
+
1660
+ # This file is autogenerated by Deimos, Do NOT modify
1661
+ module Schemas
1662
+ ### Primary Schema Class ###
1663
+ # Autogenerated Schema for Record at com.my-namespace.WidgetTheThird
1664
+ class WidgetTheThird < Deimos::SchemaClass::Record
1665
+
1666
+ ### Attribute Accessors ###
1667
+ # @return [String]
1668
+ attr_accessor :id
1669
+ # @return [Integer]
1670
+ attr_accessor :widget_id
1671
+ # @return [String]
1672
+ attr_accessor :model_id
1673
+ # @return [Integer]
1674
+ attr_accessor :updated_at
1675
+ # @return [Integer]
1676
+ attr_accessor :created_at
1677
+
1678
+ # @override
1679
+ def initialize(_from_message: false, id: nil,
1680
+ widget_id: nil,
1681
+ model_id: nil,
1682
+ updated_at: nil,
1683
+ created_at: nil)
1684
+ @_from_message = _from_message
1685
+ super
1686
+ self.id = id
1687
+ self.widget_id = widget_id
1688
+ self.model_id = model_id
1689
+ self.updated_at = updated_at
1690
+ self.created_at = created_at
1691
+ end
1692
+
1693
+ # @override
1694
+ def schema
1695
+ 'WidgetTheThird'
1696
+ end
1697
+
1698
+ # @override
1699
+ def namespace
1700
+ 'com.my-namespace'
1701
+ end
1702
+
1703
+ # @override
1704
+ def as_json(_opts={})
1705
+ {
1706
+ 'id' => @id,
1707
+ 'widget_id' => @widget_id,
1708
+ 'model_id' => @model_id,
1709
+ 'updated_at' => @updated_at,
1710
+ 'created_at' => @created_at
1711
+ }
1712
+ end
1713
+ end
1714
+ end
1715
+
@@ -1648,6 +1648,66 @@ module Schemas
1648
1648
  end
1649
1649
 
1650
1650
 
1651
+ spec/app/lib/schema_classes/widget_the_third.rb:
1652
+ # frozen_string_literal: true
1653
+
1654
+ # This file is autogenerated by Deimos, Do NOT modify
1655
+ module Schemas
1656
+ ### Primary Schema Class ###
1657
+ # Autogenerated Schema for Record at com.my-namespace.WidgetTheThird
1658
+ class WidgetTheThird < Deimos::SchemaClass::Record
1659
+
1660
+ ### Attribute Accessors ###
1661
+ # @return [String]
1662
+ attr_accessor :id
1663
+ # @return [Integer]
1664
+ attr_accessor :widget_id
1665
+ # @return [String]
1666
+ attr_accessor :model_id
1667
+ # @return [Integer]
1668
+ attr_accessor :updated_at
1669
+ # @return [Integer]
1670
+ attr_accessor :created_at
1671
+
1672
+ # @override
1673
+ def initialize(_from_message: false, id: nil,
1674
+ widget_id: nil,
1675
+ model_id: nil,
1676
+ updated_at: nil,
1677
+ created_at: nil)
1678
+ @_from_message = _from_message
1679
+ super
1680
+ self.id = id
1681
+ self.widget_id = widget_id
1682
+ self.model_id = model_id
1683
+ self.updated_at = updated_at
1684
+ self.created_at = created_at
1685
+ end
1686
+
1687
+ # @override
1688
+ def schema
1689
+ 'WidgetTheThird'
1690
+ end
1691
+
1692
+ # @override
1693
+ def namespace
1694
+ 'com.my-namespace'
1695
+ end
1696
+
1697
+ # @override
1698
+ def as_json(_opts={})
1699
+ {
1700
+ 'id' => @id,
1701
+ 'widget_id' => @widget_id,
1702
+ 'model_id' => @model_id,
1703
+ 'updated_at' => @updated_at,
1704
+ 'created_at' => @created_at
1705
+ }
1706
+ end
1707
+ end
1708
+ end
1709
+
1710
+
1651
1711
  spec/app/lib/schema_classes/yet_another_enum.rb:
1652
1712
  # frozen_string_literal: true
1653
1713