sequent 4.0.0 → 4.3.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 +33 -26
  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 +48 -13
  7. data/lib/sequent/core/aggregate_root.rb +36 -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 +89 -51
  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 +53 -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 +30 -9
  51. data/lib/sequent/core/transactions/no_transactions.rb +2 -1
  52. data/lib/sequent/core/transactions/read_only_active_record_transaction_provider.rb +46 -0
  53. data/lib/sequent/core/transactions/transactions.rb +3 -0
  54. data/lib/sequent/core/value_object.rb +8 -10
  55. data/lib/sequent/core/workflow.rb +35 -5
  56. data/lib/sequent/generator/aggregate.rb +16 -10
  57. data/lib/sequent/generator/command.rb +26 -19
  58. data/lib/sequent/generator/event.rb +19 -17
  59. data/lib/sequent/generator/generator.rb +2 -0
  60. data/lib/sequent/generator/project.rb +9 -0
  61. data/lib/sequent/generator/template_project/Gemfile +1 -1
  62. data/lib/sequent/generator/template_project/ruby-version +1 -0
  63. data/lib/sequent/generator.rb +2 -0
  64. data/lib/sequent/migrations/executor.rb +22 -13
  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 +84 -45
  72. data/lib/sequent/rake/migration_tasks.rb +58 -22
  73. data/lib/sequent/rake/tasks.rb +5 -2
  74. data/lib/sequent/sequent.rb +2 -0
  75. data/lib/sequent/support/database.rb +30 -15
  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 +35 -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 +28 -20
  85. data/lib/sequent/util/printer.rb +6 -5
  86. data/lib/sequent/util/skip_if_already_processing.rb +3 -1
  87. data/lib/sequent/util/timer.rb +2 -0
  88. data/lib/sequent/util/util.rb +2 -0
  89. data/lib/sequent.rb +2 -0
  90. data/lib/version.rb +3 -1
  91. metadata +84 -67
@@ -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
66
  sequent_schema = File.join(Sequent.configuration.database_schema_directory, "#{event_store_schema}.rb")
59
- fail "File #{sequent_schema} does not exist. Check your Sequent configuration." unless File.exists?(sequent_schema)
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'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/hash_with_indifferent_access'
2
4
 
3
5
  module Sequent
@@ -16,20 +18,25 @@ module Sequent
16
18
  end
17
19
 
18
20
  def self.read_config(env)
19
- fail ArgumentError.new("env is mandatory") unless env
21
+ fail ArgumentError, 'env is mandatory' unless env
20
22
 
21
23
  database_yml = File.join(Sequent.configuration.database_config_directory, 'database.yml')
22
- YAML.load(ERB.new(File.read(database_yml)).result)[env]
24
+ config = YAML.safe_load(ERB.new(File.read(database_yml)).result, aliases: true)[env]
25
+ if Gem.loaded_specs['activerecord'].version >= Gem::Version.create('6.1.0')
26
+ ActiveRecord::Base.configurations.resolve(config).configuration_hash.with_indifferent_access
27
+ else
28
+ ActiveRecord::Base.resolve_config_for_connection(config)
29
+ end
23
30
  end
24
31
 
25
32
  def self.create!(db_config)
26
- ActiveRecord::Base.establish_connection(db_config.merge('database' => 'postgres'))
27
- ActiveRecord::Base.connection.create_database(db_config['database'])
33
+ ActiveRecord::Base.establish_connection(db_config.merge(database: 'postgres'))
34
+ ActiveRecord::Base.connection.create_database(db_config[:database])
28
35
  end
29
36
 
30
37
  def self.drop!(db_config)
31
- ActiveRecord::Base.establish_connection(db_config.merge('database' => 'postgres'))
32
- ActiveRecord::Base.connection.drop_database(db_config['database'])
38
+ ActiveRecord::Base.establish_connection(db_config.merge(database: 'postgres'))
39
+ ActiveRecord::Base.connection.drop_database(db_config[:database])
33
40
  end
34
41
 
35
42
  def self.establish_connection(db_config)
@@ -46,9 +53,8 @@ module Sequent
46
53
 
47
54
  def self.create_schema(schema)
48
55
  sql = "CREATE SCHEMA IF NOT EXISTS #{schema}"
