sequent 5.0.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequent +1 -1
  3. data/db/sequent_schema.rb +3 -3
  4. data/lib/sequent/configuration.rb +19 -1
  5. data/lib/sequent/core/aggregate_root.rb +2 -6
  6. data/lib/sequent/core/aggregate_roots.rb +2 -6
  7. data/lib/sequent/core/command.rb +8 -12
  8. data/lib/sequent/core/command_service.rb +13 -2
  9. data/lib/sequent/core/core.rb +1 -0
  10. data/lib/sequent/core/event.rb +2 -2
  11. data/lib/sequent/core/event_store.rb +15 -2
  12. data/lib/sequent/core/ext/ext.rb +17 -0
  13. data/lib/sequent/core/helpers/attribute_support.rb +1 -0
  14. data/lib/sequent/core/helpers/default_validators.rb +3 -0
  15. data/lib/sequent/core/helpers/param_support.rb +2 -0
  16. data/lib/sequent/core/helpers/string_to_value_parsers.rb +5 -0
  17. data/lib/sequent/core/helpers/time_validator.rb +23 -0
  18. data/lib/sequent/core/helpers/value_validators.rb +11 -0
  19. data/lib/sequent/core/middleware/chain.rb +37 -0
  20. data/lib/sequent/core/middleware/middleware.rb +3 -0
  21. data/lib/sequent/core/projector.rb +3 -11
  22. data/lib/sequent/core/workflow.rb +3 -11
  23. data/lib/sequent/generator/template_project/Rakefile +2 -2
  24. data/lib/sequent/generator/template_project/db/sequent_schema.rb +3 -3
  25. data/lib/sequent/generator/template_project/spec/spec_helper.rb +1 -1
  26. data/lib/sequent/migrations/migrations.rb +1 -0
  27. data/lib/sequent/migrations/projectors.rb +2 -2
  28. data/lib/sequent/migrations/sequent_schema.rb +40 -0
  29. data/lib/sequent/migrations/view_schema.rb +39 -3
  30. data/lib/sequent/rake/migration_tasks.rb +36 -33
  31. data/lib/sequent/support/database.rb +29 -13
  32. data/lib/sequent/test/command_handler_helpers.rb +1 -1
  33. data/lib/sequent/test/database_helpers.rb +20 -0
  34. data/lib/sequent/test/time_comparison.rb +2 -5
  35. data/lib/sequent/test/{event_handler_helpers.rb → workflow_helpers.rb} +24 -10
  36. data/lib/sequent/test.rb +2 -1
  37. data/lib/sequent/util/dry_run.rb +1 -1
  38. data/lib/version.rb +1 -1
  39. metadata +11 -7
  40. data/lib/sequent/rake/tasks.rb +0 -121
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1dea4b53205452eaa68007ed47688b3fa074229677186e9342265cdd7a31d4db
4
- data.tar.gz: 2d342b9a013372ca99b441c4980432f642a56741746291d814c68ad60cf6c26e
3
+ metadata.gz: 24d2ee32c038b179c4386d2df97025af873487c5fe333952e5fedbe5d3ed47d8
4
+ data.tar.gz: 8d0464ac057a6e458ee7f131caba027fbddbd751af5b9d4314907ed248d0734d
5
5
  SHA512:
6
- metadata.gz: cea76a7429f29dcff3e73789f608c968030ade74446493effcf6cf6b51f39c3815da7e5ccc040cf0392a797a6978fa773b5f8f2791f907ebbb31830ab7ae56bc
7
- data.tar.gz: e03569382132ffefabd44ebc973d78a2c1745291cf64ef17c0dc744bd513d5fe72c816a0cfb69da7297ddd076df81139a53058816f02bc215f58ad48c0b7bc67
6
+ metadata.gz: 5b064934ae367d2e57baaa081c0b3f392bb932a1bb12fed00db337a6365f32c0949e5c6c4fe3eae6fa4a1374d931db517cf33efa911fdc265883bb122e8ce6ab
7
+ data.tar.gz: b5570de3a8b9deb24c1006486c37166f6e17d06d23a30d38a9de9b1e0755f1cbf4e57e1a2c1890ff9a68908f2077e90d7f8d1e79240ae3c5943de23f185be6f1
data/bin/sequent CHANGED
@@ -31,7 +31,7 @@ def new_project(args)
31
31
  bundle exec rake sequent:migrate:offline
