sequent 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequent +31 -25
  3. data/lib/notices.rb +2 -0
  4. data/lib/sequent/application_record.rb +2 -0
  5. data/lib/sequent/configuration.rb +24 -31
  6. data/lib/sequent/core/aggregate_repository.rb +17 -13
  7. data/lib/sequent/core/aggregate_root.rb +16 -7
  8. data/lib/sequent/core/aggregate_roots.rb +24 -0
  9. data/lib/sequent/core/aggregate_snapshotter.rb +8 -5
  10. data/lib/sequent/core/base_command_handler.rb +4 -2
  11. data/lib/sequent/core/command.rb +17 -9
  12. data/lib/sequent/core/command_record.rb +8 -3
  13. data/lib/sequent/core/command_service.rb +18 -18
  14. data/lib/sequent/core/core.rb +2 -0
  15. data/lib/sequent/core/current_event.rb +2 -0
  16. data/lib/sequent/core/event.rb +16 -11
  17. data/lib/sequent/core/event_publisher.rb +16 -15
  18. data/lib/sequent/core/event_record.rb +7 -7
  19. data/lib/sequent/core/event_store.rb +57 -50
  20. data/lib/sequent/core/ext/ext.rb +9 -1
  21. data/lib/sequent/core/helpers/array_with_type.rb +4 -1
  22. data/lib/sequent/core/helpers/association_validator.rb +9 -7
  23. data/lib/sequent/core/helpers/attribute_support.rb +45 -28
  24. data/lib/sequent/core/helpers/autoset_attributes.rb +4 -4
  25. data/lib/sequent/core/helpers/boolean_validator.rb +6 -1
  26. data/lib/sequent/core/helpers/copyable.rb +2 -2
  27. data/lib/sequent/core/helpers/date_time_validator.rb +4 -1
  28. data/lib/sequent/core/helpers/date_validator.rb +6 -1
  29. data/lib/sequent/core/helpers/default_validators.rb +12 -10
  30. data/lib/sequent/core/helpers/equal_support.rb +8 -6
  31. data/lib/sequent/core/helpers/helpers.rb +2 -0
  32. data/lib/sequent/core/helpers/mergable.rb +6 -5
  33. data/lib/sequent/core/helpers/message_handler.rb +3 -1
  34. data/lib/sequent/core/helpers/param_support.rb +19 -15
  35. data/lib/sequent/core/helpers/secret.rb +14 -12
  36. data/lib/sequent/core/helpers/string_support.rb +5 -4
  37. data/lib/sequent/core/helpers/string_to_value_parsers.rb +7 -2
  38. data/lib/sequent/core/helpers/string_validator.rb +6 -1
  39. data/lib/sequent/core/helpers/type_conversion_support.rb +5 -3
  40. data/lib/sequent/core/helpers/uuid_helper.rb +5 -2
  41. data/lib/sequent/core/helpers/value_validators.rb +23 -9
  42. data/lib/sequent/core/persistors/active_record_persistor.rb +19 -9
  43. data/lib/sequent/core/persistors/persistor.rb +16 -14
  44. data/lib/sequent/core/persistors/persistors.rb +2 -0
  45. data/lib/sequent/core/persistors/replay_optimized_postgres_persistor.rb +70 -47
  46. data/lib/sequent/core/projector.rb +25 -22
  47. data/lib/sequent/core/random_uuid_generator.rb +2 -0
  48. data/lib/sequent/core/sequent_oj.rb +2 -0
  49. data/lib/sequent/core/stream_record.rb +9 -3
  50. data/lib/sequent/core/transactions/active_record_transaction_provider.rb +5 -9
  51. data/lib/sequent/core/transactions/no_transactions.rb +2 -1
  52. data/lib/sequent/core/transactions/transactions.rb +2 -0
  53. data/lib/sequent/core/value_object.rb +8 -10
  54. data/lib/sequent/core/workflow.rb +7 -5
  55. data/lib/sequent/generator/aggregate.rb +16 -10
  56. data/lib/sequent/generator/command.rb +26 -19
  57. data/lib/sequent/generator/event.rb +19 -17
  58. data/lib/sequent/generator/generator.rb +2 -0
  59. data/lib/sequent/generator/project.rb +2 -0
  60. data/lib/sequent/generator/template_project/Gemfile +1 -1
  61. data/lib/sequent/generator.rb +2 -0
  62. data/lib/sequent/migrations/executor.rb +22 -13
  63. data/lib/sequent/migrations/functions.rb +5 -6
  64. data/lib/sequent/migrations/migrate_events.rb +12 -9
  65. data/lib/sequent/migrations/migrations.rb +2 -1
  66. data/lib/sequent/migrations/planner.rb +33 -23
  67. data/lib/sequent/migrations/projectors.rb +4 -3
  68. data/lib/sequent/migrations/sql.rb +2 -0
  69. data/lib/sequent/migrations/view_schema.rb +84 -45
  70. data/lib/sequent/rake/migration_tasks.rb +58 -22
  71. data/lib/sequent/rake/tasks.rb +5 -2
  72. data/lib/sequent/sequent.rb +2 -0
  73. data/lib/sequent/support/database.rb +30 -15
  74. data/lib/sequent/support/view_projection.rb +6 -3
  75. data/lib/sequent/support/view_schema.rb +2 -0
  76. data/lib/sequent/support.rb +2 -0
  77. data/lib/sequent/test/command_handler_helpers.rb +35 -17
  78. data/lib/sequent/test/event_handler_helpers.rb +10 -4
  79. data/lib/sequent/test/event_stream_helpers.rb +7 -3
  80. data/lib/sequent/test/time_comparison.rb +12 -5
  81. data/lib/sequent/test.rb +2 -0
  82. data/lib/sequent/util/dry_run.rb +11 -8
  83. data/lib/sequent/util/printer.rb +6 -5
  84. data/lib/sequent/util/skip_if_already_processing.rb +3 -1
  85. data/lib/sequent/util/timer.rb +2 -0
  86. data/lib/sequent/util/util.rb +2 -0
  87. data/lib/sequent.rb +2 -0
  88. data/lib/version.rb +3 -1
  89. metadata +81 -66
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Core
3
5
  module Persistors
