sequent 3.3.1 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequent +31 -25
  3. data/lib/notices.rb +6 -0
  4. data/lib/sequent/application_record.rb +2 -0
  5. data/lib/sequent/configuration.rb +29 -29
  6. data/lib/sequent/core/aggregate_repository.rb +24 -14
  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 +30 -11
  12. data/lib/sequent/core/command_record.rb +12 -4
  13. data/lib/sequent/core/command_service.rb +41 -25
  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 +20 -15
  18. data/lib/sequent/core/event_record.rb +7 -7
  19. data/lib/sequent/core/event_store.rb +75 -49
  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 +64 -33
  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 -4
  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 +7 -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 +6 -0
  59. data/lib/sequent/generator/project.rb +3 -1
  60. data/lib/sequent/generator/template_project/Gemfile +1 -1
  61. data/lib/sequent/generator/template_project/spec/app/projectors/post_projector_spec.rb +1 -1
  62. data/lib/sequent/generator/template_project/spec/lib/post/post_command_handler_spec.rb +1 -1
  63. data/lib/sequent/generator.rb +3 -4
  64. data/lib/sequent/migrations/executor.rb +30 -9
  65. data/lib/sequent/migrations/functions.rb +5 -6
  66. data/lib/sequent/migrations/migrate_events.rb +12 -9
  67. data/lib/sequent/migrations/migrations.rb +2 -1
  68. data/lib/sequent/migrations/planner.rb +33 -23
  69. data/lib/sequent/migrations/projectors.rb +4 -3
  70. data/lib/sequent/migrations/sql.rb +2 -0
  71. data/lib/sequent/migrations/view_schema.rb +93 -44
  72. data/lib/sequent/rake/migration_tasks.rb +59 -23
  73. data/lib/sequent/rake/tasks.rb +5 -2
  74. data/lib/sequent/sequent.rb +6 -1
  75. data/lib/sequent/support/database.rb +39 -17
  76. data/lib/sequent/support/view_projection.rb +6 -3
  77. data/lib/sequent/support/view_schema.rb +2 -0
  78. data/lib/sequent/support.rb +2 -0
  79. data/lib/sequent/test/command_handler_helpers.rb +39 -17
  80. data/lib/sequent/test/event_handler_helpers.rb +10 -4
  81. data/lib/sequent/test/event_stream_helpers.rb +7 -3
  82. data/lib/sequent/test/time_comparison.rb +12 -5
  83. data/lib/sequent/test.rb +2 -0
  84. data/lib/sequent/util/dry_run.rb +194 -0
  85. data/lib/sequent/util/printer.rb +6 -5
  86. data/lib/sequent/util/skip_if_already_processing.rb +21 -5
  87. data/lib/sequent/util/timer.rb +2 -0
  88. data/lib/sequent/util/util.rb +3 -0
  89. data/lib/sequent.rb +4 -0
  90. data/lib/version.rb +3 -1
  91. metadata +110 -59
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Migrations
3
5
  class Planner
4
6
  Plan = Struct.new(:projectors, :migrations) do
5
7
  def replay_tables
6
- migrations.select { |m| m.class == ReplayTable }
8
+ migrations.select { |m| m.instance_of?(ReplayTable) }
7
9
  end
8
10
 
9
11
  def alter_tables
10
- migrations.select { |m| m.class == AlterTable }
12
+ migrations.select { |m| m.instance_of?(AlterTable) }
11
13
  end
12
14
 
13
15
  def empty?
@@ -28,7 +30,7 @@ module Sequent
28
30
  migrations.yield_self(&method(:select_projectors)),
29
31
  migrations
30
32
  .yield_self(&method(:create_migrations))
31
- .yield_self(&method(:remove_redundant_migrations))
33
+ .yield_self(&method(:remove_redundant_migrations)),
32
34
  )
33
35
  end
34
36
 
@@ -43,11 +45,11 @@ module Sequent
43
45
 
44
46
  def remove_redundant_migrations(migrations)
45
47
  redundant_migrations = migrations
46
- .yield_self(&method(:group_identical_migrations))
47
- .yield_self(&method(:select_redundant_migrations))
48
- .yield_self(&method(:remove_redundancy))
49
- .values
50
- .flatten
48
+ .yield_self(&method(:group_identical_migrations))
49
+ .yield_self(&method(:select_redundant_migrations))
50
+ .yield_self(&method(:remove_redundancy))
51
+ .values
52
+ .flatten
51
53
 