32
32
 
33
33
  Run the example specs:
34
- RACK_ENV=test bundle exec rake sequent:db:create
34
+ SEQUENT_ENV=test bundle exec rake sequent:db:create
35
35
  bundle exec rspec spec
36
36
 
37
37
  To generate new aggregates use:
data/db/sequent_schema.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  ActiveRecord::Schema.define do
2
2
 
3
3
  create_table "event_records", :force => true do |t|
4
- t.string "aggregate_id", :null => false
4
+ t.uuid "aggregate_id", :null => false
5
5
  t.integer "sequence_number", :null => false
6
6
  t.datetime "created_at", :null => false
7
7
  t.string "event_type", :null => false
@@ -27,7 +27,7 @@ CREATE INDEX snapshot_events ON event_records (aggregate_id, sequence_number DES
27
27
 
28
28
  create_table "command_records", :force => true do |t|
29
29
  t.string "user_id"
30
- t.string "aggregate_id"
30
+ t.uuid "aggregate_id"
31
31
  t.string "command_type", :null => false
32
32
  t.string "event_aggregate_id"
33
33
  t.integer "event_sequence_number"
@@ -40,7 +40,7 @@ CREATE INDEX snapshot_events ON event_records (aggregate_id, sequence_number DES
40
40
  create_table "stream_records", :force => true do |t|
41
41
  t.datetime "created_at", :null => false
42
42
  t.string "aggregate_type", :null => false
43
- t.string "aggregate_id", :null => false
43
+ t.uuid "aggregate_id", :null => false
44
44
  t.integer "snapshot_threshold"
45
45
  end
46
46
 
@@ -32,6 +32,8 @@ module Sequent
32
32
 
33
33
  DEFAULT_ERROR_LOCALE_RESOLVER = -> { I18n.locale || :en }
34
34
 
35
+ DEFAULT_TIME_PRECISION = ActiveSupport::JSON::Encoding.time_precision
36
+
35
37
  attr_accessor :aggregate_repository,
36
38
  :event_store,
37
39
  :command_service,
@@ -43,6 +45,7 @@ module Sequent
43
45
  :event_record_hooks_class,
44
46
  :command_handlers,
45
47
  :command_filters,
48
+ :command_middleware,
46
49
  :event_handlers,
47
50
  :uuid_generator,
48
51
  :disable_event_handlers,
@@ -56,7 +59,11 @@ module Sequent
56
59
  :database_config_directory,
57
60
  :database_schema_directory,
58
61
  :event_store_schema_name,
59
- :strict_check_attributes_on_apply_events
62
+ :strict_check_attributes_on_apply_events,
63
+ :enable_multiple_database_support,
64
+ :primary_database_role,
65
+ :primary_database_key,
66
+ :time_precision
60
67
 
61
68
  attr_reader :migrations_class_name,
62
69
  :versions_table_name,
@@ -78,6 +85,7 @@ module Sequent
78
85
  self.command_handlers = []
79
86
  self.command_filters = []
80
87
  self.event_handlers = []
88
+ self.command_middleware = Sequent::Core::Middleware::Chain.new
81
89
 
82
90
  self.aggregate_repository = Sequent::Core::AggregateRepository.new
83
91
  self.event_store = Sequent::Core::EventStore.new
@@ -107,6 +115,16 @@ module Sequent
107
115
 
108
116
  self.logger = Logger.new(STDOUT).tap { |l| l.level = Logger::INFO }
109
117
  self.error_locale_resolver = DEFAULT_ERROR_LOCALE_RESOLVER
118
+
119
+ self.enable_multiple_database_support = false
120
+ self.primary_database_role = :writing
121
+ self.primary_database_key = :primary
122
+
123
+ self.time_precision = DEFAULT_TIME_PRECISION
124
+ end
125
+
126
+ def can_use_multiple_databases?
127
+ enable_multiple_database_support && ActiveRecord.version > Gem::Version.new('6.1.0')
110
128
  end
111
129
 
112
130
  def replayed_ids_table_name=(table_name)
@@ -39,14 +39,10 @@ module Sequent
39
39
  include Helpers::MessageHandler
40
40
  include Helpers::AutosetAttributes
41
41
  include SnapshotConfiguration
42
+ extend ActiveSupport::DescendantsTracker
42
43
 
43
44
  attr_reader :id, :uncommitted_events, :sequence_number, :event_stream
44
45
 
45
- def self.inherited(subclass)
46
- super
47
- AggregateRoots << subclass
48
- end
49
-
50
46
  def self.load_from_history(stream, events)
51
47
  first, *rest = events
52
48
  if first.is_a? SnapshotEvent
@@ -127,7 +123,7 @@ module Sequent
127
123
  # Provide subclasses nice DSL to 'apply' events via:
128
124
  #
129
125
  # def send_invoice
130
- # apply InvoiceSentEvent, send_date: DateTime.now
126
+ # apply InvoiceSentEvent, send_date: Time.now
131
127
  # end
132
128
  #
133
129
  def apply(event, params = {})
@@ -3,21 +3,17 @@
3
3
  module Sequent
4
4
  module Core
5
5
  #
6
- # Utility class containing all subclasses of AggregateRoot
6
+ # Utility class containing all subclasses of AggregateRoot.
7
7
  #
8
8
  class AggregateRoots
9
9
  class << self
10
10
  def aggregate_roots
11
- @aggregate_roots ||= []
11
+ Sequent::AggregateRoot.descendants
12
12
  end
13
13
 
14
14
  def all
15
15
  aggregate_roots
16
16
  end
17
-
18
- def <<(aggregate_root)
19
- aggregate_roots << aggregate_root
20
- end
21
17
  end
22
18
  end
23
19
  end
@@ -28,17 +28,17 @@ module Sequent
28
28
  include ActiveModel::Validations
29
29
  include ActiveModel::Validations::Callbacks
30
30
  include Sequent::Core::Helpers::TypeConversionSupport
31
+ extend ActiveSupport::DescendantsTracker
31
32
 
32
- attrs created_at: DateTime
33
+ attrs created_at: Time
34
+
35
+ define_model_callbacks :initialize, only: :after
33
36
 
34
37
  def initialize(args = {})
35
38
  update_all_attributes args
36
- @created_at = DateTime.now
37
- end
39
+ @created_at = Time.now
38
40
 
39
- def self.inherited(subclass)
40
- super
41
- Commands << subclass
41
+ _run_initialize_callbacks
42
42
  end
43
43
  end
44
44
 
@@ -56,22 +56,18 @@ module Sequent
56
56
  end
57
57
 
58
58
  #
59
- # Utility class containing all subclasses of BaseCommand
59
+ # Utility class containing all subclasses of BaseCommand.
60
60
  #
61
61
  class Commands
62
62
  class << self
63
63
  def commands
64
- @commands ||= []
64
+ Sequent::Core::BaseCommand.descendants
65
65
  end
66
66
 
67
67
  def all
68
68
  commands
69
69
  end
70
70
 
71
- def <<(command)
72
- commands << command
73
- end
74
-
75
71
  def find(command_name)
76
72
  commands.find { |c| c.name == command_name }
77
73
  end
@@ -50,7 +50,12 @@ module Sequent
50
50
  def process_commands
51
51
  Sequent::Util.skip_if_already_processing(:command_service_process_commands) do
52
52
  transaction_provider.transactional do
53
- process_command(command_queue.pop) until command_queue.empty?
53
+ until command_queue.empty?
54
+ command = command_queue.pop
55
+ command_middleware.invoke(command) do
56
+ process_command(command)
57
+ end
58
+ end
54
59
  Sequent::Util.done_processing(:command_service_process_commands)
55
60
  end
56
61
  ensure
@@ -66,7 +71,9 @@ module Sequent
66
71
 
67
72
  filters.each { |filter| filter.execute(command) }
68
73
 
69
- fail CommandNotValid, command unless command.valid?
74
+ I18n.with_locale(Sequent.configuration.error_locale_resolver.call) do
75
+ fail CommandNotValid, command unless command.valid?
76
+ end
70
77
 
71
78
  parsed_command = command.parse_attrs_to_correct_types
72
79
  command_handlers.select do |h|
@@ -98,6 +105,10 @@ module Sequent
98
105
  def command_handlers
99
106
  Sequent.configuration.command_handlers
100
107
  end
108
+
109
+ def command_middleware
110
+ Sequent.configuration.command_middleware
111
+ end
101
112
  end
102
113
 
103
114
  # Raised when BaseCommand.valid? returns false
@@ -4,6 +4,7 @@ require_relative 'sequent_oj'
4
4
  require_relative 'helpers/helpers'
5
5
  require_relative 'persistors/persistors'
6
6
  require_relative 'transactions/transactions'
7
+ require_relative 'middleware/middleware'
7
8
  require_relative 'event'
8
9
  require_relative 'aggregate_repository'
9
10
  require_relative 'aggregate_root'
@@ -13,14 +13,14 @@ module Sequent
13
13
  include Sequent::Core::Helpers::AttributeSupport
14
14
  include Sequent::Core::Helpers::EqualSupport
15
15
  include Sequent::Core::Helpers::StringSupport
16
- attrs aggregate_id: String, sequence_number: Integer, created_at: DateTime
16
+ attrs aggregate_id: String, sequence_number: Integer, created_at: Time
17
17
 
18
18
  def initialize(args = {})
19
19
  update_all_attributes args
20
20
  fail 'Missing aggregate_id' unless @aggregate_id
21
21
  fail 'Missing sequence_number' unless @sequence_number
22
22
 
23
- @created_at ||= DateTime.now
23
+ @created_at ||= Time.now
24
24
  end
25
25
 
26
26
  def payload
@@ -26,8 +26,21 @@ module Sequent
26
26
  end
27
27
  end
28
28
 
29
- def initialize
30
- @event_types = ThreadSafe::Cache.new
29
+ ##
30
+ # Disables event type caching (ie. for in development).
31
+ #
32
+ class NoEventTypesCache
33
+ def fetch_or_store(event_type)
34
+ yield(event_type)
35
+ end
36
+ end
37
+
38
+ def initialize(cache_event_types: true)
39
+ @event_types = if cache_event_types
40
+ ThreadSafe::Cache.new
41
+ else
42
+ NoEventTypesCache.new
43
+ end
31
44
  end
32
45
 
33
46
  ##
@@ -68,6 +68,23 @@ class DateTime
68
68
  end
69
69
  end
70
70
 
71
+ class Time
72
+ def self.from_params(value)
73
+ value.blank? ? nil : Time.iso8601(value.dup)
74
+ rescue ArgumentError
75
+ value
76
+ end
77
+
78
+ def self.deserialize_from_json(value)
79
+ value.blank? ? nil : Time.iso8601(value.dup)
80
+ rescue ArgumentError => e
81
+ return Time.parse(value.dup) if e.message =~ /invalid xmlschema format/ # ruby >= 3
82
+ return Time.parse(value.dup) if e.message =~ /invalid date:/ # ruby 2.7
83
+
84
+ raise
85
+ end
86
+ end
87
+
71
88
  class Array
72
89
  def self.deserialize_from_json(value)
73
90
  value
@@ -6,6 +6,7 @@ require_relative 'array_with_type'
6
6
  require_relative 'default_validators'
7
7
  require_relative 'type_conversion_support'
8
8
  require_relative 'date_time_validator'
9
+ require_relative 'time_validator'
9
10
  require_relative 'association_validator'
10
11
 
11
12
  module Sequent
@@ -17,6 +17,9 @@ module Sequent
17
17
  Date => ->(klass, field) do
18
18
  klass.validates field, 'sequent::Core::Helpers::Date' => true
19
19
  end,
20
+ Time => ->(klass, field) do
21
+ klass.validates field, 'sequent::Core::Helpers::Time' => true
22
+ end,
20
23
  DateTime => ->(klass, field) do
21
24
  klass.validates field, 'sequent::Core::Helpers::DateTime' => true
22
25
  end,
@@ -83,6 +83,8 @@ module Sequent
83
83
  val.iso8601
84
84
  elsif val.is_a? Date
85
85
  val.iso8601
86
+ elsif val.is_a? Time
87
+ val.iso8601(Sequent.configuration.time_precision)
86
88
  else
87
89
  val
88
90
  end
@@ -16,6 +16,7 @@ module Sequent
16
16
  ::Boolean => ->(value) { parse_to_bool(value) },
17
17
  ::Date => ->(value) { parse_to_date(value) },
18
18
  ::DateTime => ->(value) { parse_to_date_time(value) },
19
+ ::Time => ->(value) { parse_to_time(value) },
19
20
  ::Sequent::Core::Helpers::ArrayWithType => ->(values, type_in_array) { parse_array(values, type_in_array) },
20
21
  ::Sequent::Core::Helpers::Secret => ->(value) { Sequent::Core::Helpers::Secret.new(value).encrypt },
21
22
  }.freeze
@@ -52,6 +53,10 @@ module Sequent
52
53
  value.is_a?(DateTime) ? value : DateTime.deserialize_from_json(value)
53
54
  end
54
55
 
56
+ def self.parse_to_time(value)
57
+ value.is_a?(Time) ? value : Time.deserialize_from_json(value)
58
+ end
59
+
55
60
  def self.parse_array(values, type_in_array)
56
61
  fail "invalid value for array(): \"#{values}\"" unless values.is_a?(Array)
57
62
 
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model'
4
+
5
+ module Sequent
6
+ module Core
7
+ module Helpers
8
+ # Validates Time
9
+ # Automatically included when using a
10
+ #
11
+ # attrs value: Time
12
+ class TimeValidator < ActiveModel::EachValidator
13
+ def validate_each(subject, attribute, value)
14
+ return if value.is_a?(Time)
15
+
16
+ Time.deserialize_from_json(value)
17
+ rescue StandardError
18
+ subject.errors.add attribute, :invalid_time
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -16,6 +16,7 @@ module Sequent
16
16
  ::Integer => ->(value) { valid_integer?(value) },
17
17
  ::Boolean => ->(value) { valid_bool?(value) },
18
18
  ::Date => ->(value) { valid_date?(value) },
19
+ ::Time => ->(value) { valid_time?(value) },
19
20
  ::DateTime => ->(value) { valid_date_time?(value) },
20
21
  }.freeze
