sequent 4.0.0 → 4.1.0

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.
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