@@ -9,74 +11,74 @@ module Sequent
9
11
  module Persistor
10
12
  # Updates the view state
11
13
  def update_record
12
- fail "Method not supported in this persistor"
14
+ fail 'Method not supported in this persistor'
13
15
  end
14
16
 
15
17
  # Create a single record in the view state
16
18
  def create_record
17
- fail "Method not supported in this persistor"
19
+ fail 'Method not supported in this persistor'
18
20
  end
19
21
 
20
22
  # Creates multiple records at once in the view state
21
23
  def create_records
22
- fail "Method not supported in this persistor"
24
+ fail 'Method not supported in this persistor'
23
25
  end
24
26
 
25
27
  # Creates or updates a record in the view state.
26
28
  def create_or_update_record
27
- fail "Method not supported in this persistor"
29
+ fail 'Method not supported in this persistor'
28
30
  end
29
31
  # Gets a record from the view state, fails if it not exists
30
32
  def get_record!
31
- fail "Method not supported in this persistor"
33
+ fail 'Method not supported in this persistor'
32
34
  end
33
35
 
34
36
  # Gets a record from the view state, returns +nil+ if it not exists
35
37
  def get_record
36
- fail "Method not supported in this persistor"
38
+ fail 'Method not supported in this persistor'
37
39
  end
38
40
 
39
41
  # Deletes all records given a where
40
42
  def delete_all_records
41
- fail "Method not supported in this persistor"
43
+ fail 'Method not supported in this persistor'
42
44
  end
43
45
 
44
46
  # Updates all record given a where and an update clause
45
47
  def update_all_records
46
- fail "Method not supported in this persistor"
48
+ fail 'Method not supported in this persistor'
47
49
  end
48
50
 