21
22
 
@@ -53,6 +54,16 @@ module Sequent
53
54
  end
54
55
  end
55
56
 
57
+ def self.valid_time?(value)
58
+ return true if value.blank?
59
+
60
+ begin
61
+ value.is_a?(Time) || !!Time.iso8601(value.dup)
62
+ rescue StandardError
63
+ false
64
+ end
65
+ end
66
+
56
67
  def self.valid_string?(value)
57
68
  return true if value.nil?
58
69
 
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sequent
4
+ module Core
5
+ module Middleware
6
+ class Chain
7
+ attr_reader :entries
8
+
9
+ def initialize
10
+ clear
11
+ end
12
+
13
+ def add(middleware)
14
+ @entries.push(middleware)
15
+ end
16
+
17
+ def clear
18
+ @entries = []
19
+ end
20
+
21
+ def invoke(*args, &invoker)
22
+ chain = @entries.dup
23
+
24
+ traverse_chain = -> do
25
+ if chain.empty?
26
+ invoker.call
27
+ else
28
+ chain.shift.call(*args, &traverse_chain)
29
+ end
30
+ end
31
+
32
+ traverse_chain.call
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'chain'
@@ -86,17 +86,13 @@ module Sequent
86
86
  extend Forwardable
87
87
  include Helpers::MessageHandler
