sequent 6.0.1 → 7.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e420a9289f17d8552364afd49977cdb8cdbc0b37f39445a0a7ce310f9c1a180f
4
- data.tar.gz: 116144ee36558de52bc381b8e99e13d44aad3bedda6d531b380d93b3d7b96445
3
+ metadata.gz: 2271f62c7221e3eb21eb7707ad2f7df52b9adeae3154c0856b860133af872457
4
+ data.tar.gz: dc3b99bc7c06957f98f5c45be5cff4082ae0d9cca493a9ea3431fc14eea67e08
5
5
  SHA512:
6
- metadata.gz: 434eb99e05dda79479a8c8a6529a0d2b3bfa2fc3e02c7143ad314f27a2140d33c6a01f1d3d1a147892b1dafefcd899bed8bde9cbe3b0c76520f3561b5dfa2b05
7
- data.tar.gz: 6fd8a9df8cac32aaad084fd2b472caab478c13840da563b8d3a8e8c9e1d86c32bfc3d43a7e8d7031386a36a127c9be6648d3baaa241dcfb45d92f3d7721a9d4d
6
+ metadata.gz: 150cff9d3a13c1b230dfb71f6897f740c1c4c5ac3448dbb0fd334858229ae1bf3ff67e1bdad897145e5bc0a2a5cd70a31a430e93939857ba753f2f9895a84fa1
7
+ data.tar.gz: ae8ae17080b36ed4b60f361aefb3d9b816b12b037f9eb4022c00969c2b9ce2c78cdfe6f4c67358d02c376ec37479d5951ad10e92944ba4b3e6b056b3fd8c2b5e
@@ -36,6 +36,7 @@ module Sequent
36
36
 
37
37
  attr_accessor :aggregate_repository,
38
38
  :event_store,
39
+ :event_store_cache_event_types,
39
40
  :command_service,
40
41
  :event_record_class,
41
42
  :stream_record_class,
@@ -63,7 +64,8 @@ module Sequent
63
64
  :enable_multiple_database_support,
64
65
  :primary_database_role,
65
66
  :primary_database_key,
66
- :time_precision
67
+ :time_precision,
68
+ :enable_autoregistration
67
69
 
68
70
  attr_reader :migrations_class_name,
69
71
  :versions_table_name,
@@ -73,10 +75,13 @@ module Sequent
73
75
  @instance ||= new
74
76
  end
75
77
 
78
+ # Create a new instance of Configuration
76
79
  def self.reset
77
80
  @instance = new
78
81
  end
79
82
 
83
+ # Restore the given Configuration
84
+ # @param configuration [Sequent::Configuration]
80
85
  def self.restore(configuration)
81
86
  @instance = configuration
82
87
  end
@@ -88,6 +93,7 @@ module Sequent
88
93
  self.command_middleware = Sequent::Core::Middleware::Chain.new
89
94
 
90
95
  self.aggregate_repository = Sequent::Core::AggregateRepository.new
96
+ self.event_store_cache_event_types = true
91
97
  self.event_store = Sequent::Core::EventStore.new
92
98
  self.command_service = Sequent::Core::CommandService.new
93
99
  self.event_record_class = Sequent::Core::EventRecord
@@ -121,6 +127,8 @@ module Sequent
121
127
  self.primary_database_key = :primary
122
128
 
123
129
  self.time_precision = DEFAULT_TIME_PRECISION
130
+
131
+ self.enable_autoregistration = false
124
132
  end
125
133
 
126
134
  def can_use_multiple_databases?
@@ -149,5 +157,53 @@ module Sequent
149
157
 
150
158
  @migrations_class_name = class_name
151
159
  end
