sequent 4.0.0 → 4.3.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 +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'