88
88
  include Migratable
89
+ extend ActiveSupport::DescendantsTracker
89
90
 
90
91
  def initialize(persistor = Sequent::Core::Persistors::ActiveRecordPersistor.new)
91
92
  ensure_valid!
92
93
  @persistor = persistor
93
94
  end
94
95
 
95
- def self.inherited(subclass)
96
- super
97
- Projectors << subclass
98
- end
99
-
100
96
  def self.replay_persistor
101
97
  nil
102
98
  end
@@ -132,22 +128,18 @@ module Sequent
132
128
  end
133
129
 
134
130
  #
135
- # Utility class containing all subclasses of Projector
131
+ # Utility class containing all subclasses of Projector.
136
132
  #
137
133
  class Projectors
138
134
  class << self
139
135
  def projectors
140
- @projectors ||= []
136
+ Sequent::Projector.descendants
141
137
  end
142
138
 
143
139
  def all
144
140
  projectors
145
141
  end
146
142
 
147
- def <<(projector)
148
- projectors << projector
149
- end
150
-
151
143
  def find(projector_name)
152
144
  projectors.find { |c| c.name == projector_name }
153
145
  end
@@ -7,11 +7,7 @@ module Sequent
7
7
  module Core
8
8
  class Workflow
9
9
  include Helpers::MessageHandler