52
54
  (migrations - redundant_migrations)
53
55
  .yield_self(&method(:remove_alter_tables_before_replay_table))
@@ -64,21 +66,22 @@ module Sequent
64
66
 
65
67
  def remove_alter_tables_before_replay_table(migrations)
66
68
  migrations - migrations
67
- .each_with_index
68
- .select { |migration, _index| migration.class == AlterTable }
69
- .select { |migration, index| migrations
70
- .slice((index + 1)..-1)
71
- .find { |m| m.class == ReplayTable && m.record_class == migration.record_class }
72
- }.map(&:first)
69
+ .each_with_index
70
+ .select { |migration, _index| migration.instance_of?(AlterTable) }
71
+ .select do |migration, index|
72
+ migrations
73
+ .slice((index + 1)..-1)
74
+ .find { |m| m.instance_of?(ReplayTable) && m.record_class == migration.record_class }
75
+ end.map(&:first)
73
76
  end
74
77
 
75
78
  def remove_redundancy(grouped_migrations)
76
- grouped_migrations.reduce({}) { |memo, (key, ms)|
79
+ grouped_migrations.reduce({}) do |memo, (key, ms)|
77
80
  memo[key] = ms
78
- .yield_self(&method(:order_by_version_desc))
79
- .slice(1..-1)
81
+ .yield_self(&method(:order_by_version_desc))
82
+ .slice(1..-1)
80
83
  memo
81
- }
84
+ end
82
85
  end
83
86
 
84
87
  def order_by_version_desc(migrations)
@@ -95,13 +98,20 @@ module Sequent
95
98
  end
96
99
 
97
100
  def map_to_migrations(migrations)
98
- migrations.reduce({}) do |memo, (version, _migrations)|
99
- fail "Declared migrations for version #{version} must be an Array. For example: {'3' => [FooProjector]}" unless _migrations.is_a?(Array)
101
+ migrations.reduce({}) do |memo, (version, ms)|
102
+ unless ms.is_a?(Array)
103
+ fail "Declared migrations for version #{version} must be an Array. For example: {'3' => [FooProjector]}"
104
+ end
100
105
 
101
- memo[version] = _migrations.flat_map do |migration|
106
+ memo[version] = ms.flat_map do |migration|
102
107
  if migration.is_a?(AlterTable)
103
- alter_table_sql_file_name = "#{Sequent.configuration.migration_sql_files_directory}/#{migration.table_name}_#{version}.sql"
104
- fail "Missing file #{alter_table_sql_file_name} to apply for version #{version}" unless File.exist?(alter_table_sql_file_name)
108
+ alter_table_sql_file_name = <<~EOS.chomp
109
+ #{Sequent.configuration.migration_sql_files_directory}/#{migration.table_name}_#{version}.sql
110
+ EOS
111
+ unless File.exist?(alter_table_sql_file_name)
112
+ fail "Missing file #{alter_table_sql_file_name} to apply for version #{version}"
113
+ end
114
+
105
115
  migration.copy(version)
106
116
  elsif migration < Sequent::Projector
107
117
  migration.managed_tables.map { |table| ReplayTable.create(table, version) }
@@ -1,19 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'planner'
2
4
  module Sequent
3
5
  module Migrations
4
6
  class Projectors
5
7
  def self.versions
6
- fail "Define your own Sequent::Migrations::List class that extends this class and implements this method"
8
+ fail 'Define your own Sequent::Migrations::List class that extends this class and implements this method'
7
9
  end
8
10
 
9
11
  def self.version
10
- fail "Define your own Sequent::Migrations::List class that extends this class and implements this method"
12
+ fail 'Define your own Sequent::Migrations::List class that extends this class and implements this method'
11
13
  end
12
14
 
13
15
  def self.migrations_between(old, new)
14
16
  Planner.new(versions).plan(old, new)
15
17
  end
16
18
  end
17
-
18
19
  end
19
20
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../application_record'
2
4
 
3
5
  module Sequent
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'parallel'
2
4
  require 'postgresql_cursor'
3
5
 
