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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequent +31 -25
  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 +17 -13
  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 +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 +57 -50
  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 +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 +5 -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 +2 -0
  59. data/lib/sequent/generator/project.rb +2 -0
  60. data/lib/sequent/generator/template_project/Gemfile +1 -1
  61. data/lib/sequent/generator.rb +2 -0
  62. data/lib/sequent/migrations/executor.rb +22 -13
  63. data/lib/sequent/migrations/functions.rb +5 -6
  64. data/lib/sequent/migrations/migrate_events.rb +12 -9
  65. data/lib/sequent/migrations/migrations.rb +2 -1
  66. data/lib/sequent/migrations/planner.rb +33 -23
  67. data/lib/sequent/migrations/projectors.rb +4 -3
  68. data/lib/sequent/migrations/sql.rb +2 -0
  69. data/lib/sequent/migrations/view_schema.rb +84 -45
  70. data/lib/sequent/rake/migration_tasks.rb +58 -22
  71. data/lib/sequent/rake/tasks.rb +5 -2
  72. data/lib/sequent/sequent.rb +2 -0
  73. data/lib/sequent/support/database.rb +30 -15
  74. data/lib/sequent/support/view_projection.rb +6 -3
  75. data/lib/sequent/support/view_schema.rb +2 -0
  76. data/lib/sequent/support.rb +2 -0
  77. data/lib/sequent/test/command_handler_helpers.rb +35 -17
  78. data/lib/sequent/test/event_handler_helpers.rb +10 -4
  79. data/lib/sequent/test/event_stream_helpers.rb +7 -3
  80. data/lib/sequent/test/time_comparison.rb +12 -5
  81. data/lib/sequent/test.rb +2 -0
  82. data/lib/sequent/util/dry_run.rb +11 -8
  83. data/lib/sequent/util/printer.rb +6 -5
  84. data/lib/sequent/util/skip_if_already_processing.rb +3 -1
  85. data/lib/sequent/util/timer.rb +2 -0
  86. data/lib/sequent/util/util.rb +2 -0
  87. data/lib/sequent.rb +2 -0
  88. data/lib/version.rb +3 -1
  89. 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.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'
@@ -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
- :load_events,
37
- :publish_events,
38
- :stream_exists?,
39
- to: :event_store
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 "+++++++++++++++++++++++++++++++++++" if index == 0
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 "" if i > 0
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
 
@@ -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.to_s}\n\n#{e.backtrace.join("\n")}"
7
+ logger.error "#{e}\n\n#{e.backtrace.join("\n")}"
6
8
 
7
- while e.cause do
8
- logger.error "+++++++++++++++ CAUSE +++++++++++++++"
9
- logger.error "#{e.cause.to_s}\n\n#{e.cause.backtrace.join("\n")}"
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, &block)
11
+ def self.skip_if_already_processing(processing_key)
10
12
  return if Thread.current[processing_key]
11
13
 
12
14
  begin
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
4
  module Util
3
5
  module Timer
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'skip_if_already_processing'
2
4
  require_relative 'timer'
3
5
  require_relative 'printer'
data/lib/sequent.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'sequent/application_record'
2
4
  require_relative 'sequent/sequent'
3
5
  require_relative 'sequent/core/core'
data/lib/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequent
2
- VERSION = '4.0.0'
4
+ VERSION = '4.1.0'
3
5
  end