10
-
11
- def self.inherited(subclass)
12
- super
13
- Workflows << subclass
14
- end
10
+ extend ActiveSupport::DescendantsTracker
15
11
 
16
12
  def self.on(*args, **opts, &block)
17
13
  decorated_block = ->(event) do
@@ -49,22 +45,18 @@ module Sequent
49
45
  end
50
46
 
51
47
  #
52
- # Utility class containing all subclasses of Workflow
48
+ # Utility class containing all subclasses of Workflow.
53
49
  #
54
50
  class Workflows
55
51
  class << self
56
52
  def workflows
57
- @workflows ||= []
53
+ Sequent::Workflow.descendants
58
54
  end
59
55
 
60
56
  def all
61
57
  workflows
62
58
  end
63
59
 
64
- def <<(workflow)
65
- workflows << workflow
66
- end
67
-
68
60
  def find(workflow_name)
69
61
  workflows.find { |c| c.name == workflow_name }
70
62
  end
@@ -1,4 +1,4 @@
1
- ENV['RACK_ENV'] ||= 'development'
1
+ ENV['SEQUENT_ENV'] ||= 'development'
2
2
 
3
3
  require './my_app'
4
4
  require 'sequent/rake/migration_tasks'
@@ -8,5 +8,5 @@ Sequent::Rake::MigrationTasks.new.register_tasks!
8
8
  task "sequent:migrate:init" => [:db_connect]