49
51
  # Decide for yourself what to do with the records
50
52
  # @deprecated
51
53
  def do_with_records
52
- fail "Method not supported in this persistor"
54
+ fail 'Method not supported in this persistor'
53
55
  end
54
56
 
55
57
  # Decide for yourself what to do with a single record
56
58
  # @deprecated
57
59
  def do_with_record
58
- fail "Method not supported in this persistor"
60
+ fail 'Method not supported in this persistor'
59
61
  end
60
62
 
61
63
  # Delete a single record
62
64
  # @deprecated
63
65
  def delete_record
64
- fail "Method not supported in this persistor"
66
+ fail 'Method not supported in this persistor'
65
67
  end
66
68
 
67
69
  # Find records given a where
68
70
  def find_records
69
- fail "Method not supported in this persistor"
71
+ fail 'Method not supported in this persistor'
70
72
  end
71
73
 
72
74
  # Returns the last record given a where
73
75
  def last_record
74
- fail "Method not supported in this persistor"
76
+ fail 'Method not supported in this persistor'
75
77
  end
76
78
 
77
79
  # Hook to implement for instance the persistor batches statements
78
80
  def commit
79
- fail "Method not supported in this persistor"
81
+ fail 'Method not supported in this persistor'
80
82
  end
81
83
  end
82
84
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'persistor'
2
4
  require_relative 'active_record_persistor'
3
5
  require_relative 'replay_optimized_postgres_persistor'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
  require 'active_record'
3
5
  require 'csv'
@@ -13,9 +15,10 @@ module Sequent
13
15
  # using normal sql.
14
16
  #
15
17
  # Rebuilding the view state (or projection) of an aggregate typically consists
16
- # of an initial insert and then many updates and maybe a delete. With a normal Persistor (like ActiveRecordPersistor)
17
- # each action is executed to the database. This persitor creates an inmemory store first and finally flushes
18
- # the in memory store to the database. This can significantly reduces the amount of queries to the database.
18
+ # of an initial insert and then many updates and maybe a delete.
19
+ # With a normal Persistor (like ActiveRecordPersistor) each action is executed to the database.
20
+ # This persistor creates an in-memory store first and finally flushes
21
+ # the in-memory store to the database. This can significantly reduce the amount of queries to the database.
19
22
  # E.g. 1 insert, 6 updates is only a single insert using this Persistor.
20
23
  #
21
24
  # After lot of experimenting this turned out to be the fastest way to to bulk inserts in the database.
@@ -29,22 +32,26 @@ module Sequent
29
32
  #
30
33
  # class InvoiceProjector < Sequent::Core::Projector
31
34
  # on RecipientMovedEvent do |event|
32
- # update_all_records InvoiceRecord, recipient_id: event.recipient.aggregate_id do |record|
33
- # record.recipient_street = record.recipient.street
35
+ # update_all_records(
36
+ # InvoiceRecord,
37
+ # { aggregate_id: event.aggregate_id, recipient_id: event.recipient.aggregate_id },
38
+ # { recipient_street: event.recipient.street },
34
39
  # end
35
40
  # end
36
41
  # end
37
42
  #
38
- # In this case it is wise to create an index on InvoiceRecord on the recipient_id like you would in the database.
43
+ # In this case it is wise to create an index on InvoiceRecord on the aggregate_id and recipient_id
44
+ # like you would in the database.
39
45
  #
40
46
  # Example:
41
47
  #
42
48
  # ReplayOptimizedPostgresPersistor.new(
43
49
  # 50,
44
- # {InvoiceRecord => [[:recipient_id]]}
50
+ # {InvoiceRecord => [[:aggregate_id, :recipient_id]]}
45
51
  # )
46
52
  class ReplayOptimizedPostgresPersistor
47
53
  include Persistor
54
+ CHUNK_SIZE = 1024
48
55
 
49
56
  attr_reader :record_store
50
57
  attr_accessor :insert_with_csv_size
@@ -65,14 +72,18 @@ module Sequent
65
72
  class Index