160
+
161
+ def autoregister!
162
+ return unless enable_autoregistration
163
+
164
+ # Only autoregister the AggregateSnapshotter if the autoregistration is enabled
165
+ Sequent::Core::AggregateSnapshotter.skip_autoregister = false
166
+
167
+ Rails.autoloaders.main.eager_load(force: true) if defined?(Rails)
168
+
169
+ self.class.instance.command_handlers ||= []
170
+ for_each_autoregisterable_descenant_of(Sequent::CommandHandler) do |command_handler_class|
171
+ Sequent.logger.debug("[Configuration] Autoregistering CommandHandler #{command_handler_class}")
172
+ self.class.instance.command_handlers << command_handler_class.new
173
+ end
174
+
175
+ self.class.instance.event_handlers ||= []
176
+ for_each_autoregisterable_descenant_of(Sequent::Projector) do |projector_class|
177
+ Sequent.logger.debug("[Configuration] Autoregistering Projector #{projector_class}")
178
+ self.class.instance.event_handlers << projector_class.new
179
+ end
180
+
181
+ for_each_autoregisterable_descenant_of(Sequent::Workflow) do |workflow_class|
182
+ Sequent.logger.debug("[Configuration] Autoregistering Workflow #{workflow_class}")
183
+ self.class.instance.event_handlers << workflow_class.new
184
+ end
185
+
186
+ self.class.instance.command_handlers.map(&:class).tally.each do |(clazz, count)|
187
+ if count > 1
188
+ fail "CommandHandler #{clazz} is registered #{count} times. A CommandHandler can only be registered once"
189
+ end
190
+ end
191
+
192
+ self.class.instance.event_handlers.map(&:class).tally.each do |(clazz, count)|
193
+ if count > 1
194
+ fail "EventHandler #{clazz} is registered #{count} times. An EventHandler can only be registered once"
195
+ end
196
+ end
197
+ end
198
+
199
+ private
200
+
201
+ def for_each_autoregisterable_descenant_of(clazz, &block)
202
+ clazz
203
+ .descendants
204
+ .reject(&:abstract_class)
205
+ .reject(&:skip_autoregister)
206
+ .each(&block)
207
+ end
152
208
  end
153
209
  end
@@ -79,8 +79,8 @@ module Sequent
79
79
  .configuration
80
80
  .event_store
81
81
  .stream_events_for_aggregate(aggregate_id, load_until: load_until) do |event_stream|
82
- aggregate.stream_from_history(event_stream)
83
- end
82
+ aggregate.stream_from_history(event_stream)
83
+ end
84
84
 
85
85
  if clazz
86
86
  fail TypeError, "#{aggregate.class} is not a #{clazz}" unless aggregate.class <= clazz
@@ -15,6 +15,10 @@ module Sequent
15
15
  end
16
16
 
17
17
  class AggregateSnapshotter < BaseCommandHandler
18
+ # By default skip autoregistering this CommandHandler.
19
+ # The AggregateSnapshotter is only autoregistered if autoregistration is enabled.
20
+ self.skip_autoregister = true
21
+
18
22
  on SnapshotCommand do |command|
