sequent 5.0.0 → 6.0.1

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 (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 +22 -12
  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: e420a9289f17d8552364afd49977cdb8cdbc0b37f39445a0a7ce310f9c1a180f
4
+ data.tar.gz: 116144ee36558de52bc381b8e99e13d44aad3bedda6d531b380d93b3d7b96445
5
5
  SHA512:
6
- metadata.gz: cea76a7429f29dcff3e73789f608c968030ade74446493effcf6cf6b51f39c3815da7e5ccc040cf0392a797a6978fa773b5f8f2791f907ebbb31830ab7ae56bc
7
- data.tar.gz: e03569382132ffefabd44ebc973d78a2c1745291cf64ef17c0dc744bd513d5fe72c816a0cfb69da7297ddd076df81139a53058816f02bc215f58ad48c0b7bc67
6
+ metadata.gz: 434eb99e05dda79479a8c8a6529a0d2b3bfa2fc3e02c7143ad314f27a2140d33c6a01f1d3d1a147892b1dafefcd899bed8bde9cbe3b0c76520f3561b5dfa2b05
7
+ data.tar.gz: 6fd8a9df8cac32aaad084fd2b472caab478c13840da563b8d3a8e8c9e1d86c32bfc3d43a7e8d7031386a36a127c9be6648d3baaa241dcfb45d92f3d7721a9d4d
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