66
73
  def initialize(indexed_columns)
67
74
  @indexed_columns = Hash.new do |hash, record_class|
68
- if record_class.column_names.include? 'aggregate_id'
69
- hash[record_class] = [:aggregate_id]
70
- else
71
- hash[record_class] = []
72
- end
75
+ hash[record_class] = if record_class.column_names.include? 'aggregate_id'
76
+ ['aggregate_id']
77
+ else
78
+ []
79
+ end
73
80
  end
74
81
 
75
- @indexed_columns.merge!(indexed_columns)
82
+ @indexed_columns = @indexed_columns.merge(
83
+ indexed_columns.reduce({}) do |memo, (key, ics)|
84
+ memo.merge({key => ics.map { |c| c.map(&:to_s) }})
85
+ end,
86
+ )
76
87
 
77
88
  @index = {}
78
89
  @reverse_index = {}
@@ -82,10 +93,10 @@ module Sequent
82
93
  return unless indexed?(record_class)
83
94
 
84
95
  get_keys(record_class, record).each do |key|
85
- @index[key.hash] = [] unless @index.has_key? key.hash
96
+ @index[key.hash] = [] unless @index.key? key.hash
86
97
  @index[key.hash] << record
87
98
 
88
- @reverse_index[record.object_id.hash] = [] unless @reverse_index.has_key? record.object_id.hash
99
+ @reverse_index[record.object_id.hash] = [] unless @reverse_index.key? record.object_id.hash
89
100
  @reverse_index[record.object_id.hash] << key.hash
90
101
  end
91
102
  end
@@ -112,7 +123,7 @@ module Sequent
112
123
  key = [record_class.name]
113
124
  get_index(record_class, where_clause).each do |field|
114
125
  key << field
115
- key << where_clause[field]
126
+ key << where_clause.stringify_keys[field]
116
127
  end
117
128
  @index[key.hash] || []
118
129
  end
@@ -123,13 +134,13 @@ module Sequent
123
134
  end
124
135
 
125
136
  def use_index?(record_class, where_clause)
126
- @indexed_columns.has_key?(record_class) && @indexed_columns[record_class].any? { |indexed_where| where_clause.keys.size == indexed_where.size && (where_clause.keys - indexed_where).empty? }
137
+ @indexed_columns.key?(record_class) && get_index(record_class, where_clause).present?
127
138
  end
128
139
 
129
140
  private
130
141
 
131
142
  def indexed?(record_class)
132
- @indexed_columns.has_key?(record_class)
143
+ @indexed_columns.key?(record_class)
133
144
  end
134
145
 
135
146
  def get_keys(record_class, record)
@@ -144,7 +155,9 @@ module Sequent
144
155
  end
145
156
 
146
157
  def get_index(record_class, where_clause)
147
- @indexed_columns[record_class].find { |indexed_where| where_clause.keys.size == indexed_where.size && (where_clause.keys - indexed_where).empty? }
158
+ @indexed_columns[record_class].find do |indexed_where|
159
+ where_clause.keys.size == indexed_where.size && (where_clause.keys.map(&:to_s) - indexed_where).empty?
160
+ end
148
161
  end
149
162
  end
150
163
 
@@ -152,37 +165,40 @@ module Sequent
152
165
  #
153
166
  # +indices+ Hash of indices to create in memory. Greatly speeds up the replaying.
154
167
  # Key corresponds to the name of the 'Record'
155
- # Values contains list of lists on which columns to index. E.g. [[:first_index_column], [:another_index, :with_to_columns]]
168
+ # Values contains list of lists on which columns to index.
169
+ # E.g. [[:first_index_column], [:another_index, :with_to_columns]]
156
170
  def initialize(insert_with_csv_size = 50, indices = {})
157
171
  @insert_with_csv_size = insert_with_csv_size
158
172
  @record_store = Hash.new { |h, k| h[k] = Set.new }
159
173
  @record_index = Index.new(indices)
160
174
  end