49
- if user = ActiveRecord::Base.connection_config[:username]
50
- sql += %Q{ AUTHORIZATION "#{user}"}
51
- end
56
+ user = configuration_hash[:username]
57
+ sql += %( AUTHORIZATION "#{user}") if user
52
58
  execute_sql(sql)
53
59
  end
54
60
 
@@ -57,32 +63,41 @@ module Sequent
57
63
  end
58
64
 
59
65
  def self.with_schema_search_path(search_path, db_config, env = ENV['RACK_ENV'])
60
- fail ArgumentError.new("env is required") unless env
66
+ fail ArgumentError, 'env is required' unless env
61
67
 
62
68
  disconnect!
63
- original_search_paths = db_config['schema_search_path'].dup
69
+ original_search_paths = db_config[:schema_search_path].dup
64
70
 
65
71
  if ActiveRecord::VERSION::MAJOR < 6
66
- ActiveRecord::Base.configurations[env.to_s] = ActiveSupport::HashWithIndifferentAccess.new(db_config).stringify_keys
72
+ ActiveRecord::Base.configurations[env.to_s] =
73
+ ActiveSupport::HashWithIndifferentAccess.new(db_config).stringify_keys
67
74
  end
68
75
 
69
- db_config['schema_search_path'] = search_path
76
+ db_config[:schema_search_path] = search_path
70
77
 
71
78
  ActiveRecord::Base.establish_connection db_config
72
79
 
73
80
  yield
74
81
  ensure
75
82
  disconnect!
76
- db_config['schema_search_path'] = original_search_paths
83
+ db_config[:schema_search_path] = original_search_paths
77
84
  establish_connection(db_config)
78
85
  end
79
86
 
80
87
  def self.schema_exists?(schema)
81
88
  ActiveRecord::Base.connection.execute(
82
- "SELECT schema_name FROM information_schema.schemata WHERE schema_name like '#{schema}'"
89
+ "SELECT schema_name FROM information_schema.schemata WHERE schema_name like '#{schema}'",
83
90
  ).count == 1
84
91
  end
85
92
 
93
+ def self.configuration_hash
94
+ if Gem.loaded_specs['activesupport'].version >= Gem::Version.create('6.1.0')
95
+ ActiveRecord::Base.connection_db_config.configuration_hash
96
+ else
97
+ ActiveRecord::Base.connection_config
98
+ end
99
+ end
100
+
86
101
  def schema_exists?(schema)
87
102
  self.class.schema_exists?(schema)
88
103
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'postgresql_cursor'
2
4
 
3
5
  module Sequent
4
6
  module Support
5
7
  class ViewProjection
6
8
  attr_reader :name, :version, :schema_definition
9
+
7
10
  def initialize(options)
8
11
  @name = options.fetch(:name)
9
12
  @version = options.fetch(:version)
@@ -20,7 +23,7 @@ module Sequent
20
23
  ordering = Events::ORDERED_BY_STREAM
21
24
  event_store.replay_events_from_cursor(
22
25
  block_size: 10_000,
23
- get_events: ->() { ordering[event_store] }
26
+ get_events: -> { ordering[event_store] },
24
27
  )
25
28
  end
26
29
  end
@@ -42,13 +45,13 @@ module Sequent
42
45
  module Events
43
46
  extend ActiveRecord::ConnectionAdapters::Quoting
44
47
 
45
- ORDERED_BY_STREAM = lambda do |event_store|
48
+ ORDERED_BY_STREAM = ->(_event_store) do
46
49
  event_records = quote_table_name(Sequent.configuration.event_record_class.table_name)
47
50
  stream_records = quote_table_name(Sequent.configuration.stream_record_class.table_name)
48
51
  snapshot_event_type = quote(Sequent.configuration.snapshot_event_class)
49
52
 
50
53
  Sequent.configuration.event_record_class
51
- .select("event_type, event_json")
54
+ .select('event_type, event_json')
52
55
  .joins("INNER JOIN #{stream_records} ON #{event_records}.stream_record_id = #{stream_records}.id")
53
56
  .where("event_type <> #{snapshot_event_type}")
54
57
  .order!("#{stream_records}.id, #{event_records}.sequence_number")
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Support
3
5
  class ViewSchema < ActiveRecord::Schema
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'support/database'
2
4
  require_relative 'support/view_projection'
3
5
  require_relative 'support/view_schema'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thread_safe'
2
4
  require 'sequent/core/event_store'
3
5
 
@@ -36,7 +38,6 @@ module Sequent
36
38
  #
