sequent 2.1.0 → 3.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 +65 -0
- data/db/sequent_schema.rb +9 -8
- data/lib/sequent.rb +3 -0
- data/lib/sequent/configuration.rb +67 -0
- data/lib/sequent/core/aggregate_repository.rb +1 -1
- data/lib/sequent/core/core.rb +2 -2
- data/lib/sequent/core/event_store.rb +2 -2
- data/lib/sequent/core/helpers/attribute_support.rb +3 -0
- data/lib/sequent/core/helpers/self_applier.rb +1 -1
- data/lib/sequent/core/{record_sessions/active_record_session.rb → persistors/active_record_persistor.rb} +21 -19
- data/lib/sequent/core/persistors/persistor.rb +84 -0
- data/lib/sequent/core/persistors/persistors.rb +3 -0
- data/lib/sequent/core/{record_sessions/replay_events_session.rb → persistors/replay_optimized_postgres_persistor.rb} +16 -7
- data/lib/sequent/core/projector.rb +96 -0
- data/lib/sequent/generator.rb +2 -0
- data/lib/sequent/generator/aggregate.rb +71 -0
- data/lib/sequent/generator/project.rb +61 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate.rb +4 -0
- 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 +9 -0
- data/lib/sequent/generator/template_aggregate/template_aggregate/template_aggregate_command_handler.rb +5 -0
- data/lib/sequent/generator/template_project/Gemfile +11 -0
- data/lib/sequent/generator/template_project/Gemfile.lock +72 -0
- data/lib/sequent/generator/template_project/Rakefile +12 -0
- data/lib/sequent/generator/template_project/app/projectors/post_projector.rb +22 -0
- data/lib/sequent/generator/template_project/app/records/post_record.rb +2 -0
- data/lib/sequent/generator/template_project/config/initializers/sequent.rb +13 -0
- data/lib/sequent/generator/template_project/db/database.yml +17 -0
- data/lib/sequent/generator/template_project/db/migrations.rb +17 -0
- data/lib/sequent/generator/template_project/db/sequent_schema.rb +51 -0
- data/lib/sequent/generator/template_project/db/tables/post_records.sql +10 -0
- data/lib/sequent/generator/template_project/lib/post.rb +4 -0
- data/lib/sequent/generator/template_project/lib/post/commands.rb +4 -0
- data/lib/sequent/generator/template_project/lib/post/events.rb +14 -0
- data/lib/sequent/generator/template_project/lib/post/post.rb +24 -0
- data/lib/sequent/generator/template_project/lib/post/post_command_handler.rb +5 -0
- data/lib/sequent/generator/template_project/my_app.rb +11 -0
- data/lib/sequent/generator/template_project/spec/app/projectors/post_projector_spec.rb +32 -0
- data/lib/sequent/generator/template_project/spec/lib/post/post_command_handler_spec.rb +20 -0
- data/lib/sequent/generator/template_project/spec/spec_helper.rb +29 -0
- data/lib/sequent/migrations/migrate_events.rb +1 -0
- data/lib/sequent/migrations/migrations.rb +2 -0
- data/lib/sequent/migrations/projectors.rb +18 -0
- data/lib/sequent/migrations/view_schema.rb +364 -0
- data/lib/sequent/rake/migration_tasks.rb +109 -0
- data/lib/sequent/rake/tasks.rb +16 -0
- data/lib/sequent/sequent.rb +53 -13
- data/lib/sequent/support/database.rb +53 -8
- data/lib/sequent/util/printer.rb +16 -0
- data/lib/sequent/util/timer.rb +14 -0
- data/lib/sequent/util/util.rb +2 -0
- data/lib/version.rb +1 -1
- metadata +67 -14
- data/lib/sequent/core/base_event_handler.rb +0 -54
- data/lib/sequent/core/record_sessions/record_sessions.rb +0 -2
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'helpers/self_applier'
|
2
|
+
require_relative './persistors/active_record_persistor'
|
3
|
+
|
4
|
+
module Sequent
|
5
|
+
module Core
|
6
|
+
|
7
|
+
module Migratable
|
8
|
+
module ClassMethods
|
9
|
+
def manages_tables(*tables)
|
10
|
+
@managed_tables = tables
|
11
|
+
end
|
12
|
+
|
13
|
+
def managed_tables
|
14
|
+
@managed_tables
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.projectors
|
19
|
+
Sequent.configuration.event_handlers.select { |x| x.is_a? Migratable }.map(&:class)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(host_class)
|
23
|
+
host_class.extend(ClassMethods)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.none
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.all
|
31
|
+
Migratable.projectors
|
32
|
+
end
|
33
|
+
|
34
|
+
def managed_tables
|
35
|
+
self.class.managed_tables
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# Projectors listen to events and update the view state as they see fit.
|
41
|
+
#
|
42
|
+
# Example of updating view state, in this case the InvoiceRecord table representing an Invoice
|
43
|
+
#
|
44
|
+
# class InvoiceProjector < Sequent::Core::Projector
|
45
|
+
# on InvoiceCreated do |event|
|
46
|
+
# create_record(
|
47
|
+
# InvoiceRecord,
|
48
|
+
# recipient: event.recipient,
|
49
|
+
# amount: event.amount
|
50
|
+
# )
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Please note that the actual storage is abstracted away in the +persistors+.
|
55
|
+
# Due to this abstraction you can not traverse persist or traverse child objects
|
56
|
+
# like you are used to do with ActiveRecord. The following example will not work:
|
57
|
+
#
|
58
|
+
# invoice_record.line_item_records << create_record(LineItemRecord, ...)
|
59
|
+
#
|
60
|
+
# In this case you should simply do:
|
61
|
+
#
|
62
|
+
# create_record(LineItemRecord, invoice_id: invoice_record.aggregate_id)
|
63
|
+
#
|
64
|
+
class Projector
|
65
|
+
extend Forwardable
|
66
|
+
include Helpers::SelfApplier
|
67
|
+
include Migratable
|
68
|
+
|
69
|
+
def initialize(persistor = Sequent::Core::Persistors::ActiveRecordPersistor.new)
|
70
|
+
@persistor = persistor
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.replay_persistor
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def_delegators :@persistor,
|
78
|
+
:update_record,
|
79
|
+
:create_record,
|
80
|
+
:create_records,
|
81
|
+
:create_or_update_record,
|
82
|
+
:get_record!,
|
83
|
+
:get_record,
|
84
|
+
:delete_all_records,
|
85
|
+
:update_all_records,
|
86
|
+
:do_with_records,
|
87
|
+
:do_with_record,
|
88
|
+
:delete_record,
|
89
|
+
:find_records,
|
90
|
+
:last_record,
|
91
|
+
:execute_sql,
|
92
|
+
:commit
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/core_ext/string'
|
4
|
+
|
5
|
+
class TargetAlreadyExists < StandardError; end
|
6
|
+
|
7
|
+
module Sequent
|
8
|
+
module Generator
|
9
|
+
class Aggregate
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
def initialize(name)
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
ensure_not_used!
|
18
|
+
copy_files
|
19
|
+
rename_files
|
20
|
+
replace_template_aggregate
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def copy_files
|
26
|
+
FileUtils.copy_entry(File.expand_path('template_aggregate', __dir__), path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def rename_files
|
30
|
+
FileUtils.mv("#{path}/template_aggregate.rb", "#{path}/#{name_underscored}.rb")
|
31
|
+
FileUtils.mv("#{path}/template_aggregate", "#{path}/#{name_underscored}")
|
32
|
+
|
33
|
+
FileUtils.mv("#{path}/#{name_underscored}/template_aggregate_command_handler.rb", "#{path}/#{name_underscored}/#{name_underscored}_command_handler.rb")
|
34
|
+
FileUtils.mv("#{path}/#{name_underscored}/template_aggregate.rb", "#{path}/#{name_underscored}/#{name_underscored}.rb")
|
35
|
+
end
|
36
|
+
|
37
|
+
def replace_template_aggregate
|
38
|
+
files = Dir["#{path}/**/*"].select { |f| File.file?(f) }
|
39
|
+
|
40
|
+
files.each do |filename|
|
41
|
+
contents = File.read(filename)
|
42
|
+
contents.gsub!('template_aggregate', name_underscored)
|
43
|
+
contents.gsub!('TemplateAggregate', name_camelized)
|
44
|
+
File.open(filename, 'w') { |f| f.puts contents }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def path
|
49
|
+
@path ||= File.expand_path("lib")
|
50
|
+
end
|
51
|
+
|
52
|
+
def name
|
53
|
+
@name ||= File.basename(path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def name_underscored
|
57
|
+
@name_underscored ||= name.underscore
|
58
|
+
end
|
59
|
+
|
60
|
+
def name_camelized
|
61
|
+
@name_camelized ||= name.camelize
|
62
|
+
end
|
63
|
+
|
64
|
+
def ensure_not_used!
|
65
|
+
if File.directory?("#{path}/#{name_underscored}") || File.exist?("#{path}/#{name_underscored}.rb")
|
66
|
+
raise TargetAlreadyExists
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/core_ext/string'
|
4
|
+
|
5
|
+
module Sequent
|
6
|
+
module Generator
|
7
|
+
class Project
|
8
|
+
def initialize(path_or_name)
|
9
|
+
@path_or_name = path_or_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
make_directory
|
14
|
+
copy_files
|
15
|
+
rename_app_file
|
16
|
+
replace_app_name
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def make_directory
|
22
|
+
FileUtils.mkdir_p(path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def copy_files
|
26
|
+
FileUtils.copy_entry(File.expand_path('template_project', __dir__), path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def rename_app_file
|
30
|
+
FileUtils.mv("#{path}/my_app.rb", "#{path}/#{name_underscored}.rb")
|
31
|
+
end
|
32
|
+
|
33
|
+
def replace_app_name
|
34
|
+
files = Dir["#{path}/**/*"].select { |f| File.file?(f) }
|
35
|
+
|
36
|
+
files.each do |filename|
|
37
|
+
contents = File.read(filename)
|
38
|
+
contents.gsub!('my_app', name_underscored)
|
39
|
+
contents.gsub!('MyApp', name_camelized)
|
40
|
+
File.open(filename, 'w') { |f| f.puts contents }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def path
|
45
|
+
@path ||= File.expand_path(@path_or_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def name
|
49
|
+
@name ||= File.basename(path)
|
50
|
+
end
|
51
|
+
|
52
|
+
def name_underscored
|
53
|
+
@name_underscored ||= name.underscore
|
54
|
+
end
|
55
|
+
|
56
|
+
def name_camelized
|
57
|
+
@name_camelized ||= name.camelize
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
GIT
|
2
|
+
remote: https://github.com/zilverline/sequent
|
3
|
+
revision: 1e1066279c49d58adbb24f1a1d0e0a7e7bd10a55
|
4
|
+
branch: sequent-30
|
5
|
+
specs:
|
6
|
+
sequent (2.0.0)
|
7
|
+
activemodel (>= 5.0, < 5.2)
|
8
|
+
activerecord (>= 5.0, < 5.2)
|
9
|
+
oj (~> 3.3.9)
|
10
|
+
parallel (~> 1.12.1)
|
11
|
+
pg (~> 1.0)
|
12
|
+
postgresql_cursor (~> 0.6)
|
13
|
+
thread_safe (~> 0.3.5)
|
14
|
+
|
15
|
+
GEM
|
16
|
+
remote: https://rubygems.org/
|
17
|
+
specs:
|
18
|
+
activemodel (5.1.6)
|
19
|
+
activesupport (= 5.1.6)
|
20
|
+
activerecord (5.1.6)
|
21
|
+
activemodel (= 5.1.6)
|
22
|
+
activesupport (= 5.1.6)
|
23
|
+
arel (~> 8.0)
|
24
|
+
activesupport (5.1.6)
|
25
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
26
|
+
i18n (>= 0.7, < 2)
|
27
|
+
minitest (~> 5.1)
|
28
|
+
tzinfo (~> 1.1)
|
29
|
+
arel (8.0.0)
|
30
|
+
concurrent-ruby (1.0.5)
|
31
|
+
database_cleaner (1.7.0)
|
32
|
+
diff-lcs (1.3)
|
33
|
+
i18n (1.0.1)
|
34
|
+
concurrent-ruby (~> 1.0)
|
35
|
+
minitest (5.11.3)
|
36
|
+
oj (3.3.10)
|
37
|
+
parallel (1.12.1)
|
38
|
+
pg (1.0.0)
|
39
|
+
postgresql_cursor (0.6.1)
|
40
|
+
activerecord (>= 3.1.0)
|
41
|
+
rake (12.3.1)
|
42
|
+
rspec (3.7.0)
|
43
|
+
rspec-core (~> 3.7.0)
|
44
|
+
rspec-expectations (~> 3.7.0)
|
45
|
+
rspec-mocks (~> 3.7.0)
|
46
|
+
rspec-core (3.7.1)
|
47
|
+
rspec-support (~> 3.7.0)
|
48
|
+
rspec-expectations (3.7.0)
|
49
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
+
rspec-support (~> 3.7.0)
|
51
|
+
rspec-mocks (3.7.0)
|
52
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
53
|
+
rspec-support (~> 3.7.0)
|
54
|
+
rspec-support (3.7.1)
|
55
|
+
thread_safe (0.3.6)
|
56
|
+
tzinfo (1.2.5)
|
57
|
+
thread_safe (~> 0.1)
|
58
|
+
|
59
|
+
PLATFORMS
|
60
|
+
ruby
|
61
|
+
|
62
|
+
DEPENDENCIES
|
63
|
+
database_cleaner
|
64
|
+
rake
|
65
|
+
rspec
|
66
|
+
sequent!
|
67
|
+
|
68
|
+
RUBY VERSION
|
69
|
+
ruby 2.5.1p57
|
70
|
+
|
71
|
+
BUNDLED WITH
|
72
|
+
1.16.2
|
@@ -0,0 +1,12 @@
|
|
1
|
+
ENV['RACK_ENV'] ||= 'development'
|
2
|
+
|
3
|
+
require './my_app'
|
4
|
+
require 'sequent/rake/migration_tasks'
|
5
|
+
|
6
|
+
Sequent::Rake::MigrationTasks.new.register_tasks!
|
7
|
+
|
8
|
+
task "sequent:migrate:init" => [:db_connect]
|
9
|
+
|
10
|
+
task "db_connect" do
|
11
|
+
Sequent::Support::Database.connect!(ENV['RACK_ENV'])
|
12
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative '../records/post_record'
|
2
|
+
require_relative '../../lib/post/events'
|
3
|
+
|
4
|
+
class PostProjector < Sequent::Projector
|
5
|
+
manages_tables PostRecord
|
6
|
+
|
7
|
+
on PostAdded do |event|
|
8
|
+
create_record(PostRecord, aggregate_id: event.aggregate_id)
|
9
|
+
end
|
10
|
+
|
11
|
+
on PostAuthorChanged do |event|
|
12
|
+
update_all_records(PostRecord, {aggregate_id: event.aggregate_id}, event.attributes.slice(:author))
|
13
|
+
end
|
14
|
+
|
15
|
+
on PostTitleChanged do |event|
|
16
|
+
update_all_records(PostRecord, {aggregate_id: event.aggregate_id}, event.attributes.slice(:title))
|
17
|
+
end
|
18
|
+
|
19
|
+
on PostContentChanged do |event|
|
20
|
+
update_all_records(PostRecord, {aggregate_id: event.aggregate_id}, event.attributes.slice(:content))
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
database: &database
|
2
|
+
adapter: postgresql
|
3
|
+
host: localhost
|
4
|
+
port: 5432
|
5
|
+
timeout: 5000
|
6
|
+
schema_search_path: "sequent_schema, view_schema"
|
7
|
+
|
8
|
+
development:
|
9
|
+
<<: *database
|
10
|
+
pool: 5
|
11
|
+
database: my_app_development
|
12
|
+
|
13
|
+
test:
|
14
|
+
<<: *database
|
15
|
+
pool: 5
|
16
|
+
database: my_app_test
|
17
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'sequent/migrations/projectors'
|
2
|
+
|
3
|
+
VIEW_SCHEMA_VERSION = 1
|
4
|
+
|
5
|
+
class Migrations < Sequent::Migrations::Projectors
|
6
|
+
def self.version
|
7
|
+
VIEW_SCHEMA_VERSION
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.versions
|
11
|
+
{
|
12
|
+
'1' => [
|
13
|
+
PostProjector
|
14
|
+
]
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|