sequent 2.1.0 → 3.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequent +65 -0
  3. data/db/sequent_schema.rb +9 -8
  4. data/lib/sequent.rb +3 -0
  5. data/lib/sequent/configuration.rb +67 -0
  6. data/lib/sequent/core/aggregate_repository.rb +1 -1
  7. data/lib/sequent/core/core.rb +2 -2
  8. data/lib/sequent/core/event_store.rb +2 -2
  9. data/lib/sequent/core/helpers/attribute_support.rb +3 -0
  10. data/lib/sequent/core/helpers/self_applier.rb +1 -1
  11. data/lib/sequent/core/{record_sessions/active_record_session.rb → persistors/active_record_persistor.rb} +21 -19
  12. data/lib/sequent/core/persistors/persistor.rb +84 -0
  13. data/lib/sequent/core/persistors/persistors.rb +3 -0
  14. data/lib/sequent/core/{record_sessions/replay_events_session.rb → persistors/replay_optimized_postgres_persistor.rb} +16 -7
  15. data/lib/sequent/core/projector.rb +96 -0
  16. data/lib/sequent/generator.rb +2 -0
  17. data/lib/sequent/generator/aggregate.rb +71 -0
  18. data/lib/sequent/generator/project.rb +61 -0
  19. data/lib/sequent/generator/template_aggregate/template_aggregate.rb +4 -0
  20. data/lib/sequent/generator/template_aggregate/template_aggregate/commands.rb +2 -0
  21. data/lib/sequent/generator/template_aggregate/template_aggregate/events.rb +2 -0
  22. data/lib/sequent/generator/template_aggregate/template_aggregate/template_aggregate.rb +9 -0
  23. data/lib/sequent/generator/template_aggregate/template_aggregate/template_aggregate_command_handler.rb +5 -0
  24. data/lib/sequent/generator/template_project/Gemfile +11 -0
  25. data/lib/sequent/generator/template_project/Gemfile.lock +72 -0
  26. data/lib/sequent/generator/template_project/Rakefile +12 -0
  27. data/lib/sequent/generator/template_project/app/projectors/post_projector.rb +22 -0
  28. data/lib/sequent/generator/template_project/app/records/post_record.rb +2 -0
  29. data/lib/sequent/generator/template_project/config/initializers/sequent.rb +13 -0
  30. data/lib/sequent/generator/template_project/db/database.yml +17 -0
  31. data/lib/sequent/generator/template_project/db/migrations.rb +17 -0
  32. data/lib/sequent/generator/template_project/db/sequent_schema.rb +51 -0
  33. data/lib/sequent/generator/template_project/db/tables/post_records.sql +10 -0
  34. data/lib/sequent/generator/template_project/lib/post.rb +4 -0
  35. data/lib/sequent/generator/template_project/lib/post/commands.rb +4 -0
  36. data/lib/sequent/generator/template_project/lib/post/events.rb +14 -0
  37. data/lib/sequent/generator/template_project/lib/post/post.rb +24 -0
  38. data/lib/sequent/generator/template_project/lib/post/post_command_handler.rb +5 -0
  39. data/lib/sequent/generator/template_project/my_app.rb +11 -0
  40. data/lib/sequent/generator/template_project/spec/app/projectors/post_projector_spec.rb +32 -0
  41. data/lib/sequent/generator/template_project/spec/lib/post/post_command_handler_spec.rb +20 -0
  42. data/lib/sequent/generator/template_project/spec/spec_helper.rb +29 -0
  43. data/lib/sequent/migrations/migrate_events.rb +1 -0
  44. data/lib/sequent/migrations/migrations.rb +2 -0
  45. data/lib/sequent/migrations/projectors.rb +18 -0
  46. data/lib/sequent/migrations/view_schema.rb +364 -0
  47. data/lib/sequent/rake/migration_tasks.rb +109 -0
  48. data/lib/sequent/rake/tasks.rb +16 -0
  49. data/lib/sequent/sequent.rb +53 -13
  50. data/lib/sequent/support/database.rb +53 -8
  51. data/lib/sequent/util/printer.rb +16 -0
  52. data/lib/sequent/util/timer.rb +14 -0
  53. data/lib/sequent/util/util.rb +2 -0
  54. data/lib/version.rb +1 -1
  55. metadata +67 -14
  56. data/lib/sequent/core/base_event_handler.rb +0 -54
  57. 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,2 @@
1
+ require_relative './generator/project'
2
+ require_relative './generator/aggregate'
@@ -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,4 @@
1
+ require_relative 'template_aggregate/commands'
2
+ require_relative 'template_aggregate/events'
3
+ require_relative 'template_aggregate/template_aggregate'
4
+ require_relative 'template_aggregate/template_aggregate_command_handler'
@@ -0,0 +1,2 @@
1
+ class AddTemplateAggregate < Sequent::Command
2
+ end
@@ -0,0 +1,2 @@
1
+ class TemplateAggregateAdded < Sequent::Event
2
+ end
@@ -0,0 +1,9 @@
1
+ class TemplateAggregate < Sequent::AggregateRoot
2
+ def initialize(command)
3
+ super(command.aggregate_id)
4
+ apply TemplateAggregateAdded
5
+ end
6
+
7
+ on TemplateAggregateAdded do
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class TemplateAggregateCommandHandler < Sequent::CommandHandler
2
+ on AddTemplateAggregate do |command|
3
+ repository.add_aggregate TemplateAggregate.new(command)
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+ ruby "2.5.1"
3
+
4
+ gem 'rake'
5
+ # let's use the latest and greatest
6
+ gem 'sequent', git: 'https://github.com/zilverline/sequent', branch: 'sequent-30'
7
+
8
+ group :test do
9
+ gem 'rspec'
10
+ gem 'database_cleaner'
11
+ 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,2 @@
1
+ class PostRecord < ActiveRecord::Base
2
+ end
@@ -0,0 +1,13 @@
1
+ require './db/migrations'
2
+
3
+ Sequent.configure do |config|
4
+ config.migrations_class_name = 'Migrations'
5
+
6
+ config.command_handlers = [
7
+ PostCommandHandler.new
8
+ ]
9
+
10
+ config.event_handlers = [
11
+ PostProjector.new
12
+ ]
13
+ 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