161
175
 
162
- def update_record(record_class, event, where_clause = {aggregate_id: event.aggregate_id}, options = {}, &block)
176
+ def update_record(record_class, event, where_clause = {aggregate_id: event.aggregate_id}, options = {})
163
177
  record = get_record!(record_class, where_clause)
164
178
  record.updated_at = event.created_at if record.respond_to?(:updated_at)
165
179
  yield record if block_given?
166
180
  @record_index.update(record_class, record)
167
- update_sequence_number = options.key?(:update_sequence_number) ?
168
- options[:update_sequence_number] :
181
+ update_sequence_number = if options.key?(:update_sequence_number)
182
+ options[:update_sequence_number]
183
+ else
169
184
  record.respond_to?(:sequence_number=)
185
+ end
170
186
  record.sequence_number = event.sequence_number if update_sequence_number
171
187
  end
172
188
 
173
189
  def create_record(record_class, values)
174
190
  column_names = record_class.column_names
175
191
  values = record_class.column_defaults.with_indifferent_access.merge(values)
176
- values.merge!(updated_at: values[:created_at]) if column_names.include?("updated_at")
177
- struct_class_name = "#{record_class.to_s}Struct"
178
- if self.class.struct_cache.has_key?(struct_class_name)
192
+ values.merge!(updated_at: values[:created_at]) if column_names.include?('updated_at')
193
+ struct_class_name = "#{record_class}Struct"
194
+ if self.class.struct_cache.key?(struct_class_name)
179
195
  struct_class = self.class.struct_cache[struct_class_name]
180
196
  else
181
197
  # We create a struct on the fly.
182
198
  # Since the replay happens in memory we implement the ==, eql? and hash methods
183
199
  # to point to the same object. A record is the same if and only if they point to
184
200
  # the same object. These methods are necessary since we use Set instead of [].
185
- class_def=<<-EOD
201
+ class_def = <<-EOD
186
202
  #{struct_class_name} = Struct.new(*#{column_names.map(&:to_sym)})
187
203
  class #{struct_class_name}
188
204
  include InitStruct
@@ -194,7 +210,9 @@ module Sequent
194
210
  end
195
211
  end
196
212
  EOD
197
- eval("#{class_def}")
213
+ # rubocop:disable Security/Eval
214
+ eval(class_def.to_s)
215
+ # rubocop:enable Security/Eval
198
216
  struct_class = ReplayOptimizedPostgresPersistor.const_get(struct_class_name)
199
217
  self.class.struct_cache[struct_class_name] = struct_class
200
218
  end
@@ -214,9 +232,7 @@ module Sequent
214
232
 
215
233
  def create_or_update_record(record_class, values, created_at = Time.now)
216
234
  record = get_record(record_class, values)
217
- unless record
218
- record = create_record(record_class, values.merge(created_at: created_at))
219
- end
235
+ record ||= create_record(record_class, values.merge(created_at: created_at))
220
236
  yield record if block_given?
221
237
  @record_index.update(record_class, record)
222
238
  record
@@ -224,7 +240,10 @@ module Sequent
224
240
 
225
241
  def get_record!(record_class, where_clause)
226
242
  record = get_record(record_class, where_clause)
227
- raise("record #{record_class} not found for #{where_clause}, store: #{@record_store[record_class]}") unless record
243
+ unless record
244
+ fail("record #{record_class} not found for #{where_clause}, store: #{@record_store[record_class]}")
245
+ end
246
+
228
247
  record
229
248
  end
230
249
 
@@ -273,10 +292,10 @@ module Sequent
273
292
  else
274
293
  @record_store[record_class].select do |record|
275
294
  where_clause.all? do |k, v|
276
- expected_value = v.kind_of?(Symbol) ? v.to_s : v
295
+ expected_value = v.is_a?(Symbol) ? v.to_s : v
277
296
  actual_value = record[k.to_sym]
