sequent 4.0.0 → 4.3.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 +33 -26
- 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 +48 -13
- data/lib/sequent/core/aggregate_root.rb +36 -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 +89 -51
- 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 +53 -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 +30 -9
- data/lib/sequent/core/transactions/no_transactions.rb +2 -1
- data/lib/sequent/core/transactions/read_only_active_record_transaction_provider.rb +46 -0
- data/lib/sequent/core/transactions/transactions.rb +3 -0
- data/lib/sequent/core/value_object.rb +8 -10
- data/lib/sequent/core/workflow.rb +35 -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 +9 -0
- data/lib/sequent/generator/template_project/Gemfile +1 -1
- data/lib/sequent/generator/template_project/ruby-version +1 -0
- 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 +28 -20
- 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 +84 -67
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_record'
|
|
2
4
|
|
|
3
5
|
module Sequent
|
|
@@ -14,8 +16,7 @@ module Sequent
|
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
class StreamRecord < Sequent::ApplicationRecord
|
|
17
|
-
|
|
18
|
-
self.table_name = "stream_records"
|
|
19
|
+
self.table_name = 'stream_records'
|
|
19
20
|
|
|
20
21
|
validates_presence_of :aggregate_type, :aggregate_id
|
|
21
22
|
validates_numericality_of :snapshot_threshold, only_integer: true, greater_than: 0, allow_nil: true
|
|
@@ -23,7 +24,12 @@ module Sequent
|
|
|
23
24
|
has_many :event_records
|
|
24
25
|
|
|
25
26
|
def event_stream
|
|
26
|
-
EventStream.new(
|
|
27
|
+
EventStream.new(
|
|
28
|
+
aggregate_type: aggregate_type,
|
|
29
|
+
aggregate_id: aggregate_id,
|
|
30
|
+
snapshot_threshold: snapshot_threshold,
|
|
31
|
+
stream_record_id: id,
|
|
32
|
+
)
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def event_stream=(data)
|
|
@@ -1,15 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Core
|
|
3
5
|
module Transactions
|
|
4
|
-
|
|
6
|
+
##
|
|
7
|
+
# Always require a new transaction.
|
|
8
|
+
#
|
|
9
|
+
# Read:
|
|
10
|
+
# http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
|
|
11
|
+
#
|
|
12
|
+
# Without this change, there is a potential bug:
|
|
13
|
+
#
|
|
14
|
+
# ```ruby
|
|
15
|
+
# ActiveRecord::Base.transaction do
|
|
16
|
+
# Sequent.configuration.command_service.execute_commands command
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# on Command do
|
|
20
|
+
# do.some.things
|
|
21
|
+
# fail ActiveRecord::Rollback
|
|
22
|
+
# end
|
|
23
|
+
# ```
|
|
24
|
+
#
|
|
25
|
+
# In this example, you might be surprised to find that `do.some.things`
|
|
26
|
+
# does not get rolled back! This is because AR doesn't automatically make
|
|
27
|
+
# a "savepoint" for us when we call `.transaction` in a nested manner. In
|
|
28
|
+
# order to enable this behaviour, we have to call `.transaction` like
|
|
29
|
+
# this: `.transaction(requires_new: true)`.
|
|
30
|
+
#
|
|
5
31
|
class ActiveRecordTransactionProvider
|
|
6
|
-
def transactional
|
|
7
|
-
Sequent::ApplicationRecord.transaction(requires_new: true)
|
|
8
|
-
|
|
9
|
-
end
|
|
10
|
-
while(!after_commit_queue.empty?) do
|
|
11
|
-
after_commit_queue.pop.call
|
|
12
|
-
end
|
|
32
|
+
def transactional(&block)
|
|
33
|
+
Sequent::ApplicationRecord.transaction(requires_new: true, &block)
|
|
34
|
+
after_commit_queue.pop.call until after_commit_queue.empty?
|
|
13
35
|
ensure
|
|
14
36
|
clear_after_commit_queue
|
|
15
37
|
end
|
|
@@ -28,7 +50,6 @@ module Sequent
|
|
|
28
50
|
after_commit_queue.clear
|
|
29
51
|
end
|
|
30
52
|
end
|
|
31
|
-
|
|
32
53
|
end
|
|
33
54
|
end
|
|
34
55
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sequent
|
|
4
|
+
module Core
|
|
5
|
+
module Transactions
|
|
6
|
+
class ReadOnlyActiveRecordTransactionProvider
|
|
7
|
+
def initialize(transaction_provider)
|
|
8
|
+
@transaction_provider = transaction_provider
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def transactional(&block)
|
|
12
|
+
register_call
|
|
13
|
+
@transaction_provider.transactional do
|
|
14
|
+
Sequent::ApplicationRecord.connection.execute('SET TRANSACTION READ ONLY')
|
|
15
|
+
block.call
|
|
16
|
+
rescue ActiveRecord::StatementInvalid
|
|
17
|
+
@skip_set_transaction = true
|
|
18
|
+
raise
|
|
19
|
+
ensure
|
|
20
|
+
deregister_call
|
|
21
|
+
reset_stack_size if stack_size == 0
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def stack_size
|
|
28
|
+
Thread.current[:read_only_active_record_transaction_provider_calls]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def register_call
|
|
32
|
+
Thread.current[:read_only_active_record_transaction_provider_calls] ||= 0
|
|
33
|
+
Thread.current[:read_only_active_record_transaction_provider_calls] += 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def deregister_call
|
|
37
|
+
Thread.current[:read_only_active_record_transaction_provider_calls] -= 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def reset_stack_size
|
|
41
|
+
Thread.current[:read_only_active_record_transaction_provider_calls] = nil
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_model'
|
|
2
4
|
require_relative 'helpers/string_support'
|
|
3
5
|
require_relative 'helpers/equal_support'
|
|
@@ -6,7 +8,6 @@ require_relative 'helpers/attribute_support'
|
|
|
6
8
|
require_relative 'helpers/param_support'
|
|
7
9
|
|
|
8
10
|
module Sequent
|
|
9
|
-
|
|
10
11
|
module Core
|
|
11
12
|
#
|
|
12
13
|
# ValueObject is a container for data that belongs together but requires no identity
|
|
@@ -26,20 +27,17 @@ module Sequent
|
|
|
26
27
|
#
|
|
27
28
|
# This a deep clone of the address with the street attribute containing "New Street"
|
|
28
29
|
class ValueObject
|
|
29
|
-
include
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
include ActiveModel::Validations
|
|
31
|
+
include Sequent::Core::Helpers::ParamSupport
|
|
32
|
+
include Sequent::Core::Helpers::AttributeSupport
|
|
33
|
+
include Sequent::Core::Helpers::Copyable
|
|
34
|
+
include Sequent::Core::Helpers::EqualSupport
|
|
35
|
+
include Sequent::Core::Helpers::StringSupport
|
|
35
36
|
include Sequent::Core::Helpers::TypeConversionSupport
|
|
36
37
|
|
|
37
38
|
def initialize(args = {})
|
|
38
39
|
update_all_attributes args
|
|
39
40
|
end
|
|
40
|
-
|
|
41
41
|
end
|
|
42
|
-
|
|
43
42
|
end
|
|
44
43
|
end
|
|
45
|
-
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'helpers/message_handler'
|
|
2
4
|
require_relative 'current_event'
|
|
3
5
|
|
|
@@ -6,12 +8,17 @@ module Sequent
|
|
|
6
8
|
class Workflow
|
|
7
9
|
include Helpers::MessageHandler
|
|
8
10
|
|
|
11
|
+
def self.inherited(subclass)
|
|
12
|
+
super
|
|
13
|
+
Workflows << subclass
|
|
14
|
+
end
|
|
15
|
+
|
|
9
16
|
def self.on(*message_classes, &block)
|
|
10
17
|
decorated_block = ->(event) do
|
|
11
18
|
begin
|
|
12
19
|
old_event = CurrentEvent.current
|
|
13
20
|
CurrentEvent.current = event
|
|
14
|
-
|
|
21
|
+
instance_exec(event, &block)
|
|
15
22
|
ensure
|
|
16
23
|
CurrentEvent.current = old_event
|
|
17
24
|
end
|
|
@@ -31,12 +38,35 @@ module Sequent
|
|
|
31
38
|
# jobs processor is not using the same database connection
|
|
32
39
|
# to enqueue jobs.
|
|
33
40
|
def after_commit(ignore_errors: false, &block)
|
|
34
|
-
Sequent.configuration.transaction_provider.after_commit
|
|
35
|
-
rescue StandardError =>
|
|
41
|
+
Sequent.configuration.transaction_provider.after_commit(&block)
|
|
42
|
+
rescue StandardError => e
|
|
36
43
|
if ignore_errors
|
|
37
|
-
Sequent.logger.warn("An exception was raised in an after_commit hook: #{
|
|
44
|
+
Sequent.logger.warn("An exception was raised in an after_commit hook: #{e}, #{e.inspect}")
|
|
38
45
|
else
|
|
39
|
-
raise
|
|
46
|
+
raise e
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
#
|
|
52
|
+
# Utility class containing all subclasses of Workflow
|
|
53
|
+
#
|
|
54
|
+
class Workflows
|
|
55
|
+
class << self
|
|
56
|
+
def workflows
|
|
57
|
+
@workflows ||= []
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def all
|
|
61
|
+
workflows
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def <<(workflow)
|
|
65
|
+
workflows << workflow
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def find(workflow_name)
|
|
69
|
+
workflows.find { |c| c.name == workflow_name }
|
|
40
70
|
end
|
|
41
71
|
end
|
|
42
72
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'fileutils'
|
|
2
4
|
require 'active_support'
|
|
3
5
|
require 'active_support/core_ext/string'
|
|
@@ -7,8 +9,6 @@ class TargetAlreadyExists < StandardError; end
|
|
|
7
9
|
module Sequent
|
|
8
10
|
module Generator
|
|
9
11
|
class Aggregate
|
|
10
|
-
attr_reader :name
|
|
11
|
-
|
|
12
12
|
def initialize(name)
|
|
13
13
|
@name = name
|
|
14
14
|
end
|
|
@@ -20,6 +20,10 @@ module Sequent
|
|
|
20
20
|
replace_template_aggregate
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
def name
|
|
24
|
+
@name ||= File.basename(path)
|
|
25
|
+
end
|
|
26
|
+
|
|
23
27
|
private
|
|
24
28
|
|
|
25
29
|
def copy_files
|
|
@@ -30,8 +34,14 @@ module Sequent
|
|
|
30
34
|
FileUtils.mv("#{path}/template_aggregate.rb", "#{path}/#{name_underscored}.rb")
|
|
31
35
|
FileUtils.mv("#{path}/template_aggregate", "#{path}/#{name_underscored}")
|
|
32
36
|
|
|
33
|
-
FileUtils.mv(
|
|
34
|
-
|
|
37
|
+
FileUtils.mv(
|
|
38
|
+
"#{path}/#{name_underscored}/template_aggregate_command_handler.rb",
|
|
39
|
+
"#{path}/#{name_underscored}/#{name_underscored}_command_handler.rb",
|
|
40
|
+
)
|
|
41
|
+
FileUtils.mv(
|
|
42
|
+
"#{path}/#{name_underscored}/template_aggregate.rb",
|
|
43
|
+
"#{path}/#{name_underscored}/#{name_underscored}.rb",
|
|
44
|
+
)
|
|
35
45
|
end
|
|
36
46
|
|
|
37
47
|
def replace_template_aggregate
|
|
@@ -46,11 +56,7 @@ module Sequent
|
|
|
46
56
|
end
|
|
47
57
|
|
|
48
58
|
def path
|
|
49
|
-
@path ||= File.expand_path(
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def name
|
|
53
|
-
@name ||= File.basename(path)
|
|
59
|
+
@path ||= File.expand_path('lib')
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
def name_underscored
|
|
@@ -63,7 +69,7 @@ module Sequent
|
|
|
63
69
|
|
|
64
70
|
def ensure_not_used!
|
|
65
71
|
if File.directory?("#{path}/#{name_underscored}") || File.exist?("#{path}/#{name_underscored}.rb")
|
|
66
|
-
|
|
72
|
+
fail TargetAlreadyExists
|
|
67
73
|
end
|
|
68
74
|
end
|
|
69
75
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'fileutils'
|
|
2
4
|
require 'active_support'
|
|
3
5
|
require 'active_support/core_ext/string'
|
|
@@ -9,12 +11,12 @@ class NoAggregateFound < StandardError; end
|
|
|
9
11
|
module Sequent
|
|
10
12
|
module Generator
|
|
11
13
|
class Command
|
|
12
|
-
attr_reader :
|
|
14
|
+
attr_reader :command, :attrs
|
|
13
15
|
|
|
14
16
|
def initialize(name, command, attrs)
|
|
15
17
|
@name = name
|
|
16
18
|
@command = command
|
|
17
|
-
@attrs = attrs.map{|a| a.split(':')}
|
|
19
|
+
@attrs = attrs.map { |a| a.split(':') }
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
def execute
|
|
@@ -22,32 +24,39 @@ module Sequent
|
|
|
22
24
|
add_command_to_aggregate
|
|
23
25
|
end
|
|
24
26
|
|
|
27
|
+
def name
|
|
28
|
+
@name ||= File.basename(path)
|
|
29
|
+
end
|
|
30
|
+
|
|
25
31
|
private
|
|
32
|
+
|
|
26
33
|
def append_command_handler
|
|
27
34
|
ast = Parser::CurrentRuby.parse(File.read("#{path_to_dir}/#{name_underscored}_command_handler.rb"))
|
|
28
35
|
target_cursor_position = find_target_cursor_position(ast)
|
|
29
|
-
|
|
36
|
+
|
|
30
37
|
File.open("#{path_to_dir}/#{name_underscored}_command_handler.rb", 'r+') do |f|
|
|
31
38
|
f.seek(target_cursor_position, IO::SEEK_SET)
|
|
32
39
|
lines_to_be_overwritten = f.read
|
|
33
40
|
f.seek(target_cursor_position, IO::SEEK_SET)
|
|
34
|
-
f << command_handler_template.result(binding).gsub(/^.+(\s)$/) { |x| x.gsub!(
|
|
41
|
+
f << command_handler_template.result(binding).gsub(/^.+(\s)$/) { |x| x.gsub!(Regexp.last_match(1), '') }
|
|
35
42
|
f << lines_to_be_overwritten
|
|
36
43
|
end
|
|
37
44
|
end
|
|
38
45
|
|
|
39
46
|
def find_target_cursor_position(ast)
|
|
40
47
|
return unless ast.children.any?
|
|
48
|
+
return if ast.children.any? { |c| c.class.to_s != 'Parser::AST::Node' }
|
|
49
|
+
if (child = ast.children.find { |c| c.type.to_s == 'block' })
|
|
50
|
+
return child.loc.expression.end_pos
|
|
51
|
+
end
|
|
41
52
|
|
|
42
|
-
ast.children.map do |
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
find_target_cursor_position(child)
|
|
46
|
-
end.flatten.compact.max
|
|
53
|
+
ast.children.map do |c|
|
|
54
|
+
find_target_cursor_position(c)
|
|
55
|
+
end&.flatten&.compact&.max
|
|
47
56
|
end
|
|
48
57
|
|
|
49
58
|
def append_command
|
|
50
|
-
File.open("#{path_to_dir}/commands.rb",
|
|
59
|
+
File.open("#{path_to_dir}/commands.rb", 'a') { |f| f << command_template.result(binding) }
|
|
51
60
|
end
|
|
52
61
|
|
|
53
62
|
def add_command_to_aggregate
|
|
@@ -56,11 +65,7 @@ module Sequent
|
|
|
56
65
|
end
|
|
57
66
|
|
|
58
67
|
def path
|
|
59
|
-
@path ||= File.expand_path(
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def name
|
|
63
|
-
@name ||= File.basename(path)
|
|
68
|
+
@path ||= File.expand_path('lib')
|
|
64
69
|
end
|
|
65
70
|
|
|
66
71
|
def name_underscored
|
|
@@ -72,17 +77,19 @@ module Sequent
|
|
|
72
77
|
end
|
|
73
78
|
|
|
74
79
|
def ensure_existing_aggregate!
|
|
75
|
-
if !File.directory?(path_to_dir) ||
|
|
76
|
-
|
|
80
|
+
if !File.directory?(path_to_dir) ||
|
|
81
|
+
!File.exist?("#{path_to_dir}/#{name_underscored}_command_handler.rb") ||
|
|
82
|
+
!File.exist?("#{path_to_dir}/commands.rb")
|
|
83
|
+
fail NoAggregateFound
|
|
77
84
|
end
|
|
78
85
|
end
|
|
79
86
|
|
|
80
87
|
def command_template
|
|
81
|
-
ERB.new(File.read(File.join(
|
|
88
|
+
ERB.new(File.read(File.join(File.dirname(__FILE__), 'template_command.erb')))
|
|
82
89
|
end
|
|
83
90
|
|
|
84
91
|
def command_handler_template
|
|
85
|
-
ERB.new(File.read(File.join(
|
|
92
|
+
ERB.new(File.read(File.join(File.dirname(__FILE__), 'template_command_handler.erb')))
|
|
86
93
|
end
|
|
87
94
|
end
|
|
88
95
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'fileutils'
|
|
2
4
|
require 'active_support'
|
|
3
5
|
require 'active_support/core_ext/string'
|
|
@@ -7,12 +9,16 @@ class NoAggregateFound < StandardError; end
|
|
|
7
9
|
module Sequent
|
|
8
10
|
module Generator
|
|
9
11
|
class Event
|
|
10
|
-
attr_reader :
|
|
12
|
+
attr_reader :event, :attrs
|
|
11
13
|
|
|
12
14
|
def initialize(name, event, attrs)
|
|
13
15
|
@name = name
|
|
14
16
|
@event = event
|
|
15
|
-
@attrs = attrs.map{|a| a.split(':')}
|
|
17
|
+
@attrs = attrs.map { |a| a.split(':') }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def name
|
|
21
|
+
@name ||= File.basename(path)
|
|
16
22
|
end
|
|
17
23
|
|
|
18
24
|
def execute
|
|
@@ -23,29 +29,31 @@ module Sequent
|
|
|
23
29
|
private
|
|
24
30
|
|
|
25
31
|
def append_event
|
|
26
|
-
File.open("#{path_to_dir}/events.rb",
|
|
32
|
+
File.open("#{path_to_dir}/events.rb", 'a') { |f| f << event_template.result(binding) }
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def append_event_to_domain
|
|
30
36
|
ast = Parser::CurrentRuby.parse(File.read("#{path_to_dir}/#{name_underscored}.rb"))
|
|
31
37
|
target_cursor_position = find_target_cursor_position(ast)
|
|
32
|
-
|
|
38
|
+
|
|
33
39
|
File.open("#{path_to_dir}/#{name_underscored}.rb", 'r+') do |f|
|
|
34
40
|
f.seek(target_cursor_position, IO::SEEK_SET)
|
|
35
41
|
lines_to_be_overwritten = f.read
|
|
36
42
|
f.seek(target_cursor_position, IO::SEEK_SET)
|
|
37
|
-
f << event_handler_template.result(binding).gsub(/^.+(\s)$/) { |x| x.gsub!(
|
|
43
|
+
f << event_handler_template.result(binding).gsub(/^.+(\s)$/) { |x| x.gsub!(Regexp.last_match(1), '') }
|
|
38
44
|
f << lines_to_be_overwritten
|
|
39
45
|
end
|
|
40
46
|
end
|
|
41
47
|
|
|
42
48
|
def find_target_cursor_position(ast)
|
|
43
49
|
return unless ast.children.any?
|
|
50
|
+
return if ast.children.any? { |c| c.class.to_s != 'Parser::AST::Node' }
|
|
51
|
+
if (child = ast.children.find { |c| c.type.to_s == 'block' })
|
|
52
|
+
return child.loc.expression.end_pos
|
|
53
|
+
end
|
|
44
54
|
|
|
45
|
-
ast.children.reverse.map do |
|
|
46
|
-
|
|
47
|
-
return child.loc.expression.end_pos if child.type.to_s == 'block'
|
|
48
|
-
find_target_cursor_position(child)
|
|
55
|
+
ast.children.reverse.map do |c|
|
|
56
|
+
find_target_cursor_position(c)
|
|
49
57
|
end.flatten.compact.max
|
|
50
58
|
end
|
|
51
59
|
|
|
@@ -55,11 +63,7 @@ module Sequent
|
|
|
55
63
|
end
|
|
56
64
|
|
|
57
65
|
def path
|
|
58
|
-
@path ||= File.expand_path(
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def name
|
|
62
|
-
@name ||= File.basename(path)
|
|
66
|
+
@path ||= File.expand_path('lib')
|
|
63
67
|
end
|
|
64
68
|
|
|
65
69
|
def name_underscored
|
|
@@ -71,9 +75,7 @@ module Sequent
|
|
|
71
75
|
end
|
|
72
76
|
|
|
73
77
|
def ensure_existing_aggregate!
|
|
74
|
-
if !File.directory?(path_to_dir) || !File.exist?("#{path_to_dir}/#{name_underscored}.rb")
|
|
75
|
-
raise NoAggregateFound
|
|
76
|
-
end
|
|
78
|
+
fail NoAggregateFound if !File.directory?(path_to_dir) || !File.exist?("#{path_to_dir}/#{name_underscored}.rb")
|
|
77
79
|
end
|
|
78
80
|
|
|
79
81
|
def event_template
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'fileutils'
|
|
2
4
|
require 'active_support'
|
|
3
5
|
require 'active_support/core_ext/string'
|
|
@@ -12,6 +14,7 @@ module Sequent
|
|
|
12
14
|
def execute
|
|
13
15
|
make_directory
|
|
14
16
|
copy_files
|
|
17
|
+
rename_ruby_version
|
|
15
18
|
rename_app_file
|
|
16
19
|
replace_app_name
|
|
17
20
|
end
|
|
@@ -26,6 +29,12 @@ module Sequent
|
|
|
26
29
|
FileUtils.copy_entry(File.expand_path('template_project', __dir__), path)
|
|
27
30
|
end
|
|
28
31
|
|
|
32
|
+
# Hidden files are by default excluded from gem build.
|
|
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")
|
|
36
|
+
end
|
|
37
|
+
|
|
29
38
|
def rename_app_file
|
|
30
39
|
FileUtils.mv("#{path}/my_app.rb", "#{path}/#{name_underscored}.rb")
|
|
31
40
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.0.0
|
data/lib/sequent/generator.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'sql'
|
|
2
4
|
|
|
3
5
|
module Sequent
|
|
4
6
|
module Migrations
|
|
5
|
-
|
|
6
7
|
##
|
|
7
8
|
# The executor is the implementation of the 3-phase deploy in Sequent.
|
|
8
9
|
# is responsible for executing the `Planner::Plan`.
|
|
@@ -25,18 +26,22 @@ module Sequent
|
|
|
25
26
|
plan.replay_tables.each do |migration|
|
|
26
27
|
table = migration.record_class
|
|
27
28
|
original_table_name = table.table_name.gsub("_#{migration.version}", '')
|
|
28
|
-
indexes_file_name =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
indexes_file_name = <<~EOS.chomp
|
|
30
|
+
#{Sequent.configuration.migration_sql_files_directory}/#{original_table_name}.indexes.sql
|
|
31
|
+
EOS
|
|
32
|
+
next unless File.exist?(indexes_file_name)
|
|
33
|
+
|
|
34
|
+
statements = sql_file_to_statements(indexes_file_name) do |raw_sql|
|
|
35
|
+
raw_sql.gsub('%SUFFIX%', "_#{migration.version}")
|
|
32
36
|
end
|
|
37
|
+
statements.each(&method(:exec_sql))
|
|
33
38
|
end
|
|
34
39
|
end
|
|
35
40
|
|
|
36
41
|
def execute_offline(plan, current_version)
|
|
37
42
|
plan.replay_tables.each do |migration|
|
|
38
43
|
table = migration.record_class
|
|
39
|
-
current_table_name = table.table_name.gsub("_#{migration.version}",
|
|
44
|
+
current_table_name = table.table_name.gsub("_#{migration.version}", '')
|
|
40
45
|
# 2 Rename old table
|
|
41
46
|
exec_sql("ALTER TABLE IF EXISTS #{current_table_name} RENAME TO #{current_table_name}_#{current_version}")
|
|
42
47
|
# 3 Rename new table
|
|
@@ -48,7 +53,9 @@ module Sequent
|
|
|
48
53
|
|
|
49
54
|
plan.alter_tables.each do |migration|
|
|
50
55
|
table = migration.record_class
|
|
51
|
-
sql_file =
|
|
56
|
+
sql_file = <<~EOS.chomp
|
|
57
|
+
#{Sequent.configuration.migration_sql_files_directory}/#{table.table_name}_#{migration.version}.sql
|
|
58
|
+
EOS
|
|
52
59
|
statements = sql_file_to_statements(sql_file)
|
|
53
60
|
statements.each(&method(:exec_sql))
|
|
54
61
|
end
|
|
@@ -57,7 +64,7 @@ module Sequent
|
|
|
57
64
|
def reset_table_names(plan)
|
|
58
65
|
plan.replay_tables.each do |migration|
|
|
59
66
|
table = migration.record_class
|
|
60
|
-
table.table_name = table.table_name.gsub("_#{migration.version}",
|
|
67
|
+
table.table_name = table.table_name.gsub("_#{migration.version}", '')
|
|
61
68
|
table.reset_column_information
|
|
62
69
|
end
|
|
63
70
|
end
|
|
@@ -65,14 +72,16 @@ module Sequent
|
|
|
65
72
|
def set_table_names_to_new_version(plan)
|
|
66
73
|
plan.replay_tables.each do |migration|
|
|
67
74
|
table = migration.record_class
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
next if table.table_name.end_with?("_#{migration.version}")
|
|
76
|
+
|
|
77
|
+
table.table_name = "#{table.table_name}_#{migration.version}"
|
|
78
|
+
table.reset_column_information
|
|
79
|
+
unless table.table_exists?
|
|
80
|
+
fail MigrationError,
|
|
81
|
+
"Table #{table.table_name} does not exist. Did you run ViewSchema.migrate_online first?"
|
|
72
82
|
end
|
|
73
83
|
end
|
|
74
84
|
end
|
|
75
|
-
|
|
76
85
|
end
|
|
77
86
|
end
|
|
78
87
|
end
|