19
23
  aggregate_ids = Sequent.configuration.event_store.aggregates_that_need_snapshots(
20
24
  @last_aggregate_id,
@@ -21,6 +21,11 @@ module Sequent
21
21
  class BaseCommandHandler
22
22
  include Sequent::Core::Helpers::UuidHelper
23
23
  include Sequent::Core::Helpers::MessageHandler
24
+ extend ActiveSupport::DescendantsTracker
25
+
26
+ class << self
27
+ attr_accessor :abstract_class, :skip_autoregister
28
+ end
24
29
 
25
30
  protected
26
31
 
@@ -16,7 +16,7 @@ require_relative 'projector'
16
16
  require_relative 'event_store'
17
17
  require_relative 'event_record'
18
18
  require_relative 'command_record'
19
- require_relative 'aggregate_snapshotter'
20
19
  require_relative 'workflow'
21
20
  require_relative 'random_uuid_generator'
22
21
  require_relative 'event_publisher'
22
+ require_relative 'aggregate_snapshotter'
@@ -29,8 +29,8 @@ module Sequent
29
29
  .reject { |k| payload_variables.include?(k) }
30
30
  .select { |k| self.class.types.keys.include?(to_attribute_name(k)) }
31
31
  .each do |k|
32
- result[k.to_s[1..-1].to_sym] = instance_variable_get(k)
33
- end
32
+ result[k.to_s[1..-1].to_sym] = instance_variable_get(k)
33
+ end
34
34
  result
35
35
  end
36
36
 
@@ -35,14 +35,6 @@ module Sequent
35
35
  end
36
36
  end
37
37
 
38
- def initialize(cache_event_types: true)
39
- @event_types = if cache_event_types
40
- ThreadSafe::Cache.new
41
- else
42
- NoEventTypesCache.new
43
- end
44
- end
45
-
46
38
  ##
47
39
  # Stores the events in the EventStore and publishes the events
48
40
  # to the registered event_handlers.
@@ -114,13 +106,13 @@ module Sequent
114
106
  events
115
107
  .group_by(&:aggregate_id)
116
108
  .map do |aggregate_id, es|
117
- [
118
- streams.find do |stream_record|
119
- stream_record.aggregate_id == aggregate_id
120
- end.event_stream,
121
- es,
122
- ]
123
- end
109
+ [
110
+ streams.find do |stream_record|
111
+ stream_record.aggregate_id == aggregate_id
112
+ end.event_stream,
113
+ es,
114
+ ]
115
+ end
124
116
  end
125
117
 
126
118
  def aggregate_query(aggregate_id)
@@ -217,6 +209,14 @@ module Sequent
217
209
 
218
210
  private
219
211
 
212
+ def event_types
213
+ @event_types = if Sequent.configuration.event_store_cache_event_types
214
+ ThreadSafe::Cache.new
215
+ else
216
+ NoEventTypesCache.new
217
+ end
218
+ end
219
+
220
220
  def column_names
221
221
  @column_names ||= Sequent
222
222
  .configuration
@@ -238,7 +238,7 @@ module Sequent
238
238
  end
239
239
 
240
240
  def resolve_event_type(event_type)
241
- @event_types.fetch_or_store(event_type) { |k| Class.const_get(k) }
241
+ event_types.fetch_or_store(event_type) { |k| Class.const_get(k) }
242
242
  end
243
243
 
244
244
  def publish_events(events)
@@ -79,8 +79,8 @@ module Sequent
79
79
  super if defined?(super)
80
80
  ensure_known_attributes(attrs)
81
81
  #{@types.map do |attribute, _|
82
- "@#{attribute} = attrs[:#{attribute}]"
83
- end.join("\n ")}
82
+ "@#{attribute} = attrs[:#{attribute}]"
83
+ end.join("\n ")}
84
84
  self
85
85
  end
86
86
  EOS
@@ -89,8 +89,8 @@ EOS
89
89
  def update_all_attributes_from_json(attrs)
90
90
  super if defined?(super)
91
91
  #{@types.map do |attribute, type|
92
- "@#{attribute} = #{type}.deserialize_from_json(attrs['#{attribute}'])"
93
- end.join("\n ")}
92
+ "@#{attribute} = #{type}.deserialize_from_json(attrs['#{attribute}'])"
93
+ end.join("\n ")}
94
94
  end
95
95
  EOS
96
96
  end
@@ -206,10 +206,10 @@ EOS
206
206
  value
207
207
  .select { |val| val.respond_to?(:validation_errors) }
208
208
  .each_with_index do |val, index|
209
- val.validation_errors.each do |k, v|
210
- result["#{field[0]}_#{index}_#{k}".to_sym] = v
209
+ val.validation_errors.each do |k, v|
210
+ result["#{field[0]}_#{index}_#{k}".to_sym] = v
211
+ end
211
212
  end
212
- end
213
213
  end
214
214
  end
215
215
  prefix ? HashWithIndifferentAccess[result.map { |k, v| ["#{prefix}_#{k}", v] }] : result
@@ -12,7 +12,10 @@ module Sequent
12
12
  def dispatch_message(message)
13
13
  @message_router
14
14
  .match_message(message)
15
- .each { |handler| @context.instance_exec(message, &handler) }
15
+ .each do |handler|
16
+ Sequent.logger.debug("[MessageDispatcher] Handler #{@context.class} handling #{message.class}")
17
+ @context.instance_exec(message, &handler)
18
+ end
16
19
  end