37
39
  # end
38
40
  module CommandHandlerHelpers
39
-
40
41
  class FakeEventStore
41
42
  extend Forwardable
42
43
 
@@ -89,7 +90,7 @@ module Sequent
89
90
  end
90
91
 
91
92
  def stream_exists?(aggregate_id)
92
- @event_streams.has_key?(aggregate_id)
93
+ @event_streams.key?(aggregate_id)
93
94
  end
94
95
 
95
96
  def events_exists?(aggregate_id)
@@ -99,15 +100,21 @@ module Sequent
99
100
  private
100
101
 
101
102
  def to_event_streams(events)
102
- # Specs use a simple list of given events. We need a mapping from StreamRecord to the associated events for the event store.
103
+ # Specs use a simple list of given events.
104
+ # We need a mapping from StreamRecord to the associated events for the event store.
103
105
  streams_by_aggregate_id = {}
104
106
  events.map do |event|
105
107
  event_stream = streams_by_aggregate_id.fetch(event.aggregate_id) do |aggregate_id|
106
108
  streams_by_aggregate_id[aggregate_id] =
107
109
  find_event_stream(aggregate_id) ||
108
110
  begin
109
- aggregate_type = FakeEventStore.aggregate_type_for_event(event)
110
- raise "cannot find aggregate type associated with creation event #{event}, did you include an event handler in your aggregate for this event?" unless aggregate_type
111
+ aggregate_type = aggregate_type_for_event(event)
112
+ unless aggregate_type
113
+ fail <<~EOS
114
+ Cannot find aggregate type associated with creation event #{event}, did you include an event handler in your aggregate for this event?
115
+ EOS
116
+ end
117
+
111
118
  Sequent::Core::EventStream.new(aggregate_type: aggregate_type.name, aggregate_id: aggregate_id)
112
119
  end
113
120
  end
@@ -115,10 +122,10 @@ module Sequent
115
122
  end
116
123
  end
117
124
 
118
- def self.aggregate_type_for_event(event)
125
+ def aggregate_type_for_event(event)
119
126
  @event_to_aggregate_type ||= ThreadSafe::Cache.new
120
127
  @event_to_aggregate_type.fetch_or_store(event.class) do |klass|
121
- Sequent::Core::AggregateRoot.descendants.find { |x| x.message_mapping.has_key?(klass) }
128
+ Sequent::Core::AggregateRoots.all.find { |x| x.message_mapping.key?(klass) }
122
129
  end
123
130
  end
124
131
 
@@ -133,30 +140,41 @@ module Sequent
133
140
  end
134
141
  end
135
142
 
136
- def given_events *events
143
+ def given_events(*events)
137
144
  Sequent.configuration.event_store.given_events(events.flatten(1))
138
145
  end
139
146
 
140
- def when_command command
147
+ def when_command(command)
141
148
  Sequent.configuration.command_service.execute_commands command
142
149
  end
143
150
 
144
151
  def then_events(*expected_events)
145
- expected_classes = expected_events.flatten(1).map { |event| event.class == Class ? event : event.class }
152
+ expected_classes = expected_events.flatten(1).map { |event| event.instance_of?(Class) ? event : event.class }
146
153
  expect(Sequent.configuration.event_store.stored_events.map(&:class)).to eq(expected_classes)
147
154
 
148
- Sequent.configuration.event_store.stored_events.zip(expected_events.flatten(1)).each_with_index do |(actual, expected), index|
149
- next if expected.class == Class
150
- _actual = Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(actual.payload))
151
- _expected = Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(expected.payload))
152
- expect(_actual).to eq(_expected), "#{index+1}th Event of type #{actual.class} not equal\nexpected: #{_expected.inspect}\n got: #{_actual.inspect}" if expected
153
- end
155
+ Sequent
156
+ .configuration
157
+ .event_store
158
+ .stored_events
159
+ .zip(expected_events.flatten(1))
160
+ .each_with_index do |(actual, expected), index|
161
+ next if expected.instance_of?(Class)
162
+
163
+ actual_hash = Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(actual.payload))
164
+ expected_hash = Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(expected.payload))
165
+ next unless expected
166
+
167
+ # rubocop:disable Layout/LineLength
168
+ expect(actual_hash)
169
+ .to eq(expected_hash),
170
+ "#{index + 1}th Event of type #{actual.class} not equal\nexpected: #{expected_hash.inspect}\n got: #{actual_hash.inspect}"
171
+ # rubocop:enable Layout/LineLength
172
+ end
154
173
  end