278
- actual_value = actual_value.to_s if actual_value.kind_of? Symbol
279
- if expected_value.kind_of?(Array)
297
+ actual_value = actual_value.to_s if actual_value.is_a? Symbol
298
+ if expected_value.is_a?(Array)
280
299
  expected_value.include?(actual_value)
281
300
  else
282
301
  actual_value == expected_value
@@ -295,39 +314,38 @@ module Sequent
295
314
  @record_store.each do |clazz, records|
296
315
  @column_cache ||= {}
297
316
  @column_cache[clazz.name] ||= clazz.columns.reduce({}) do |hash, column|
298
- hash.merge({ column.name => column })
317
+ hash.merge({column.name => column})
299
318
  end
300
319
  if records.size > @insert_with_csv_size
301
- csv = CSV.new("")
302
- column_names = clazz.column_names.reject { |name| name == "id" }
320
+ csv = CSV.new(StringIO.new)
321
+ column_names = clazz.column_names.reject { |name| name == 'id' }
303
322
  records.each do |record|
304
323
  csv << column_names.map do |column_name|
305
324
  cast_value_to_column_type(clazz, column_name, record)
306
325
  end
307
326
  end
308
327
 
309
- buf = ''
310
328
  conn = Sequent::ApplicationRecord.connection.raw_connection
311
- copy_data = StringIO.new csv.string
329
+ copy_data = StringIO.new(csv.string)
312
330
  conn.transaction do
313
- conn.copy_data("COPY #{clazz.table_name} (#{column_names.join(",")}) FROM STDIN WITH csv") do
314
- while copy_data.read(1024, buf)
315
- conn.put_copy_data(buf)
331
+ conn.copy_data("COPY #{clazz.table_name} (#{column_names.join(',')}) FROM STDIN WITH csv") do
332
+ while (out = copy_data.read(CHUNK_SIZE))
333
+ conn.put_copy_data(out)
316
334
  end
317
335
  end
318
336
  end
319
337
  else
320
338
  clazz.unscoped do
321
339
  inserts = []
322
- column_names = clazz.column_names.reject { |name| name == "id" }
323
- prepared_values = (1..column_names.size).map { |i| "$#{i}" }.join(",")
340
+ column_names = clazz.column_names.reject { |name| name == 'id' }
341
+ prepared_values = (1..column_names.size).map { |i| "$#{i}" }.join(',')
324
342
  records.each do |record|
325
343
  values = column_names.map do |column_name|
326
344
  cast_value_to_column_type(clazz, column_name, record)
327
345
  end
328
346
  inserts << values
329
347
  end