17
20
  end
18
21
  end
@@ -88,6 +88,10 @@ module Sequent
88
88
  include Migratable
89
89
  extend ActiveSupport::DescendantsTracker
90
90
 
91
+ class << self
92
+ attr_accessor :abstract_class, :skip_autoregister
93
+ end
94
+
91
95
  def initialize(persistor = Sequent::Core::Persistors::ActiveRecordPersistor.new)
92
96
  ensure_valid!
93
97
  @persistor = persistor
@@ -30,8 +30,9 @@ module Sequent
30
30
  #
31
31
  class ActiveRecordTransactionProvider
32
32
  def transactional(&block)
33
- Sequent::ApplicationRecord.transaction(requires_new: true, &block)
33
+ result = Sequent::ApplicationRecord.transaction(requires_new: true, &block)
34
34
  after_commit_queue.pop.call until after_commit_queue.empty?
35
+ result
35
36
  ensure
36
37
  clear_after_commit_queue
37
38
  end
@@ -9,6 +9,10 @@ module Sequent
9
9
  include Helpers::MessageHandler
10
10
  extend ActiveSupport::DescendantsTracker
11
11
 
12
+ class << self
13
+ attr_accessor :abstract_class, :skip_autoregister
14
+ end
15
+
12
16
  def self.on(*args, **opts, &block)
13
17
  decorated_block = ->(event) do
14
18
  begin
@@ -5,7 +5,6 @@ module Sequent
5
5
  end
6
6
  end
7
7
 
8
- require_relative 'migrate_events'
9
8
  require_relative 'projectors'
10
9
  require_relative 'view_schema'
11
10
  require_relative 'functions'
@@ -69,10 +69,10 @@ module Sequent
69
69
  .each_with_index
70
70
  .select { |migration, _index| migration.instance_of?(AlterTable) }
71
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)
72
+ migrations
73
+ .slice((index + 1)..-1)
74
+ .find { |m| m.instance_of?(ReplayTable) && m.record_class == migration.record_class }
75
+ end.map(&:first)
76
76
  end
77
77
 
78
78
  def remove_redundancy(grouped_migrations)
@@ -387,8 +387,8 @@ module Sequent
387
387
  unless ids.empty?
388
388
  exec_sql(
389
389
  "insert into #{ReplayedIds.table_name} (event_id) values #{ids.map do |id|
390
- "(#{id})"
391
- end.join(',')}",
390
+ "(#{id})"
391
+ end.join(',')}",
392
392
  )
393
393
  end
394
394
  Sequent::Core::EventStore::PRINT_PROGRESS[progress, done, ids] if progress > 0
@@ -22,21 +22,14 @@ module Sequent
22
22
  # A minimal setup could look like this:
23
23
  #
24
24
  # Sequent.configure do |config|
25
- # config.event_handlers = [
26
- # MyProjector.new,
27
- # AnotherProjector.new,
28
- # MyWorkflow.new,
29
- # ]
30
- #
31
- # config.command_handlers = [
32
- # MyCommandHandler.new,
33
- # ]
34
- #
25
+ # config.enable_autoregistration = true
35
26
  # end
36
27
  #
37
- #
28
+ # Calling configure a second time will create a new configuration
38
29
  def self.configure
30
+ Configuration.reset
39
31
  yield Configuration.instance
32
+ Configuration.instance.autoregister!
40
33
  end
41
34
 
42
35
  def self.configuration
@@ -53,7 +53,9 @@ module Sequent
53
53
  db_config = db_config.deep_merge(
54
54
  Sequent.configuration.primary_database_key => db_config_overrides,
55
55
  ).stringify_keys
56
- ActiveRecord.legacy_connection_handling = false
56
+ if Gem.loaded_specs['activerecord'].version < Gem::Version.create('7.1.0')
57
+ ActiveRecord.legacy_connection_handling = false
58
+ end
57
59
  ActiveRecord::Base.configurations = db_config.stringify_keys