@@ -24,11 +26,13 @@ module Sequent
24
26
  # - AlterTable (For instance if you introduce a new column)
25
27
  #
26
28
  # To maintain your migrations you need to:
27
- # 1. Create a class that extends `Sequent::Migrations::Projectors` and specify in `Sequent.configuration.migrations_class_name`
29
+ # 1. Create a class that extends `Sequent::Migrations::Projectors`
30
+ # and specify in `Sequent.configuration.migrations_class_name`
28
31
  # 2. Define per version which migrations you want to execute
29
32
  # See the definition of `Sequent::Migrations::Projectors.versions` and `Sequent::Migrations::Projectors.version`
30
33
  # 3. Specify in Sequent where your sql files reside (Sequent.configuration.migration_sql_files_directory)
31
- # 4. Ensure that you add %SUFFIX% to each name that needs to be unique in postgres (like TABLE names, INDEX names, PRIMARY KEYS)
34
+ # 4. Ensure that you add %SUFFIX% to each name that needs to be unique in postgres
35
+ # (like TABLE names, INDEX names, PRIMARY KEYS)
32
36
  # E.g. `create table foo%SUFFIX% (id serial NOT NULL, CONSTRAINT foo_pkey%SUFFIX% PRIMARY KEY (id))`
33
37
  # 5. If you want to run an `alter_table` migration ensure that
34
38
  # a sql file named `table_name_VERSION.sql` exists.
@@ -96,8 +100,17 @@ module Sequent
96
100
  create_view_schema_if_not_exists
97
101
  in_view_schema do
98
102
  Sequent::Core::Migratable.all.flat_map(&:managed_tables).each do |table|
99
- statements = sql_file_to_statements("#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}.sql") { |raw_sql| raw_sql.remove('%SUFFIX%') }
103
+ sql_file = "#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}.sql"
104
+ statements = sql_file_to_statements(sql_file) do |raw_sql|
105
+ raw_sql.remove('%SUFFIX%')
106
+ end
100
107
  statements.each { |statement| exec_sql(statement) }
108
+
109
+ indexes_file_name = "#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}.indexes.sql"
110
+ if File.exist?(indexes_file_name)
111
+ statements = sql_file_to_statements(indexes_file_name) { |raw_sql| raw_sql.remove('%SUFFIX%') }
112
+ statements.each(&method(:exec_sql))
113
+ end
101
114
  end
102
115
  end
103
116
  end
@@ -115,10 +128,14 @@ module Sequent
115
128
  #
116
129
  # This method is mainly useful during an initial setup of the view schema
117
130
  def create_view_schema_if_not_exists
