sequent 4.0.0 → 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.
- checksums.yaml +4 -4
- data/bin/sequent +31 -25
- data/lib/notices.rb +2 -0
- data/lib/sequent/application_record.rb +2 -0
- data/lib/sequent/configuration.rb +24 -31
- data/lib/sequent/core/aggregate_repository.rb +17 -13
- data/lib/sequent/core/aggregate_root.rb +16 -7
- data/lib/sequent/core/aggregate_roots.rb +24 -0
- data/lib/sequent/core/aggregate_snapshotter.rb +8 -5
- data/lib/sequent/core/base_command_handler.rb +4 -2
- data/lib/sequent/core/command.rb +17 -9
- data/lib/sequent/core/command_record.rb +8 -3
- data/lib/sequent/core/command_service.rb +18 -18
- data/lib/sequent/core/core.rb +2 -0
- data/lib/sequent/core/current_event.rb +2 -0
- data/lib/sequent/core/event.rb +16 -11
- data/lib/sequent/core/event_publisher.rb +16 -15
- data/lib/sequent/core/event_record.rb +7 -7
- data/lib/sequent/core/event_store.rb +57 -50
- data/lib/sequent/core/ext/ext.rb +9 -1
- data/lib/sequent/core/helpers/array_with_type.rb +4 -1
- data/lib/sequent/core/helpers/association_validator.rb +9 -7
- data/lib/sequent/core/helpers/attribute_support.rb +45 -28
- data/lib/sequent/core/helpers/autoset_attributes.rb +4 -4
- data/lib/sequent/core/helpers/boolean_validator.rb +6 -1
- data/lib/sequent/core/helpers/copyable.rb +2 -2
- data/lib/sequent/core/helpers/date_time_validator.rb +4 -1
- data/lib/sequent/core/helpers/date_validator.rb +6 -1
- data/lib/sequent/core/helpers/default_validators.rb +12 -10
- data/lib/sequent/core/helpers/equal_support.rb +8 -6
- data/lib/sequent/core/helpers/helpers.rb +2 -0
- data/lib/sequent/core/helpers/mergable.rb +6 -5
- data/lib/sequent/core/helpers/message_handler.rb +3 -1
- data/lib/sequent/core/helpers/param_support.rb +19 -15
- data/lib/sequent/core/helpers/secret.rb +14 -12
- data/lib/sequent/core/helpers/string_support.rb +5 -4
- data/lib/sequent/core/helpers/string_to_value_parsers.rb +7 -2
- data/lib/sequent/core/helpers/string_validator.rb +6 -1
- data/lib/sequent/core/helpers/type_conversion_support.rb +5 -3
- data/lib/sequent/core/helpers/uuid_helper.rb +5 -2
- data/lib/sequent/core/helpers/value_validators.rb +23 -9
- data/lib/sequent/core/persistors/active_record_persistor.rb +19 -9
- data/lib/sequent/core/persistors/persistor.rb +16 -14
- data/lib/sequent/core/persistors/persistors.rb +2 -0
- data/lib/sequent/core/persistors/replay_optimized_postgres_persistor.rb +70 -47
- data/lib/sequent/core/projector.rb +25 -22
- data/lib/sequent/core/random_uuid_generator.rb +2 -0
- data/lib/sequent/core/sequent_oj.rb +2 -0
- data/lib/sequent/core/stream_record.rb +9 -3
- data/lib/sequent/core/transactions/active_record_transaction_provider.rb +5 -9
- data/lib/sequent/core/transactions/no_transactions.rb +2 -1
- data/lib/sequent/core/transactions/transactions.rb +2 -0
- data/lib/sequent/core/value_object.rb +8 -10
- data/lib/sequent/core/workflow.rb +7 -5
- data/lib/sequent/generator/aggregate.rb +16 -10
- data/lib/sequent/generator/command.rb +26 -19
- data/lib/sequent/generator/event.rb +19 -17
- data/lib/sequent/generator/generator.rb +2 -0
- data/lib/sequent/generator/project.rb +2 -0
- data/lib/sequent/generator/template_project/Gemfile +1 -1
- data/lib/sequent/generator.rb +2 -0
- data/lib/sequent/migrations/executor.rb +22 -13
- data/lib/sequent/migrations/functions.rb +5 -6
- data/lib/sequent/migrations/migrate_events.rb +12 -9
- data/lib/sequent/migrations/migrations.rb +2 -1
- data/lib/sequent/migrations/planner.rb +33 -23
- data/lib/sequent/migrations/projectors.rb +4 -3
- data/lib/sequent/migrations/sql.rb +2 -0
- data/lib/sequent/migrations/view_schema.rb +84 -45
- data/lib/sequent/rake/migration_tasks.rb +58 -22
- data/lib/sequent/rake/tasks.rb +5 -2
- data/lib/sequent/sequent.rb +2 -0
- data/lib/sequent/support/database.rb +30 -15
- data/lib/sequent/support/view_projection.rb +6 -3
- data/lib/sequent/support/view_schema.rb +2 -0
- data/lib/sequent/support.rb +2 -0
- data/lib/sequent/test/command_handler_helpers.rb +35 -17
- data/lib/sequent/test/event_handler_helpers.rb +10 -4
- data/lib/sequent/test/event_stream_helpers.rb +7 -3
- data/lib/sequent/test/time_comparison.rb +12 -5
- data/lib/sequent/test.rb +2 -0
- data/lib/sequent/util/dry_run.rb +11 -8
- data/lib/sequent/util/printer.rb +6 -5
- data/lib/sequent/util/skip_if_already_processing.rb +3 -1
- data/lib/sequent/util/timer.rb +2 -0
- data/lib/sequent/util/util.rb +2 -0
- data/lib/sequent.rb +2 -0
- data/lib/version.rb +3 -1
- metadata +81 -66
|
@@ -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.
|
|
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.
|
|
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 =
|
|
110
|
-
|
|
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
|
|
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::
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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.
|
|
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.
|
|
63
|
-
|
|
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
|
-
|
|
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(
|
|
75
|
+
def self.included(_spec)
|
|
72
76
|
require 'factory_bot'
|
|
73
77
|
rescue LoadError
|
|
74
|
-
raise ArgumentError,
|
|
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(
|
|
8
|
+
in_time_zone('UTC')
|
|
7
9
|
end
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
module Compare
|
|
11
|
-
|
|
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
|
|
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
|
-
|
|
39
|
-
|
|
43
|
+
module ActiveSupport
|
|
44
|
+
class TimeWithZone
|
|
45
|
+
prepend Sequent::Test::DateTimePatches::Normalize
|
|
46
|
+
end
|
|
40
47
|
end
|
data/lib/sequent/test.rb
CHANGED
data/lib/sequent/util/dry_run.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../test/command_handler_helpers'
|
|
2
4
|
|
|
3
5
|
module Sequent
|
|
@@ -33,10 +35,10 @@ module Sequent
|
|
|
33
35
|
attr_reader :command_with_events, :event_store
|
|
34
36
|
|
|
35
37
|
delegate :load_events_for_aggregates,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
:load_events,
|
|
39
|
+
:publish_events,
|
|
40
|
+
:stream_exists?,
|
|
41
|
+
to: :event_store
|
|
40
42
|
|
|
41
43
|
def initialize(result)
|
|
42
44
|
@event_store = Sequent::Test::CommandHandlerHelpers::FakeEventStore.new
|
|
@@ -59,6 +61,7 @@ module Sequent
|
|
|
59
61
|
attr_reader :projectors, :workflows
|
|
60
62
|
|
|
61
63
|
def initialize(result)
|
|
64
|
+
super()
|
|
62
65
|
@result = result
|
|
63
66
|
end
|
|
64
67
|
|
|
@@ -73,7 +76,7 @@ module Sequent
|
|
|
73
76
|
else
|
|
74
77
|
fail "Unrecognized event_handler #{handler.class} called for event #{event.class}"
|
|
75
78
|
end
|
|
76
|
-
rescue
|
|
79
|
+
rescue StandardError
|
|
77
80
|
raise PublishEventError.new(handler.class, event)
|
|
78
81
|
end
|
|
79
82
|
end
|
|
@@ -141,16 +144,16 @@ module Sequent
|
|
|
141
144
|
#
|
|
142
145
|
def print(io)
|
|
143
146
|
tree.each_with_index do |(command, event_called_handlerss), index|
|
|
144
|
-
io.puts
|
|
147
|
+
io.puts '+++++++++++++++++++++++++++++++++++' if index == 0
|
|
145
148
|
io.puts "Command: #{command.class} resulted in #{event_called_handlerss.length} events"
|
|
146
149
|
event_called_handlerss.each_with_index do |event_called_handlers, i|
|
|
147
|
-
io.puts
|
|
150
|
+
io.puts '' if i > 0
|
|
148
151
|
io.puts "-- Event #{event_called_handlers.event.class} was handled by:"
|
|
149
152
|
io.puts "-- Projectors: [#{event_called_handlers.projectors.join(', ')}]"
|
|
150
153
|
io.puts "-- Workflows: [#{event_called_handlers.workflows.join(', ')}]"
|
|
151
154
|
end
|
|
152
155
|
|
|
153
|
-
io.puts
|
|
156
|
+
io.puts '+++++++++++++++++++++++++++++++++++'
|
|
154
157
|
end
|
|
155
158
|
end
|
|
156
159
|
|
data/lib/sequent/util/printer.rb
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Util
|
|
3
5
|
module Printer
|
|
4
6
|
def recursively_print(e)
|
|
5
|
-
logger.error "#{e
|
|
7
|
+
logger.error "#{e}\n\n#{e.backtrace.join("\n")}"
|
|
6
8
|
|
|
7
|
-
while e.cause
|
|
8
|
-
logger.error
|
|
9
|
-
logger.error "#{e.cause
|
|
9
|
+
while e.cause
|
|
10
|
+
logger.error '+++++++++++++++ CAUSE +++++++++++++++'
|
|
11
|
+
logger.error "#{e.cause}\n\n#{e.cause.backtrace.join("\n")}"
|
|
10
12
|
e = e.cause
|
|
11
13
|
end
|
|
12
14
|
end
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
|
-
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Util
|
|
3
5
|
##
|
|
@@ -6,7 +8,7 @@ module Sequent
|
|
|
6
8
|
# it yields the given +&block+.
|
|
7
9
|
#
|
|
8
10
|
# Useful in a Queue and Processing strategy
|
|
9
|
-
def self.skip_if_already_processing(processing_key
|
|
11
|
+
def self.skip_if_already_processing(processing_key)
|
|
10
12
|
return if Thread.current[processing_key]
|
|
11
13
|
|
|
12
14
|
begin
|
data/lib/sequent/util/timer.rb
CHANGED
data/lib/sequent/util/util.rb
CHANGED
data/lib/sequent.rb
CHANGED
data/lib/version.rb
CHANGED