58
60
  ActiveRecord::Base.connects_to database: {
59
61
  Sequent.configuration.primary_database_role => Sequent.configuration.primary_database_key,
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sequent
4
- VERSION = '6.0.1'
4
+ VERSION = '7.0.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequent
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.1
4
+ version: 7.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lars Vonk
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2023-09-28 00:00:00.000000000 Z
15
+ date: 2023-11-02 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activemodel
@@ -23,7 +23,7 @@ dependencies:
23
23
  version: '5.0'
24
24
  - - "<"
25
25
  - !ruby/object:Gem::Version
26
- version: '7.1'
26
+ version: '7.2'
27
27
  type: :runtime
28
28
  prerelease: false
29
29
  version_requirements: !ruby/object:Gem::Requirement
@@ -33,7 +33,7 @@ dependencies:
33
33
  version: '5.0'
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
- version: '7.1'
36
+ version: '7.2'
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: activerecord
39
39
  requirement: !ruby/object:Gem::Requirement
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '5.0'
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
- version: '7.1'
46
+ version: '7.2'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
@@ -53,7 +53,7 @@ dependencies:
53
53
  version: '5.0'
54
54
  - - "<"
55
55
  - !ruby/object:Gem::Version
56
- version: '7.1'
56
+ version: '7.2'
57
57
  - !ruby/object:Gem::Dependency
58
58
  name: bcrypt
59
59
  requirement: !ruby/object:Gem::Requirement
@@ -430,7 +430,6 @@ files:
430
430
  - lib/sequent/generator/template_project/spec/spec_helper.rb
431
431
  - lib/sequent/migrations/executor.rb
432
432
  - lib/sequent/migrations/functions.rb
433
- - lib/sequent/migrations/migrate_events.rb
434
433
  - lib/sequent/migrations/migrations.rb
435
434
  - lib/sequent/migrations/planner.rb
436
435
  - lib/sequent/migrations/projectors.rb
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- ##
4
- # When you need to upgrade the event store based on information of the previous schema version
5
- # this is the place you need to implement a migration.
6
- # Examples are: corrupt events (due to insufficient testing for instance...)
7
- # or adding extra events to the event stream if a new concept is introduced.
8
- #
9
- # To implement a migration you should create a class according to the following contract:
10
- # module Database
11
- # class MigrateToVersionXXX
12
- # def initialize(env)
13
- # @env = env
14
- # end
15
- #
16
- # def migrate
17
- # # your migration code here...
18
- # end
19
- # end
20
- # end
21
- #
22
- module Sequent
23
- module Migrations
24
- class MigrateEvents
25
- ##
26
- # @param env The string representing the current environment. E.g. "development", "production"
27
- def initialize(env)
28
- warn <<~EOS
29
- [DEPRECATED] Use of MigrateEvents is deprecated and will be removed from future version. Please use Sequent::Migrations::ViewSchema instead. See the changelog on how to update.
30
- EOS
31
- @env = env
32
- end
33
-
34
- ##
35
- #
36
- # @param current_version The current version of the application. E.g. 10
37
- # @param new_version The version to migrate to. E.g. 11
38
- # @param &after_migration_block an optional block (with the current upgrade version as param)
39
- # to run after the migrations run. E.g. close resources
40
- #
41
- def execute_migrations(current_version, new_version)
42
- migrations(current_version, new_version).each do |migration_class|
43
- migration = migration_class.new(@env)
44
- begin
45
- migration.migrate
46
- ensure
47
- yield(migration.version) if block_given?
48
- end
49
- end
50
- end
51
-
52
- def migrations(current_version, new_version)
53
- return [] if current_version == 0
54
-
55
- ((current_version + 1)..new_version).map do |upgrade_to_version|
56
- Class.const_get("Database::MigrateToVersion#{upgrade_to_version}")
57
- rescue NameError
58
- nil
59
- end.compact
60
- end
61
-
62
- def has_migrations?(current_version, new_version)
63
- migrations(current_version, new_version).any?
64
- end
65
- end
66
- end
67
- end