330
- sql = %Q{insert into #{clazz.table_name} (#{column_names.join(",")}) values (#{prepared_values})}
348
+ sql = %{insert into #{clazz.table_name} (#{column_names.join(',')}) values (#{prepared_values})}
331
349
  inserts.each do |insert|
332
350
  clazz.connection.raw_connection.async_exec(sql, insert)
333
351
  end
@@ -346,7 +364,12 @@ module Sequent
346
364
  private
347
365
 
348
366
  def cast_value_to_column_type(clazz, column_name, record)
349
- Sequent::ApplicationRecord.connection.type_cast(record[column_name.to_sym], @column_cache[clazz.name][column_name])
367
+ uncasted_value = ActiveModel::Attribute.from_database(
368
+ column_name,
369
+ record[column_name.to_sym],
370
+ Sequent::ApplicationRecord.connection.lookup_cast_type_from_column(@column_cache[clazz.name][column_name]),
371
+ ).value_for_database
372
+ Sequent::ApplicationRecord.connection.type_cast(uncasted_value)
350
373
  end
351
374
  end
352
375
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'helpers/message_handler'
2
4
  require_relative './persistors/active_record_persistor'
3
5
 
4
6
  module Sequent
5
7
  module Core
6
-
7
8
  module Migratable
8
9
  module ClassMethods
9
10
  def manages_tables(*tables)
@@ -16,7 +17,7 @@ module Sequent
16
17
 
17
18
  def manages_no_tables
18
19
  @manages_no_tables = true
19
- manages_tables *[]
20
+ manages_tables
20
21
  end
21
22
 
22
23
  def manages_no_tables?
@@ -26,11 +27,11 @@ module Sequent
26
27
  private
27
28
 
28
29
  def managed_tables_from_superclass
29
- self.superclass.managed_tables if self.superclass.respond_to?(:managed_tables)
30
+ superclass.managed_tables if superclass.respond_to?(:managed_tables)
30
31
  end
31
32
 
32
33
  def manages_no_tables_from_superclass?
33
- self.superclass.manages_no_tables? if self.superclass.respond_to?(:manages_no_tables?)
34
+ superclass.manages_no_tables? if superclass.respond_to?(:manages_no_tables?)
34
35
  end
35
36
  end
36
37
 
@@ -53,7 +54,6 @@ module Sequent
53
54
  def managed_tables
54
55
  self.class.managed_tables
55
56
  end
56
-
57
57
  end
58
58
 
59
59
  # Projectors listen to events and update the view state as they see fit.
@@ -87,7 +87,6 @@ module Sequent
87
87
  include Helpers::MessageHandler
88
88
  include Migratable
89
89
 
90
-
91
90
  def initialize(persistor = Sequent::Core::Persistors::ActiveRecordPersistor.new)
92
91
  ensure_valid!
93
92
  @persistor = persistor
@@ -98,28 +97,32 @@ module Sequent
98
97
  end
99
98
 
100
99
  def_delegators :@persistor,
101
- :update_record,
102
- :create_record,
103
- :create_records,
104
- :create_or_update_record,
105
- :get_record!,
106
- :get_record,
107
- :delete_all_records,
108
- :update_all_records,
109
- :do_with_records,
110
- :do_with_record,
111
- :delete_record,
112
- :find_records,
113
- :last_record,
114
- :execute_sql,
115
- :commit
100
+ :update_record,
101
+ :create_record,
102
+ :create_records,
103
+ :create_or_update_record,
104
+ :get_record!,
105
+ :get_record,
106
+ :delete_all_records,
107
+ :update_all_records,
108
+ :do_with_records,
109
+ :do_with_record,
110
+ :delete_record,
111
+ :find_records,
112
+ :last_record,
113
+ :execute_sql,
114
+ :commit
116
115
 
117
116
  private
118
117
 
119
118
  def ensure_valid!
120
119
  return if self.class.manages_no_tables?
121
120
 
122
- fail "A Projector must manage at least one table. Did you forget to add `managed_tables` to #{self.class.name}?" if self.class.managed_tables.nil? || self.class.managed_tables.empty?
121
+ if self.class.managed_tables.nil? || self.class.managed_tables.empty?
122
+ fail <<~EOS.chomp
123
+ A Projector must manage at least one table. Did you forget to add `managed_tables` to #{self.class.name}?
124
+ EOS
125
+ end
123
126
  end
124
127
  end
125
128
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Core
3
5
  module RandomUuidGenerator
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oj'
2
4
 
3
5
  module Sequent
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record'
2
4
 
3
5
  module Sequent
@@ -14,8 +16,7 @@ module Sequent
14
16
  end
15
17
 
16
18
  class StreamRecord < Sequent::ApplicationRecord
17
-
18
- self.table_name = "stream_records"
19
+ self.table_name = 'stream_records'
19
20
 
20
21
  validates_presence_of :aggregate_type, :aggregate_id
21
22
  validates_numericality_of :snapshot_threshold, only_integer: true, greater_than: 0, allow_nil: true
@@ -23,7 +24,12 @@ module Sequent
23
24
  has_many :event_records
24
25
 
25
26
  def event_stream
26
- EventStream.new(aggregate_type: aggregate_type, aggregate_id: aggregate_id, snapshot_threshold: snapshot_threshold, stream_record_id: id)
27
+ EventStream.new(
28
+ aggregate_type: aggregate_type,
29
+ aggregate_id: aggregate_id,
30
+ snapshot_threshold: snapshot_threshold,
31
+ stream_record_id: id,
32
+ )
27
33
  end
28
34
 
29
35
  def event_stream=(data)
@@ -1,15 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Core
3
5
  module Transactions
4
-
5
6
  class ActiveRecordTransactionProvider
6
- def transactional
7
- Sequent::ApplicationRecord.transaction(requires_new: true) do
8
- yield
9
- end
10
- while(!after_commit_queue.empty?) do
11
- after_commit_queue.pop.call
12
- end
7
+ def transactional(&block)
8
+ Sequent::ApplicationRecord.transaction(requires_new: true, &block)
9
+ after_commit_queue.pop.call until after_commit_queue.empty?
13
10
  ensure
14
11
  clear_after_commit_queue
15
12
  end
@@ -28,7 +25,6 @@ module Sequent
28
25
  after_commit_queue.clear
29
26
  end
30
27
  end
31
-
32
28
  end
33
29
  end
34
30
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Core
3
5
  module Transactions
@@ -11,7 +13,6 @@ module Sequent
11
13
  yield
12
14
  end
13
15
  end
14
-
15
16
  end
16
17
  end
17
18
  end
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'active_record_transaction_provider'
2
4
  require_relative 'no_transactions'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_model'
2
4
  require_relative 'helpers/string_support'
3
5
  require_relative 'helpers/equal_support'
@@ -6,7 +8,6 @@ require_relative 'helpers/attribute_support'
6
8
  require_relative 'helpers/param_support'
7
9
 
8
10
  module Sequent
9
-
10
11
  module Core
11
12
  #
12
13
  # ValueObject is a container for data that belongs together but requires no identity
@@ -26,20 +27,17 @@ module Sequent
26
27
  #
27
28
  # This a deep clone of the address with the street attribute containing "New Street"
28
29
  class ValueObject
29
- include Sequent::Core::Helpers::StringSupport,
30
- Sequent::Core::Helpers::EqualSupport,
31
- Sequent::Core::Helpers::Copyable,
32
- Sequent::Core::Helpers::AttributeSupport,
33
- Sequent::Core::Helpers::ParamSupport,
34
- ActiveModel::Validations
30
+ include ActiveModel::Validations
31
+ include Sequent::Core::Helpers::ParamSupport
32
+ include Sequent::Core::Helpers::AttributeSupport
33
+ include Sequent::Core::Helpers::Copyable
34
+ include Sequent::Core::Helpers::EqualSupport
35
+ include Sequent::Core::Helpers::StringSupport
35
36
  include Sequent::Core::Helpers::TypeConversionSupport
36
37
 
37
38
  def initialize(args = {})
38
39
  update_all_attributes args
39
40
  end
40
-
41
41
  end
42
-
43
42
  end
44
43
  end
45
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'helpers/message_handler'
2
4
  require_relative 'current_event'
3
5
 
@@ -11,7 +13,7 @@ module Sequent
11
13
  begin
12
14
  old_event = CurrentEvent.current
13
15
  CurrentEvent.current = event
14
- self.instance_exec(event, &block)
16
+ instance_exec(event, &block)
15
17
  ensure
16
18
  CurrentEvent.current = old_event
17
19
  end
@@ -31,12 +33,12 @@ module Sequent
31
33
  # jobs processor is not using the same database connection
32
34
  # to enqueue jobs.
33
35
  def after_commit(ignore_errors: false, &block)
34
- Sequent.configuration.transaction_provider.after_commit &block
35
- rescue StandardError => error
36
+ Sequent.configuration.transaction_provider.after_commit(&block)
37
+ rescue StandardError => e
36
38
  if ignore_errors
37
- Sequent.logger.warn("An exception was raised in an after_commit hook: #{error}, #{error.inspect}")
39
+ Sequent.logger.warn("An exception was raised in an after_commit hook: #{e}, #{e.inspect}")
38
40
  else
39
- raise error
41
+ raise e
40
42
  end
41
43
  end
42
44
  end