118
- exec_sql(%Q{CREATE SCHEMA IF NOT EXISTS #{view_schema}})
131
+ exec_sql(%(CREATE SCHEMA IF NOT EXISTS #{view_schema}))
119
132
  in_view_schema do
120
- exec_sql(%Q{CREATE TABLE IF NOT EXISTS #{Versions.table_name} (version integer NOT NULL, CONSTRAINT version_pk PRIMARY KEY(version))})
121
- exec_sql(%Q{CREATE TABLE IF NOT EXISTS #{ReplayedIds.table_name} (event_id bigint NOT NULL, CONSTRAINT event_id_pk PRIMARY KEY(event_id))})
133
+ exec_sql(<<~SQL.chomp)
134
+ CREATE TABLE IF NOT EXISTS #{Versions.table_name} (version integer NOT NULL, CONSTRAINT version_pk PRIMARY KEY(version))
135
+ SQL
136
+ exec_sql(<<~SQL.chomp)
137
+ CREATE TABLE IF NOT EXISTS #{ReplayedIds.table_name} (event_id bigint NOT NULL, CONSTRAINT event_id_pk PRIMARY KEY(event_id))
138
+ SQL
122
139
  end
123
140
  end
124
141
 
@@ -156,10 +173,14 @@ module Sequent
156
173
  executor.execute_online(plan)
157
174
  end
158
175
 
159
- if plan.projectors.any?
160
- replay!(Sequent.configuration.online_replay_persistor_class.new)
176
+ replay!(Sequent.configuration.online_replay_persistor_class.new) if plan.projectors.any?
177
+
178
+ in_view_schema do
179
+ executor.create_indexes_after_execute_online(plan)
161
180
  end
181
+ # rubocop:disable Lint/RescueException
162
182
  rescue Exception => e
183
+ # rubocop:enable Lint/RescueException
163
184
  rollback_migration
164
185
  raise e
165
186
  end
@@ -191,7 +212,13 @@ module Sequent
191
212
  executor.set_table_names_to_new_version(plan)
192
213
 
193
214
  # 1 replay events not yet replayed
194
- replay!(Sequent.configuration.offline_replay_persistor_class.new, exclude_ids: true, group_exponent: 1) if plan.projectors.any?
215
+ if plan.projectors.any?
216
+ replay!(
217
+ Sequent.configuration.offline_replay_persistor_class.new,
218
+ exclude_ids: true,
219
+ group_exponent: 1,
220
+ )
221
+ end
195
222
 
196
223
  in_view_schema do
197
224
  Sequent::ApplicationRecord.transaction do
@@ -205,50 +232,57 @@ module Sequent
205
232
  truncate_replay_ids_table!
206
233
  end
207
234
  logger.info "Migrated to version #{Sequent.new_version}"
235
+ # rubocop:disable Lint/RescueException
208
236
  rescue Exception => e
237
+ # rubocop:enable Lint/RescueException
209
238
  rollback_migration
210
239
  raise e
211
240
  end
212
241
 
213
242
  private
214
243
 
215
-
216
244
  def ensure_version_correct!
217
245
  create_view_schema_if_not_exists
218
246
  new_version = Sequent.new_version
219
247
 
220
- fail ArgumentError.new("new_version [#{new_version}] must be greater or equal to current_version [#{current_version}]") if new_version < current_version
221
-
248
+ if new_version < current_version
249
+ fail ArgumentError,
250
+ "new_version [#{new_version}] must be greater or equal to current_version [#{current_version}]"
251
+ end
222
252
  end
223
253
 
224
254
  def replay!(replay_persistor, projectors: plan.projectors, exclude_ids: false, group_exponent: 3)
225
255
  logger.info "group_exponent: #{group_exponent.inspect}"
226
256
 
227
257
  with_sequent_config(replay_persistor, projectors) do
228
- logger.info "Start replaying events"
258
+ logger.info 'Start replaying events'
229
259
 
230
- time("#{16 ** group_exponent} groups replayed") do
260
+ time("#{16**group_exponent} groups replayed") do
231
261
  event_types = projectors.flat_map { |projector| projector.message_mapping.keys }.uniq.map(&:name)
232
262
  disconnect!
233
263
 
234
- number_of_groups = 16 ** group_exponent
264
+ number_of_groups = 16**group_exponent
235
265
  groups = groups_of_aggregate_id_prefixes(number_of_groups)
236
266
 
237
267
  @connected = false
238
268
  # using `map_with_index` because https://github.com/grosser/parallel/issues/175
239
- result = Parallel.map_with_index(groups, in_processes: Sequent.configuration.number_of_replay_processes) do |aggregate_prefixes, index|
240
- begin
241
- @connected ||= establish_connection
242
- time("Group (#{aggregate_prefixes.first}-#{aggregate_prefixes.last}) #{index + 1}/#{number_of_groups} replayed") do
243
- replay_events(aggregate_prefixes, event_types, exclude_ids, replay_persistor, &insert_ids)
244
- end
245
- nil
246
- rescue => e
247
- logger.error "Replaying failed for ids: ^#{aggregate_prefixes.first} - #{aggregate_prefixes.last}"
248
- logger.error "+++++++++++++++ ERROR +++++++++++++++"
249
- recursively_print(e)
250
- raise Parallel::Kill # immediately kill all sub-processes
269
+ result = Parallel.map_with_index(
270
+ groups,
271
+ in_processes: Sequent.configuration.number_of_replay_processes,
272
+ ) do |aggregate_prefixes, index|
273
+ @connected ||= establish_connection
274
+ msg = <<~EOS.chomp
275
+ Group (#{aggregate_prefixes.first}-#{aggregate_prefixes.last}) #{index + 1}/#{number_of_groups} replayed
276
+ EOS
277
+ time(msg) do
278
+ replay_events(aggregate_prefixes, event_types, exclude_ids, replay_persistor, &insert_ids)
251
279
  end
280
+ nil
281
+ rescue StandardError => e
282
+ logger.error "Replaying failed for ids: ^#{aggregate_prefixes.first} - #{aggregate_prefixes.last}"
283
+ logger.error '+++++++++++++++ ERROR +++++++++++++++'
284
+ recursively_print(e)
285
+ raise Parallel::Kill # immediately kill all sub-processes
252
286
  end
253
287
  establish_connection
254
288
  fail if result.nil?
@@ -260,7 +294,7 @@ module Sequent
260
294
  Sequent.configuration.event_store.replay_events_from_cursor(
261
295
  block_size: 1000,
262
296
  get_events: -> { event_stream(aggregate_prefixes, event_types, exclude_already_replayed) },
263
- on_progress: on_progress
297
+ on_progress: on_progress,
264
298
  )
265
299
 
266
300
  replay_persistor.commit
@@ -283,29 +317,31 @@ module Sequent
283
317
  end
284
318
 
285
319
  def groups_of_aggregate_id_prefixes(number_of_groups)
286
- all_prefixes = (0...16 ** LENGTH_OF_SUBSTRING_INDEX_ON_AGGREGATE_ID_IN_EVENT_STORE).to_a.map { |i| i.to_s(16) } # first x digits of hex
287
- all_prefixes = all_prefixes.map { |s| s.length == 3 ? s : "#{"0" * (3 - s.length)}#{s}" }
320
+ all_prefixes = (0...16**LENGTH_OF_SUBSTRING_INDEX_ON_AGGREGATE_ID_IN_EVENT_STORE).to_a.map do |i|
321
+ i.to_s(16)
322
+ end
323
+ all_prefixes = all_prefixes.map { |s| s.length == 3 ? s : "#{'0' * (3 - s.length)}#{s}" }
288
324
 
289
325
  logger.info "Number of groups #{number_of_groups}"
290
326
 
291
327
  logger.debug "Prefixes: #{all_prefixes.length}"
292
- fail "Can not have more groups #{number_of_groups} than number of prefixes #{all_prefixes.length}" if number_of_groups > all_prefixes.length
328
+ if number_of_groups > all_prefixes.length
329
+ fail "Can not have more groups #{number_of_groups} than number of prefixes #{all_prefixes.length}"
330
+ end
293
331
 
294
332
  all_prefixes.each_slice(all_prefixes.length / number_of_groups).to_a
295
333
  end
296
334
 
297
- def in_view_schema
298
- Sequent::Support::Database.with_schema_search_path(view_schema, db_config) do
299
- yield
300
- end
335
+ def in_view_schema(&block)
336
+ Sequent::Support::Database.with_schema_search_path(view_schema, db_config, &block)
301
337
  end
302
338
 
303
339
  def drop_old_tables(new_version)
304
340
  versions_to_check = (current_version - 10)..new_version
305
341
  old_tables = versions_to_check.flat_map do |old_version|
306
- exec_sql(
307
- "select table_name from information_schema.tables where table_schema = '#{Sequent.configuration.view_schema_name}' and table_name LIKE '%_#{old_version}'"
308
- ).flat_map { |row| row.values }
342
+ exec_sql(<<~SQL).flat_map(&:values)
343
+ select table_name from information_schema.tables where table_schema = '#{Sequent.configuration.view_schema_name}' and table_name LIKE '%_#{old_version}'
344
+ SQL
309
345
  end
310
346
  old_tables.each do |old_table|
311
347
  exec_sql("DROP TABLE #{Sequent.configuration.view_schema_name}.#{old_table} CASCADE")
@@ -314,7 +350,13 @@ module Sequent
314
350
 
315
351
  def insert_ids
316
352
  ->(progress, done, ids) do
317
- exec_sql("insert into #{ReplayedIds.table_name} (event_id) values #{ids.map { |id| "(#{id})" }.join(',')}") unless ids.empty?
353
+ unless ids.empty?
354
+ exec_sql(
355
+ "insert into #{ReplayedIds.table_name} (event_id) values #{ids.map do |id|
356
+ "(#{id})"
357
+ end.join(',')}",
358
+ )
359
+ end
318
360
  Sequent::Core::EventStore::PRINT_PROGRESS[progress, done, ids] if progress > 0
319
361
  end
320
362
  end
@@ -324,7 +366,9 @@ module Sequent
324
366
 
325
367
  config = Sequent.configuration.dup
326
368
 
327
- replay_projectors = projectors.map { |projector_class| projector_class.new(projector_class.replay_persistor || replay_persistor) }
369
+ replay_projectors = projectors.map do |projector_class|
370
+ projector_class.new(projector_class.replay_persistor || replay_persistor)
371
+ end
328
372
  config.transaction_provider = Sequent::Core::Transactions::NoTransactions.new
329
373
  config.event_handlers = replay_projectors
330
374
 
@@ -336,12 +380,17 @@ module Sequent
336
380
  end
337
381
 
338
382
  def event_stream(aggregate_prefixes, event_types, exclude_already_replayed)
339
- fail ArgumentError.new("aggregate_prefixes is mandatory") unless aggregate_prefixes.present?
383
+ fail ArgumentError, 'aggregate_prefixes is mandatory' unless aggregate_prefixes.present?
340
384
 
341
385
  event_stream = Sequent.configuration.event_record_class.where(event_type: event_types)
342
- event_stream = event_stream.where("substring(aggregate_id::varchar from 1 for #{LENGTH_OF_SUBSTRING_INDEX_ON_AGGREGATE_ID_IN_EVENT_STORE}) in (?)", aggregate_prefixes)
343
- event_stream = event_stream.where("NOT EXISTS (SELECT 1 FROM #{ReplayedIds.table_name} WHERE event_id = event_records.id)") if exclude_already_replayed
344
- event_stream = event_stream.where("event_records.created_at > ?", 1.day.ago) if exclude_already_replayed
386
+ event_stream = event_stream.where(<<~SQL, aggregate_prefixes)
387
+ substring(aggregate_id::varchar from 1 for #{LENGTH_OF_SUBSTRING_INDEX_ON_AGGREGATE_ID_IN_EVENT_STORE}) in (?)
388
+ SQL
389
+ if exclude_already_replayed
390
+ event_stream = event_stream
391
+ .where("NOT EXISTS (SELECT 1 FROM #{ReplayedIds.table_name} WHERE event_id = event_records.id)")
392
+ end
393
+ event_stream = event_stream.where('event_records.created_at > ?', 1.day.ago) if exclude_already_replayed
345
394
  event_stream.order('sequence_number ASC').select('id, event_type, event_json, sequence_number')
346
395
  end
347
396
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record'
2
4
  require 'rake'
3
5
  require 'rake/tasklib'
@@ -12,13 +14,14 @@ module Sequent
12
14
 
13
15
  def register_tasks!
14
16
  namespace :sequent do
15
- desc 'Rake task that runs before all sequent rake tasks. Hook applications can use to for instance run other rake tasks.'
17
+ desc <<~EOS
18
+ Rake task that runs before all sequent rake tasks. Hook applications can use to for instance run other rake tasks.
19
+ EOS
16
20
  task :init
17
21
 
18
22
  namespace :db do
19
-
20
23
  desc 'Creates the database and initializes the event_store schema for the current env'
21
- task :create => ['sequent:init'] do
24
+ task create: ['sequent:init'] do
22
25
  ensure_rack_env_set!
23
26
 
24
27
  db_config = Sequent::Support::Database.read_config(@env)
@@ -31,14 +34,18 @@ module Sequent
31
34
  task :drop, [:production] => ['sequent:init'] do |_t, args|
32
35
  ensure_rack_env_set!
33
36
 
34
- fail "Wont drop db in production unless you whitelist the environment as follows: rake sequent:db:drop[yes_drop_production]" if @env == 'production' && args[:production] != 'yes_drop_production'
37
+ if @env == 'production' && args[:production] != 'yes_drop_production'
38
+ fail <<~OES
39
+ Wont drop db in production unless you whitelist the environment as follows: rake sequent:db:drop[yes_drop_production]
40
+ OES
41
+ end
35
42
 
36
43
  db_config = Sequent::Support::Database.read_config(@env)
37
44
  Sequent::Support::Database.drop!(db_config)
38
45
  end
39
46
 
40
47
  desc 'Creates the view schema for the current env'
41
- task :create_view_schema => ['sequent:init'] do
48
+ task create_view_schema: ['sequent:init'] do
42
49
  ensure_rack_env_set!
43
50
 
44
51
  db_config = Sequent::Support::Database.read_config(@env)
@@ -47,33 +54,41 @@ module Sequent
47
54
  end
48
55
 
49
56
  desc 'Creates the event_store schema for the current env'
50
- task :create_event_store => ['sequent:init'] do
57
+ task create_event_store: ['sequent:init'] do
51
58
  ensure_rack_env_set!
52
59
  db_config = Sequent::Support::Database.read_config(@env)
53
60
  create_event_store(db_config)
54
61
  end
55
62
 
63
+ # rubocop:disable Lint/NestedMethodDefinition
56
64
  def create_event_store(db_config)
57
65
  event_store_schema = Sequent.configuration.event_store_schema_name
58
- sequent_schema = File.join(Sequent.configuration.database_config_directory, "#{event_store_schema}.rb")
59
- fail "File #{sequent_schema} does not exist. Check your Sequent configuration." unless File.exists?(sequent_schema)
66
+ sequent_schema = File.join(Sequent.configuration.database_schema_directory, "#{event_store_schema}.rb")
67
+ unless File.exist?(sequent_schema)
68
+ fail "File #{sequent_schema} does not exist. Check your Sequent configuration."
69
+ end
60
70
 
61
71
  Sequent::Support::Database.establish_connection(db_config)
62
- fail "Schema #{event_store_schema} already exists in the database" if Sequent::Support::Database.schema_exists?(event_store_schema)
72
+ if Sequent::Support::Database.schema_exists?(event_store_schema)
73
+ fail "Schema #{event_store_schema} already exists in the database"
74
+ end
63
75
 
64
76
  Sequent::Support::Database.create_schema(event_store_schema)
65
77
  Sequent::Support::Database.with_schema_search_path(event_store_schema, db_config, @env) do
66
78
  load(sequent_schema)
67
79
  end
68
80
  end
81
+ # rubocop:enable Lint/NestedMethodDefinition
69
82
  end
70
83
 
71
84
  namespace :migrate do
72
- desc 'Rake task that runs before all migrate rake tasks. Hook applications can use to for instance run other rake tasks.'
85
+ desc <<~EOS
86
+ Rake task that runs before all migrate rake tasks. Hook applications can use to for instance run other rake tasks.
87
+ EOS
73
88
  task :init
74
89
 
75
90
  desc 'Prints the current version in the database'
76
- task :current_version => ['sequent:init', :init] do
91
+ task current_version: ['sequent:init', :init] do
77
92
  ensure_rack_env_set!
78
93
 
79
94
  Sequent::Support::Database.connect!(@env)
@@ -81,8 +96,10 @@ module Sequent
81
96
  puts "Current version in the database is: #{Sequent::Migrations::ViewSchema::Versions.maximum(:version)}"
82
97
  end
83
98
 
84
- desc 'Migrates the Projectors while the app is running. Call +sequent:migrate:offline+ after this successfully completed.'
85
- task :online => ['sequent:init', :init] do
99
+ desc <<~EOS
100
+ Migrates the Projectors while the app is running. Call +sequent:migrate:offline+ after this successfully completed.
101
+ EOS
102
+ task online: ['sequent:init', :init] do
86
103
  ensure_rack_env_set!
87
104
 
88
105
  db_config = Sequent::Support::Database.read_config(@env)
@@ -91,8 +108,10 @@ module Sequent
91
108
  view_schema.migrate_online
92
109
  end
93
110
 
94
- desc 'Migrates the events inserted while +online+ was running. It is expected +sequent:migrate:online+ ran first.'
95
- task :offline => ['sequent:init', :init] do
111
+ desc <<~EOS
112
+ Migrates the events inserted while +online+ was running. It is expected +sequent:migrate:online+ ran first.
113
+ EOS
114
+ task offline: ['sequent:init', :init] do
96
115
  ensure_rack_env_set!
97
116
 
98
117
  db_config = Sequent::Support::Database.read_config(@env)
@@ -103,21 +122,35 @@ module Sequent
103
122
  end
104
123
 
105
124
  namespace :snapshots do
106
- desc 'Rake task that runs before all snapshots rake tasks. Hook applications can use to for instance run other rake tasks.'
125
+ desc <<~EOS
126
+ Rake task that runs before all snapshots rake tasks. Hook applications can use to for instance run other rake tasks.
127
+ EOS
107
128
  task :init
108
129
 
109
- task :set_snapshot_threshold, [:aggregate_type,:threshold] => ['sequent:init', :init] do
130
+ task :set_snapshot_threshold, %i[aggregate_type threshold] => ['sequent:init', :init] do |_t, args|
110
131
  aggregate_type = args['aggregate_type']
111
132
  threshold = args['threshold']
112
133
 
113
- fail ArgumentError.new('usage rake sequent:snapshots:set_snapshot_threshold[AggregegateType,threshold]') unless aggregate_type
114
- fail ArgumentError.new('usage rake sequent:snapshots:set_snapshot_threshold[AggregegateType,threshold]') unless threshold
134
+ unless aggregate_type
135
+ fail ArgumentError,
136
+ 'usage rake sequent:snapshots:set_snapshot_threshold[AggregegateType,threshold]'
137
+ end
138
+ unless threshold
139
+ fail ArgumentError,
140
+ 'usage rake sequent:snapshots:set_snapshot_threshold[AggregegateType,threshold]'
141
+ end
115
142
 
116
- execute "UPDATE #{Sequent.configuration.stream_record_class} SET snapshot_threshold = #{threshold.to_i} WHERE aggregate_type = '#{aggregate_type}'"
143
+ execute <<~EOS
144
+ UPDATE #{Sequent.configuration.stream_record_class} SET snapshot_threshold = #{threshold.to_i} WHERE aggregate_type = '#{aggregate_type}'
145
+ EOS
117
146
  end
118
147
 
119
- task :delete_all => ['sequent:init', :init] do
120
- result = Sequent::ApplicationRecord.connection.execute("DELETE FROM #{Sequent.configuration.event_record_class.table_name} WHERE event_type = 'Sequent::Core::SnapshotEvent'")
148
+ task delete_all: ['sequent:init', :init] do
149
+ result = Sequent::ApplicationRecord
150
+ .connection
151
+ .execute(<<~EOS)
152
+ DELETE FROM #{Sequent.configuration.event_record_class.table_name} WHERE event_type = 'Sequent::Core::SnapshotEvent'
153
+ EOS
121
154
  Sequent.logger.info "Deleted #{result.cmd_tuples} aggregate snapshots from the event store"
122
155
  end
123
156
  end
@@ -125,9 +158,12 @@ module Sequent
125
158
  end
126
159
 
127
160
  private
161
+
162
+ # rubocop:disable Naming/MemoizedInstanceVariableName
128
163
  def ensure_rack_env_set!
129
- @env ||= ENV['RACK_ENV'] || fail("RACK_ENV not set")
164
+ @env ||= ENV['RACK_ENV'] || fail('RACK_ENV not set')
130
165
  end
166
+ # rubocop:enable Naming/MemoizedInstanceVariableName
131
167
  end
132
168
  end
133
169
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record'
2
4
  require 'rake'
3
5
  require 'rake/tasklib'
@@ -11,12 +13,13 @@ module Sequent
11
13
 
12
14
  DEFAULT_OPTIONS = {
13
15
  migrations_path: 'db/migrate',
14
- event_store_schema: 'public'
15
- }
16
+ event_store_schema: 'public',
17
+ }.freeze
16
18
 
17
19
  attr_reader :options
18
20
 
19
21
  def initialize(options)
22
+ super()
20
23
  @options = DEFAULT_OPTIONS.merge(options)
21
24
  end
22
25
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'configuration'
2
4
  require_relative 'core/event'
3
5
  require_relative 'core/command'
@@ -6,7 +8,6 @@ require_relative 'core/aggregate_root'
6
8
  require_relative 'core/projector'
7
9
  require_relative 'core/workflow'
8
10
  require_relative 'core/value_object'
9
- require_relative 'generator'
10
11
  require_relative 'migrations/migrations'
11
12
 
12
13
  module Sequent
@@ -65,6 +66,10 @@ module Sequent
65
66
  configuration.aggregate_repository
66
67
  end
67
68
 
69
+ def self.dry_run(*commands)
70
+ Sequent::Util::DryRun.these_commands(commands)
71
+ end
72
+
68
73
  # Shortcut classes for easy usage
69
74
  Event = Sequent::Core::Event
70
75
  Command = Sequent::Core::Command