155
174
 
156
175
  def then_no_events
157
176
  then_events
158
177
  end
159
-
160
178
  end
161
179
  end
162
180
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Test
3
5
  ##
@@ -26,7 +28,6 @@ module Sequent
26
28
  # end
27
29
  # end
28
30
  module WorkflowHelpers
29
-
30
31
  class FakeTransactionProvider
31
32
  def initialize
32
33
  @after_commit_blocks = []
@@ -55,12 +56,17 @@ module Sequent
55
56
  end
56
57
 
57
58
  def then_events(*expected_events)
58
- expected_classes = expected_events.flatten(1).map { |event| event.class == Class ? event : event.class }
59
+ expected_classes = expected_events.flatten(1).map { |event| event.instance_of?(Class) ? event : event.class }
59
60
  expect(Sequent.configuration.event_store.stored_events.map(&:class)).to eq(expected_classes)
60
61
 
61
62
  Sequent.configuration.event_store.stored_events.zip(expected_events.flatten(1)).each do |actual, expected|
62
- next if expected.class == Class
63
- expect(Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(actual.payload))).to eq(Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(expected.payload))) if expected
63
+ next if expected.instance_of?(Class)
64
+
65
+ next unless expected
66
+
67
+ expect(
68
+ Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(actual.payload)),
69
+ ).to eq(Sequent::Core::Oj.strict_load(Sequent::Core::Oj.dump(expected.payload)))
64
70
  end
65
71
  end
66
72
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Test
3
5
  ##
@@ -41,12 +43,14 @@ module Sequent
41
43
  @events = []
42
44
  end
43
45
 
46
+ # rubocop:disable Style/MissingRespondToMissing
44
47
  def method_missing(name, *args, &block)
45
48
  args = prepare_arguments(args)
46
49
  @events << FactoryBot.build(name, *args, &block)
47
50
  end
51
+ # rubocop:enable Style/MissingRespondToMissing
48
52
 
49
- private
53
+ private
50
54
 
51
55
  def prepare_arguments(args)
52
56
  options = args.last.is_a?(Hash) ? args.pop : {}
@@ -68,10 +72,10 @@ module Sequent
68
72
  given_events(*event_stream(aggregate_id: aggregate_id, &block))
69
73
  end
70
74
 
71
- def self.included(spec)
75
+ def self.included(_spec)
72
76
  require 'factory_bot'
73
77
  rescue LoadError
74
- raise ArgumentError, "Factory bot is required to use the event stream helpers"
78
+ raise ArgumentError, 'Factory bot is required to use the event stream helpers'
75
79
  end
76
80
  end
77
81
  end
@@ -1,20 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Test
3
5
  module DateTimePatches
4
6
  module Normalize
5
7
  def normalize
6
- in_time_zone("UTC")
8
+ in_time_zone('UTC')
7
9
  end
8
10
  end
9
11
 
10
12
  module Compare
11
- alias_method :'___<=>', :<=>
13
+ # rubocop:disable Style/Alias
14
+ alias :'___<=>' :'<=>'
15
+ # rubocop:enable Style/Alias
12
16
 
13
17
  # omit nsec in datetime comparisons
14
18
  def <=>(other)
15
- if other && other.is_a?(DateTimePatches::Normalize)
19
+ if other&.is_a?(DateTimePatches::Normalize)
16
20
  result = normalize.iso8601 <=> other.normalize.iso8601
17
21
  return result unless result == 0
22
+
18
23
  # use usec here, which *truncates* the nsec (ie. like Postgres)
19
24
  return normalize.usec <=> other.normalize.usec
20
25
  end
@@ -35,6 +40,8 @@ class DateTime
35
40
  prepend Sequent::Test::DateTimePatches::Compare
36
41
  end
37
42
 
38
- class ActiveSupport::TimeWithZone
39
- prepend Sequent::Test::DateTimePatches::Normalize
43
+ module ActiveSupport
44
+ class TimeWithZone
45
+ prepend Sequent::Test::DateTimePatches::Normalize
46
+ end
40
47
  end
data/lib/sequent/test.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'sequent'
2
4
  require_relative 'test/command_handler_helpers'
3
5
  require_relative 'test/event_stream_helpers'