sequent 3.3.1 → 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 (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