9
9
 
10
10
  task "db_connect" do
11
- Sequent::Support::Database.connect!(ENV['RACK_ENV'])
11
+ Sequent::Support::Database.connect!(ENV['SEQUENT_ENV'])
12
12
  end
@@ -1,7 +1,7 @@
1
1
  ActiveRecord::Schema.define do
2
2
 
3
3
  create_table "event_records", :force => true do |t|
4
- t.string "aggregate_id", :null => false
4
+ t.uuid "aggregate_id", :null => false
5
5
  t.integer "sequence_number", :null => false
6
6
  t.datetime "created_at", :null => false
7
7
  t.string "event_type", :null => false
@@ -27,7 +27,7 @@ CREATE INDEX snapshot_events ON event_records (aggregate_id, sequence_number DES
27
27
 
28
28
  create_table "command_records", :force => true do |t|
29
29
  t.string "user_id"
30
- t.string "aggregate_id"
30
+ t.uuid "aggregate_id"
31
31
  t.string "command_type", :null => false
32
32
  t.text "command_json", :null => false
33
33
  t.datetime "created_at", :null => false
@@ -36,7 +36,7 @@ CREATE INDEX snapshot_events ON event_records (aggregate_id, sequence_number DES
36
36
  create_table "stream_records", :force => true do |t|
37
37
  t.datetime "created_at", :null => false
38
38
  t.string "aggregate_type", :null => false
39
- t.string "aggregate_id", :null => false
39
+ t.uuid "aggregate_id", :null => false
40
40
  t.integer "snapshot_threshold"
41
41
  end
42
42
 
@@ -1,7 +1,7 @@
1
1
  require 'bundler/setup'
2
2
  Bundler.setup
3
3
 
4
- ENV['RACK_ENV'] ||= 'test'
4
+ ENV['SEQUENT_ENV'] ||= 'test'
5
5
 
6
6
  require 'sequent/test'
7
7
  require 'database_cleaner'
@@ -9,3 +9,4 @@ require_relative 'migrate_events'
9
9
  require_relative 'projectors'
10
10
  require_relative 'view_schema'
11
11
  require_relative 'functions'
12
+ require_relative 'sequent_schema'
@@ -5,11 +5,11 @@ module Sequent
5
5
  module Migrations
6
6
  class Projectors
7
7
  def self.versions
8
- fail 'Define your own Sequent::Migrations::List class that extends this class and implements this method'
8
+ fail "Define your own #{name} class that extends this class and implements this method"
9
9
  end
10
10
 
11
11
  def self.version
12
- fail 'Define your own Sequent::Migrations::List class that extends this class and implements this method'
12
+ fail "Define your own #{name} class that extends this class and implements this method"
13
13
  end
14
14
 
15
15
  def self.migrations_between(old, new)
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sequent
4
+ module Migrations
5
+ class SequentSchema
6
+ FAIL_IF_EXISTS = ->(schema_name) { fail "Schema #{schema_name} already exists in the database" }
7
+
8
+ class << self
9
+ #
10
+ # Creates the sequent schema if it does not exist yet
11
+ #
12
+ # @param env [String] The environment used to setup database connection
13
+ # @param fail_if_exists [Boolean] When true fails if the schema exists, otherwise just return.
14
+ def create_sequent_schema_if_not_exists(env:, fail_if_exists: true)
15
+ fail ArgumentError, 'env is required' if env.blank?
16
+
17
+ db_config = Sequent::Support::Database.read_config(env)
18
+
19
+ Sequent::Support::Database.establish_connection(db_config)
20
+
21
+ event_store_schema = Sequent.configuration.event_store_schema_name
22
+ schema_exists = Sequent::Support::Database.schema_exists?(event_store_schema)
23
+
24
+ FAIL_IF_EXISTS.call(event_store_schema) if schema_exists && fail_if_exists
25
+ return if schema_exists
26
+
27
+ sequent_schema = File.join(Sequent.configuration.database_schema_directory, "#{event_store_schema}.rb")
28
+ unless File.exist?(sequent_schema)
29
+ fail "File '#{sequent_schema}' does not exist. Check your Sequent configuration."
30
+ end
31
+
32
+ Sequent::Support::Database.create_schema(event_store_schema)
33
+ Sequent::Support::Database.with_schema_search_path(event_store_schema, db_config, env) do
34
+ load(sequent_schema)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end