sequent 7.1.1 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/sequent +6 -107
- data/db/sequent_8_migration.sql +120 -0
- data/db/sequent_pgsql.sql +416 -0
- data/db/sequent_schema.rb +11 -57
- data/db/sequent_schema_indexes.sql +37 -0
- data/db/sequent_schema_partitions.sql +34 -0
- data/db/sequent_schema_tables.sql +74 -0
- data/lib/sequent/cli/app.rb +132 -0
- data/lib/sequent/cli/sequent_8_migration.rb +180 -0
- data/lib/sequent/configuration.rb +11 -8
- data/lib/sequent/core/aggregate_repository.rb +2 -2
- data/lib/sequent/core/aggregate_root.rb +32 -9
- data/lib/sequent/core/aggregate_snapshotter.rb +8 -6
- data/lib/sequent/core/command_record.rb +27 -18
- data/lib/sequent/core/command_service.rb +2 -2
- data/lib/sequent/core/event_publisher.rb +1 -1
- data/lib/sequent/core/event_record.rb +37 -17
- data/lib/sequent/core/event_store.rb +101 -119
- data/lib/sequent/core/helpers/array_with_type.rb +1 -1
- data/lib/sequent/core/helpers/association_validator.rb +2 -2
- data/lib/sequent/core/helpers/attribute_support.rb +8 -8
- data/lib/sequent/core/helpers/equal_support.rb +3 -3
- data/lib/sequent/core/helpers/message_matchers/has_attrs.rb +2 -0
- data/lib/sequent/core/helpers/message_router.rb +2 -2
- data/lib/sequent/core/helpers/param_support.rb +1 -3
- data/lib/sequent/core/helpers/pgsql_helpers.rb +32 -0
- data/lib/sequent/core/helpers/string_support.rb +1 -1
- data/lib/sequent/core/helpers/string_to_value_parsers.rb +1 -1
- data/lib/sequent/core/persistors/active_record_persistor.rb +1 -1
- data/lib/sequent/core/persistors/replay_optimized_postgres_persistor.rb +3 -4
- data/lib/sequent/core/projector.rb +1 -1
- data/lib/sequent/core/snapshot_record.rb +44 -0
- data/lib/sequent/core/snapshot_store.rb +105 -0
- data/lib/sequent/core/stream_record.rb +10 -15
- data/lib/sequent/dry_run/read_only_replay_optimized_postgres_persistor.rb +1 -1
- data/lib/sequent/dry_run/view_schema.rb +2 -3
- data/lib/sequent/generator/project.rb +5 -7
- data/lib/sequent/generator/template_aggregate/template_aggregate/commands.rb +2 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate/events.rb +2 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate/template_aggregate.rb +2 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate/template_aggregate_command_handler.rb +2 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate.rb +2 -0
- data/lib/sequent/generator/template_project/Gemfile +7 -5
- data/lib/sequent/generator/template_project/Rakefile +4 -2
- data/lib/sequent/generator/template_project/app/projectors/post_projector.rb +2 -0
- data/lib/sequent/generator/template_project/app/records/post_record.rb +2 -0
- data/lib/sequent/generator/template_project/config/initializers/sequent.rb +3 -8
- data/lib/sequent/generator/template_project/db/migrations.rb +3 -3
- data/lib/sequent/generator/template_project/lib/post/commands.rb +2 -0
- data/lib/sequent/generator/template_project/lib/post/events.rb +2 -0
- data/lib/sequent/generator/template_project/lib/post/post.rb +2 -0
- data/lib/sequent/generator/template_project/lib/post/post_command_handler.rb +2 -0
- data/lib/sequent/generator/template_project/lib/post.rb +2 -0
- data/lib/sequent/generator/template_project/my_app.rb +2 -1
- data/lib/sequent/generator/template_project/spec/app/projectors/post_projector_spec.rb +2 -0
- data/lib/sequent/generator/template_project/spec/lib/post/post_command_handler_spec.rb +9 -2
- data/lib/sequent/generator/template_project/spec/spec_helper.rb +4 -7
- data/lib/sequent/generator.rb +1 -1
- data/lib/sequent/internal/aggregate_type.rb +12 -0
- data/lib/sequent/internal/command_type.rb +12 -0
- data/lib/sequent/internal/event_type.rb +12 -0
- data/lib/sequent/internal/internal.rb +14 -0
- data/lib/sequent/internal/partitioned_aggregate.rb +26 -0
- data/lib/sequent/internal/partitioned_command.rb +16 -0
- data/lib/sequent/internal/partitioned_event.rb +29 -0
- data/lib/sequent/migrations/grouper.rb +90 -0
- data/lib/sequent/migrations/sequent_schema.rb +2 -1
- data/lib/sequent/migrations/view_schema.rb +76 -77
- data/lib/sequent/rake/migration_tasks.rb +49 -24
- data/lib/sequent/sequent.rb +1 -0
- data/lib/sequent/support/database.rb +20 -16
- data/lib/sequent/test/time_comparison.rb +1 -1
- data/lib/sequent/util/timer.rb +1 -1
- data/lib/version.rb +1 -1
- metadata +102 -21
- data/lib/sequent/generator/template_project/db/sequent_schema.rb +0 -52
- data/lib/sequent/generator/template_project/ruby-version +0 -1
@@ -63,13 +63,13 @@ module Sequent
|
|
63
63
|
Sequent::Core::Helpers::DefaultValidators.for(type).add_validations_for(self, attribute)
|
64
64
|
end
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
66
|
+
is_array = type.instance_of?(Sequent::Core::Helpers::ArrayWithType)
|
67
|
+
needs_validation = !is_array && included_modules.include?(ActiveModel::Validations) &&
|
68
|
+
type.included_modules.include?(Sequent::Core::Helpers::AttributeSupport)
|
69
|
+
|
70
|
+
associations << attribute if is_array || needs_validation
|
72
71
|
end
|
72
|
+
|
73
73
|
if included_modules.include?(ActiveModel::Validations) && associations.present?
|
74
74
|
validates_with Sequent::Core::Helpers::AssociationValidator, associations: associations
|
75
75
|
end
|
@@ -168,7 +168,7 @@ EOS
|
|
168
168
|
|
169
169
|
def attributes
|
170
170
|
hash = HashWithIndifferentAccess.new
|
171
|
-
self.class.types.
|
171
|
+
self.class.types.each_key do |name|
|
172
172
|
value = instance_variable_get("@#{name}")
|
173
173
|
hash[name] = if value.respond_to?(:attributes)
|
174
174
|
value.attributes
|
@@ -181,7 +181,7 @@ EOS
|
|
181
181
|
|
182
182
|
def as_json(opts = {})
|
183
183
|
hash = HashWithIndifferentAccess.new
|
184
|
-
self.class.types.
|
184
|
+
self.class.types.each_key do |name|
|
185
185
|
value = instance_variable_get("@#{name}")
|
186
186
|
hash[name] = if value.respond_to?(:as_json)
|
187
187
|
value.as_json(opts)
|
@@ -13,7 +13,7 @@ module Sequent
|
|
13
13
|
return false if other.nil?
|
14
14
|
return false if self.class != other.class
|
15
15
|
|
16
|
-
self.class.types.
|
16
|
+
self.class.types.each_key do |name|
|
17
17
|
self_value = send(name)
|
18
18
|
other_value = other.send(name)
|
19
19
|
if self_value.class == DateTime && other_value.class == DateTime
|
@@ -28,8 +28,8 @@ module Sequent
|
|
28
28
|
|
29
29
|
def hash
|
30
30
|
hash = 17
|
31
|
-
self.class.types.
|
32
|
-
hash = hash * 31 + send(name).hash
|
31
|
+
self.class.types.each_key do |name|
|
32
|
+
hash = (hash * 31) + send(name).hash
|
33
33
|
end
|
34
34
|
hash
|
35
35
|
end
|
@@ -20,10 +20,12 @@ module Sequent
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def to_s
|
23
|
+
# rubocop:disable Layout/LineEndStringConcatenationIndentation
|
23
24
|
'has_attrs(' \
|
24
25
|
"#{MessageMatchers::ArgumentSerializer.serialize_value(message_matcher)}, " \
|
25
26
|
"#{AttrMatchers::ArgumentSerializer.serialize_value(expected_attrs)}" \
|
26
27
|
')'
|
28
|
+
# rubocop:enable Layout/LineEndStringConcatenationIndentation
|
27
29
|
end
|
28
30
|
|
29
31
|
private
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
3
|
+
require_relative 'attr_matchers/attr_matchers'
|
4
|
+
require_relative 'message_matchers/message_matchers'
|
5
5
|
|
6
6
|
module Sequent
|
7
7
|
module Core
|
@@ -79,9 +79,7 @@ module Sequent
|
|
79
79
|
def value_to_string(val)
|
80
80
|
if val.is_a?(Sequent::Core::ValueObject)
|
81
81
|
val.as_params
|
82
|
-
elsif val.is_a?
|
83
|
-
val.iso8601
|
84
|
-
elsif val.is_a? Date
|
82
|
+
elsif val.is_a?(DateTime) || val.is_a?(Date)
|
85
83
|
val.iso8601
|
86
84
|
elsif val.is_a? Time
|
87
85
|
val.iso8601(Sequent.configuration.time_precision)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequent
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
module PgsqlHelpers
|
7
|
+
def call_procedure(connection, procedure, params)
|
8
|
+
fail ArgumentError if procedure.blank?
|
9
|
+
|
10
|
+
statement = "CALL #{quote_ident(procedure)}(#{bind_placeholders(params)})"
|
11
|
+
connection.exec_update(statement, procedure, params)
|
12
|
+
end
|
13
|
+
|
14
|
+
def query_function(connection, function, params, columns: [])
|
15
|
+
fail ArgumentError if function.blank?
|
16
|
+
|
17
|
+
cols = columns.blank? ? '*' : columns.map { |c| PG::Connection.quote_ident(c) }.join(', ')
|
18
|
+
query = "SELECT #{cols} FROM #{quote_ident(function)}(#{bind_placeholders(params)})"
|
19
|
+
connection.exec_query(query, function, params)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def bind_placeholders(params)
|
25
|
+
(1..params.size).map { |n| "$#{n}" }.join(', ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def quote_ident(...) = PG::Connection.quote_ident(...)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'set'
|
4
3
|
require 'active_record'
|
5
4
|
require 'csv'
|
6
|
-
require_relative '
|
5
|
+
require_relative 'persistor'
|
7
6
|
|
8
7
|
module Sequent
|
9
8
|
module Core
|
@@ -177,7 +176,7 @@ module Sequent
|
|
177
176
|
end
|
178
177
|
|
179
178
|
indices.each do |record_class, indexed_columns|
|
180
|
-
columns = indexed_columns.flatten(1).
|
179
|
+
columns = indexed_columns.flatten(1).to_set(&:to_sym) + default_indexed_columns
|
181
180
|
@record_index[record_class] = Index.new(columns & record_class.column_names.map(&:to_sym))
|
182
181
|
end
|
183
182
|
|
@@ -358,7 +357,7 @@ module Sequent
|
|
358
357
|
|
359
358
|
def clear
|
360
359
|
@record_store.clear
|
361
|
-
@record_index.
|
360
|
+
@record_index.each_value(&:clear)
|
362
361
|
end
|
363
362
|
|
364
363
|
private
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record'
|
4
|
+
require_relative 'sequent_oj'
|
5
|
+
require_relative '../application_record'
|
6
|
+
|
7
|
+
module Sequent
|
8
|
+
module Core
|
9
|
+
class SnapshotRecord < Sequent::ApplicationRecord
|
10
|
+
include SerializesEvent
|
11
|
+
|
12
|
+
self.primary_key = %i[aggregate_id sequence_number]
|
13
|
+
self.table_name = 'snapshot_records'
|
14
|
+
|
15
|
+
belongs_to :stream_record, foreign_key: :aggregate_id, primary_key: :aggregate_id
|
16
|
+
|
17
|
+
validates_presence_of :aggregate_id, :sequence_number, :snapshot_json, :stream_record
|
18
|
+
validates_numericality_of :sequence_number, only_integer: true, greater_than: 0
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def event_type
|
23
|
+
snapshot_type
|
24
|
+
end
|
25
|
+
|
26
|
+
def event_type=(type)
|
27
|
+
self.snapshot_type = type
|
28
|
+
end
|
29
|
+
|
30
|
+
def event_json
|
31
|
+
snapshot_json
|
32
|
+
end
|
33
|
+
|
34
|
+
def event_json=(json)
|
35
|
+
self.snapshot_json = json
|
36
|
+
end
|
37
|
+
|
38
|
+
def serialize_json?
|
39
|
+
json_column_type = self.class.columns_hash['snapshot_json'].sql_type_metadata.type
|
40
|
+
%i[json jsonb].exclude? json_column_type
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'sequent_oj'
|
4
|
+
require_relative 'helpers/pgsql_helpers'
|
5
|
+
|
6
|
+
module Sequent
|
7
|
+
module Core
|
8
|
+
module SnapshotStore
|
9
|
+
include Helpers::PgsqlHelpers
|
10
|
+
|
11
|
+
def store_snapshots(snapshots)
|
12
|
+
json = Sequent::Core::Oj.dump(
|
13
|
+
snapshots.map do |snapshot|
|
14
|
+
{
|
15
|
+
aggregate_id: snapshot.aggregate_id,
|
16
|
+
sequence_number: snapshot.sequence_number,
|
17
|
+
created_at: snapshot.created_at,
|
18
|
+
snapshot_type: snapshot.class.name,
|
19
|
+
snapshot_json: snapshot,
|
20
|
+
}
|
21
|
+
end,
|
22
|
+
)
|
23
|
+
|
24
|
+
call_procedure(connection, 'store_snapshots', [json])
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_latest_snapshot(aggregate_id)
|
28
|
+
snapshot_hash = query_function(connection, 'load_latest_snapshot', [aggregate_id]).first
|
29
|
+
deserialize_event(snapshot_hash) unless snapshot_hash['aggregate_id'].nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
# Deletes all snapshots for all aggregates
|
33
|
+
def delete_all_snapshots
|
34
|
+
call_procedure(connection, 'delete_all_snapshots', [Time.now])
|
35
|
+
end
|
36
|
+
|
37
|
+
# Deletes all snapshots for aggregate_id with a sequence_number lower than the specified sequence number.
|
38
|
+
def delete_snapshots_before(aggregate_id, sequence_number)
|
39
|
+
call_procedure(connection, 'delete_snapshots_before', [aggregate_id, sequence_number, Time.now])
|
40
|
+
end
|
41
|
+
|
42
|
+
# Marks an aggregate for snapshotting. Marked aggregates will be
|
43
|
+
# picked up by the background snapshotting task. Another way to
|
44
|
+
# mark aggregates for snapshotting is to pass the
|
45
|
+
# +EventStream#snapshot_outdated_at+ property to the
|
46
|
+
# +#store_events+ method as is done automatically by the
|
47
|
+
# +AggregateRepository+ based on the aggregate's
|
48
|
+
# +snapshot_threshold+.
|
49
|
+
def mark_aggregate_for_snapshotting(aggregate_id, snapshot_outdated_at: Time.now)
|
50
|
+
connection.exec_update(<<~EOS, 'mark_aggregate_for_snapshotting', [aggregate_id, snapshot_outdated_at])
|
51
|
+
INSERT INTO aggregates_that_need_snapshots AS row (aggregate_id, snapshot_outdated_at)
|
52
|
+
VALUES ($1, $2)
|
53
|
+
ON CONFLICT (aggregate_id) DO UPDATE
|
54
|
+
SET snapshot_outdated_at = LEAST(row.snapshot_outdated_at, EXCLUDED.snapshot_outdated_at),
|
55
|
+
snapshot_scheduled_at = NULL
|
56
|
+
EOS
|
57
|
+
end
|
58
|
+
|
59
|
+
# Stops snapshotting the specified aggregate. Any existing
|
60
|
+
# snapshots for this aggregate are also deleted.
|
61
|
+
def clear_aggregate_for_snapshotting(aggregate_id)
|
62
|
+
connection.exec_update(
|
63
|
+
'DELETE FROM aggregates_that_need_snapshots WHERE aggregate_id = $1',
|
64
|
+
'clear_aggregate_for_snapshotting',
|
65
|
+
[aggregate_id],
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Stops snapshotting all aggregates where the last event
|
70
|
+
# occurred before the indicated timestamp. Any existing
|
71
|
+
# snapshots for this aggregate are also deleted.
|
72
|
+
def clear_aggregates_for_snapshotting_with_last_event_before(timestamp)
|
73
|
+
connection.exec_update(<<~EOS, 'clear_aggregates_for_snapshotting_with_last_event_before', [timestamp])
|
74
|
+
DELETE FROM aggregates_that_need_snapshots s
|
75
|
+
WHERE NOT EXISTS (SELECT *
|
76
|
+
FROM aggregates a
|
77
|
+
JOIN events e ON (a.aggregate_id, a.events_partition_key) = (e.aggregate_id, e.partition_key)
|
78
|
+
WHERE a.aggregate_id = s.aggregate_id AND e.created_at >= $1)
|
79
|
+
EOS
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Returns the ids of aggregates that need a new snapshot.
|
84
|
+
#
|
85
|
+
def aggregates_that_need_snapshots(last_aggregate_id, limit = 10)
|
86
|
+
query_function(
|
87
|
+
connection,
|
88
|
+
'aggregates_that_need_snapshots',
|
89
|
+
[last_aggregate_id, limit],
|
90
|
+
columns: ['aggregate_id'],
|
91
|
+
)
|
92
|
+
.pluck('aggregate_id')
|
93
|
+
end
|
94
|
+
|
95
|
+
def select_aggregates_for_snapshotting(limit:, reschedule_snapshots_scheduled_before: nil)
|
96
|
+
query_function(
|
97
|
+
connection,
|
98
|
+
'select_aggregates_for_snapshotting',
|
99
|
+
[limit, reschedule_snapshots_scheduled_before, Time.now],
|
100
|
+
columns: ['aggregate_id'],
|
101
|
+
).pluck('aggregate_id')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -4,38 +4,33 @@ require 'active_record'
|
|
4
4
|
|
5
5
|
module Sequent
|
6
6
|
module Core
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(aggregate_type:, aggregate_id:, snapshot_threshold: nil, stream_record_id: nil)
|
11
|
-
@aggregate_type = aggregate_type
|
12
|
-
@aggregate_id = aggregate_id
|
13
|
-
@snapshot_threshold = snapshot_threshold
|
14
|
-
@stream_record_id = stream_record_id
|
7
|
+
EventStream = Data.define(:aggregate_type, :aggregate_id, :events_partition_key, :snapshot_outdated_at) do
|
8
|
+
def initialize(aggregate_type:, aggregate_id:, events_partition_key: '', snapshot_outdated_at: nil)
|
9
|
+
super
|
15
10
|
end
|
16
11
|
end
|
17
12
|
|
18
13
|
class StreamRecord < Sequent::ApplicationRecord
|
14
|
+
self.primary_key = %i[aggregate_id]
|
19
15
|
self.table_name = 'stream_records'
|
16
|
+
self.ignored_columns = %w[snapshot_threshold]
|
20
17
|
|
21
18
|
validates_presence_of :aggregate_type, :aggregate_id
|
22
|
-
validates_numericality_of :snapshot_threshold, only_integer: true, greater_than: 0, allow_nil: true
|
23
19
|
|
24
|
-
has_many :event_records
|
20
|
+
has_many :event_records, foreign_key: :aggregate_id, primary_key: :aggregate_id
|
25
21
|
|
26
22
|
def event_stream
|
27
23
|
EventStream.new(
|
28
|
-
aggregate_type
|
29
|
-
aggregate_id
|
30
|
-
|
31
|
-
stream_record_id: id,
|
24
|
+
aggregate_type:,
|
25
|
+
aggregate_id:,
|
26
|
+
events_partition_key:,
|
32
27
|
)
|
33
28
|
end
|
34
29
|
|
35
30
|
def event_stream=(data)
|
36
31
|
self.aggregate_type = data.aggregate_type
|
37
32
|
self.aggregate_id = data.aggregate_id
|
38
|
-
self.
|
33
|
+
self.events_partition_key = data.events_partition_key
|
39
34
|
end
|
40
35
|
end
|
41
36
|
end
|
@@ -15,7 +15,7 @@ module Sequent
|
|
15
15
|
# Running in dryrun mode, not committing anything.
|
16
16
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
17
17
|
elapsed = ending - @starting
|
18
|
-
count = @record_store.values.
|
18
|
+
count = @record_store.values.sum(&:size)
|
19
19
|
Sequent.logger.info(
|
20
20
|
"dryrun: processed #{count} records in #{elapsed.round(2)} s (#{(count / elapsed).round(2)} records/s)",
|
21
21
|
)
|
@@ -9,7 +9,7 @@ module Sequent
|
|
9
9
|
# This migration does not insert anything into the database, mainly usefull
|
10
10
|
# for performance testing migrations.
|
11
11
|
class ViewSchema < Migrations::ViewSchema
|
12
|
-
def migrate_dryrun(regex
|
12
|
+
def migrate_dryrun(regex:)
|
13
13
|
persistor = DryRun::ReadOnlyReplayOptimizedPostgresPersistor.new
|
14
14
|
|
15
15
|
projectors = Sequent::Core::Migratable.all.select { |p| p.replay_persistor.nil? && p.name.match(regex || /.*/) }
|
@@ -17,8 +17,7 @@ module Sequent
|
|
17
17
|
Sequent.logger.info "Dry run using the following projectors: #{projectors.map(&:name).join(', ')}"
|
18
18
|
|
19
19
|
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
20
|
-
|
21
|
-
replay!(persistor, projectors: projectors, groups: groups)
|
20
|
+
replay!(persistor, projectors:)
|
22
21
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
23
22
|
|
24
23
|
Sequent.logger.info("Done migrate_dryrun for version #{Sequent.new_version} in #{ending - starting} s")
|
@@ -12,9 +12,10 @@ module Sequent
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def execute
|
15
|
+
fail TargetAlreadyExists if File.exist?(path)
|
16
|
+
|
15
17
|
make_directory
|
16
18
|
copy_files
|
17
|
-
rename_ruby_version
|
18
19
|
rename_app_file
|
19
20
|
replace_app_name
|
20
21
|
end
|
@@ -27,12 +28,9 @@ module Sequent
|
|
27
28
|
|
28
29
|
def copy_files
|
29
30
|
FileUtils.copy_entry(File.expand_path('template_project', __dir__), path)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
# Therefor we need to rename the ruby-version to .ruby-version.
|
34
|
-
def rename_ruby_version
|
35
|
-
FileUtils.mv("#{path}/ruby-version", "#{path}/.ruby-version")
|
31
|
+
['.ruby-version', 'db'].each do |file|
|
32
|
+
FileUtils.copy_entry(File.expand_path("../../../#{file}", __dir__), "#{path}/#{file}")
|
33
|
+
end
|
36
34
|
end
|
37
35
|
|
38
36
|
def rename_app_file
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
2
|
-
ruby "3.0.0"
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
ruby file: '.ruby-version'
|
5
|
+
|
6
|
+
gem 'csv'
|
4
7
|
gem 'rake'
|
5
|
-
|
6
|
-
gem 'sequent', git: 'https://github.com/zilverline/sequent'
|
8
|
+
gem 'sequent', '>= 8'
|
7
9
|
|
8
10
|
group :test do
|
9
|
-
gem 'rspec'
|
10
11
|
gem 'database_cleaner'
|
12
|
+
gem 'rspec'
|
11
13
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
ENV['SEQUENT_ENV'] ||= 'development'
|
2
4
|
|
3
5
|
require './my_app'
|
@@ -5,8 +7,8 @@ require 'sequent/rake/migration_tasks'
|
|
5
7
|
|
6
8
|
Sequent::Rake::MigrationTasks.new.register_tasks!
|
7
9
|
|
8
|
-
task
|
10
|
+
task 'sequent:migrate:init' => [:db_connect]
|
9
11
|
|
10
|
-
task
|
12
|
+
task 'db_connect' do
|
11
13
|
Sequent::Support::Database.connect!(ENV['SEQUENT_ENV'])
|
12
14
|
end
|
@@ -1,13 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require './db/migrations'
|
2
4
|
|
3
5
|
Sequent.configure do |config|
|
4
6
|
config.migrations_class_name = 'Migrations'
|
5
|
-
|
6
|
-
config.command_handlers = [
|
7
|
-
PostCommandHandler.new
|
8
|
-
]
|
9
|
-
|
10
|
-
config.event_handlers = [
|
11
|
-
PostProjector.new
|
12
|
-
]
|
7
|
+
config.enable_autoregistration = true
|
13
8
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'sequent/migrations/projectors'
|
2
4
|
|
3
5
|
VIEW_SCHEMA_VERSION = 1
|
@@ -9,9 +11,7 @@ class Migrations < Sequent::Migrations::Projectors
|
|
9
11
|
|
10
12
|
def self.versions
|
11
13
|
{
|
12
|
-
'1' => [
|
13
|
-
PostProjector
|
14
|
-
]
|
14
|
+
'1' => [PostProjector],
|
15
15
|
}
|
16
16
|
end
|
17
17
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require_relative '../../../lib/post'
|
3
5
|
|
@@ -9,12 +11,17 @@ describe PostCommandHandler do
|
|
9
11
|
end
|
10
12
|
|
11
13
|
it 'creates a post' do
|
12
|
-
when_command AddPost.new(
|
14
|
+
when_command AddPost.new(
|
15
|
+
aggregate_id: aggregate_id,
|
16
|
+
author: 'ben',
|
17
|
+
title: 'My first blogpost',
|
18
|
+
content: 'Hello World!',
|
19
|
+
)
|
13
20
|
then_events(
|
14
21
|
PostAdded.new(aggregate_id: aggregate_id, sequence_number: 1),
|
15
22
|
PostAuthorChanged.new(aggregate_id: aggregate_id, sequence_number: 2, author: 'ben'),
|
16
23
|
PostTitleChanged,
|
17
|
-
PostContentChanged
|
24
|
+
PostContentChanged,
|
18
25
|
)
|
19
26
|
end
|
20
27
|
end
|