ruby_event_store-rom 0.30.0 → 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Makefile +20 -3
- data/lib/ruby_event_store/rom/adapters/memory/relations/events.rb +45 -0
- data/lib/ruby_event_store/rom/adapters/memory/relations/stream_entries.rb +95 -0
- data/lib/ruby_event_store/rom/adapters/memory/unit_of_work.rb +49 -0
- data/lib/ruby_event_store/rom/adapters/sql/relations/events.rb +3 -1
- data/lib/ruby_event_store/rom/adapters/sql/relations/stream_entries.rb +2 -6
- data/lib/ruby_event_store/rom/adapters/sql/tasks/migration_tasks.rake +7 -4
- data/lib/ruby_event_store/rom/event_repository.rb +41 -24
- data/lib/ruby_event_store/rom/memory.rb +68 -0
- data/lib/ruby_event_store/rom/repositories/events.rb +2 -2
- data/lib/ruby_event_store/rom/repositories/stream_entries.rb +10 -11
- data/lib/ruby_event_store/rom/sql.rb +39 -3
- data/lib/ruby_event_store/rom/tuple_uniqueness_error.rb +13 -0
- data/lib/ruby_event_store/rom/version.rb +1 -1
- data/lib/ruby_event_store/rom.rb +10 -6
- data/lib/ruby_event_store/spec/rom/event_repository_lint.rb +206 -0
- data/lib/ruby_event_store/spec/rom/relations/events_lint.rb +75 -0
- data/lib/ruby_event_store/spec/rom/relations/stream_entries_lint.rb +198 -0
- data/lib/ruby_event_store/spec/rom/spec_helper_lint.rb +15 -0
- data/lib/ruby_event_store/spec/rom/unit_of_work_lint.rb +37 -0
- data/ruby_event_store-rom.gemspec +7 -7
- metadata +37 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 578db4e9a6e68483348133d15dfee076b76a29a76ac6ae5ec6e7b94063bf7284
|
4
|
+
data.tar.gz: cb177a748a62f91911ace80e4c5175a5cc4cda773947eff6d855249d71a7b8e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0124ece3841ae0774b5a758cf5c154d5406060dfeae4803072ad37d08ad56d0305d33a81fdbe3359e834fcc8412270e50176a39b49f3ca38ecf68283e8faa6a
|
7
|
+
data.tar.gz: f773ecdc66ee0ff1974ca9977ea9b003cf63b4ac8212ddfda997c8f7c13c76693c65281cf0684f35fb43290cb411b7e3b971780f6815480fb34a2c1dc871221f
|
data/Makefile
CHANGED
@@ -1,25 +1,42 @@
|
|
1
1
|
GEM_VERSION = $(shell cat ../RES_VERSION)
|
2
2
|
GEM_NAME = ruby_event_store-rom
|
3
3
|
REQUIRE = $(GEM_NAME)
|
4
|
-
IGNORE = RubyEventStore::ROM::EventRepository\#
|
4
|
+
IGNORE = RubyEventStore::ROM::EventRepository\#initialize \
|
5
|
+
RubyEventStore::ROM::EventRepository\#handle_not_found_errors \
|
5
6
|
RubyEventStore::ROM::EventRepository\#handle_unique_violation_errors \
|
6
7
|
RubyEventStore::ROM::EventRepository\#has_event? \
|
7
8
|
RubyEventStore::ROM::EventRepository\#last_stream_event \
|
8
9
|
RubyEventStore::ROM::Repositories::Events\#find_nonexistent_pks \
|
10
|
+
RubyEventStore::ROM::Repositories::StreamEntries\#create_changeset \
|
9
11
|
RubyEventStore::ROM::Env\#handle_error \
|
10
12
|
RubyEventStore::ROM::Env\#initialize \
|
11
13
|
RubyEventStore::ROM::Env\#register_error_handler \
|
12
14
|
RubyEventStore::ROM::Env\#register_unit_of_work_options \
|
13
|
-
RubyEventStore::ROM::Env\#
|
15
|
+
RubyEventStore::ROM::Env\#unit_of_work \
|
14
16
|
RubyEventStore::ROM::UnitOfWork\#call \
|
15
17
|
RubyEventStore::ROM::UnitOfWork\#commit! \
|
16
18
|
RubyEventStore::ROM::SQL::Relations::StreamEntries\#normalize_stream_name \
|
17
19
|
RubyEventStore::ROM::SQL::IndexViolationDetector\#detect \
|
20
|
+
RubyEventStore::ROM::SQL::SpecHelper\#initialize \
|
21
|
+
RubyEventStore::ROM::SQL::SpecHelper\#has_connection_pooling? \
|
22
|
+
RubyEventStore::ROM::SQL::SpecHelper\#close_pool_connection \
|
18
23
|
RubyEventStore::ROM::SQL::SpecHelper\#load_gateway_schema \
|
19
24
|
RubyEventStore::ROM::SQL::SpecHelper\#establish_gateway_connection \
|
20
25
|
RubyEventStore::ROM::SQL::SpecHelper\#drop_gateway_schema \
|
21
26
|
RubyEventStore::ROM::SQL::SpecHelper\#close_gateway_connection \
|
22
|
-
RubyEventStore::ROM::SQL::
|
27
|
+
RubyEventStore::ROM::SQL::SpecHelper\#run_lifecycle \
|
28
|
+
RubyEventStore::ROM::SQL::UnitOfWork\#commit! \
|
29
|
+
RubyEventStore::ROM::Memory::SpecHelper\#initialize \
|
30
|
+
RubyEventStore::ROM::Memory::SpecHelper\#close_pool_connection \
|
31
|
+
RubyEventStore::ROM::Memory::SpecHelper\#load_gateway_schema \
|
32
|
+
RubyEventStore::ROM::Memory::SpecHelper\#establish_gateway_connection \
|
33
|
+
RubyEventStore::ROM::Memory::SpecHelper\#drop_gateway_schema \
|
34
|
+
RubyEventStore::ROM::Memory::SpecHelper\#close_gateway_connection \
|
35
|
+
RubyEventStore::ROM::Memory::SpecHelper\#run_lifecycle \
|
36
|
+
RubyEventStore::ROM::Memory::UnitOfWork\#commit! \
|
37
|
+
RubyEventStore::ROM::Memory::Relations::Events\#verify_uniquness! \
|
38
|
+
RubyEventStore::ROM::Memory::Relations::StreamEntries\#verify_uniquness! \
|
39
|
+
RubyEventStore::ROM::Memory::Relations::StreamEntries\#normalize_stream_name
|
23
40
|
SUBJECT ?= RubyEventStore::ROM*
|
24
41
|
DATABASE_URL ?= sqlite::memory:
|
25
42
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RubyEventStore
|
2
|
+
module ROM
|
3
|
+
module Memory
|
4
|
+
module Relations
|
5
|
+
class Events < ::ROM::Relation[:memory]
|
6
|
+
schema(:events) do
|
7
|
+
attribute :id, ::ROM::Types::Strict::String.meta(primary_key: true)
|
8
|
+
attribute :event_type, ::ROM::Types::Strict::String
|
9
|
+
attribute :metadata, ::ROM::Types::Strict::String.optional
|
10
|
+
attribute :data, ::ROM::Types::Strict::String
|
11
|
+
attribute :created_at, ::ROM::Types::Strict::Time.default { Time.now }
|
12
|
+
end
|
13
|
+
|
14
|
+
def insert(tuple)
|
15
|
+
verify_uniquness!(tuple)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def for_stream_entries(_assoc, stream_entries)
|
20
|
+
restrict(id: stream_entries.map { |e| e[:event_id] })
|
21
|
+
end
|
22
|
+
|
23
|
+
def by_pk(id)
|
24
|
+
restrict(id: id)
|
25
|
+
end
|
26
|
+
|
27
|
+
def exist?
|
28
|
+
one?
|
29
|
+
end
|
30
|
+
|
31
|
+
def pluck(name)
|
32
|
+
map { |e| e[name] }
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def verify_uniquness!(tuple)
|
38
|
+
return unless by_pk(tuple[:id]).exist?
|
39
|
+
raise TupleUniquenessError.for_event_id(tuple[:id])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module RubyEventStore
|
2
|
+
module ROM
|
3
|
+
module Memory
|
4
|
+
module Relations
|
5
|
+
class StreamEntries < ::ROM::Relation[:memory]
|
6
|
+
schema(:stream_entries) do
|
7
|
+
attribute :id, ::ROM::Types::Strict::Int.meta(primary_key: true).default { RubyEventStore::ROM::Memory.fetch_next_id }
|
8
|
+
attribute :stream, ::ROM::Types::Strict::String
|
9
|
+
attribute :position, ::ROM::Types::Strict::Int.optional
|
10
|
+
attribute :event_id, ::ROM::Types::Strict::String.meta(foreign_key: true, relation: :events)
|
11
|
+
attribute :created_at, ::ROM::Types::Strict::Time.default { Time.now }
|
12
|
+
|
13
|
+
associations do
|
14
|
+
belongs_to :events, as: :event, foreign_key: :event_id, override: true, view: :for_stream_entries
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
auto_struct true
|
19
|
+
|
20
|
+
SERIALIZED_GLOBAL_STREAM_NAME = 'all'.freeze
|
21
|
+
|
22
|
+
def offset(num)
|
23
|
+
num.zero? ? self : new(dataset.slice(num..-1))
|
24
|
+
end
|
25
|
+
|
26
|
+
def take(num)
|
27
|
+
num.nil? ? self : super
|
28
|
+
end
|
29
|
+
|
30
|
+
def insert(tuple)
|
31
|
+
verify_uniquness!(tuple)
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete(tuple)
|
36
|
+
super tuple.to_h
|
37
|
+
end
|
38
|
+
|
39
|
+
def by_stream(stream)
|
40
|
+
restrict(stream: normalize_stream_name(stream))
|
41
|
+
end
|
42
|
+
|
43
|
+
def by_stream_and_event_id(stream, event_id)
|
44
|
+
restrict(stream: normalize_stream_name(stream), event_id: event_id).one!
|
45
|
+
end
|
46
|
+
|
47
|
+
def max_position(stream)
|
48
|
+
new(by_stream(stream).order(:position).dataset.reverse).project(:position).take(1).one
|
49
|
+
end
|
50
|
+
|
51
|
+
DIRECTION_MAP = {
|
52
|
+
forward: [false, :>],
|
53
|
+
backward: [true, :<]
|
54
|
+
}.freeze
|
55
|
+
|
56
|
+
def ordered(direction, stream, offset_entry_id = nil)
|
57
|
+
reverse, operator = DIRECTION_MAP[direction]
|
58
|
+
|
59
|
+
raise ArgumentError, 'Direction must be :forward or :backward' if order.nil?
|
60
|
+
|
61
|
+
order_columns = %i[position id]
|
62
|
+
order_columns.delete(:position) if stream.global?
|
63
|
+
|
64
|
+
query = by_stream(stream)
|
65
|
+
query = query.restrict { |tuple| tuple[:id].public_send(operator, offset_entry_id) } if offset_entry_id
|
66
|
+
query = query.order(*order_columns)
|
67
|
+
query = new(query.dataset.reverse) if reverse
|
68
|
+
|
69
|
+
query
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Verifies uniqueness of [stream, event_id] and [stream, position]
|
75
|
+
def verify_uniquness!(tuple)
|
76
|
+
stream = tuple[:stream]
|
77
|
+
attrs = %i[position event_id]
|
78
|
+
attrs.delete(:position) if Stream.new(stream).global?
|
79
|
+
|
80
|
+
attrs.each do |key|
|
81
|
+
next if key == :position && tuple[key].nil?
|
82
|
+
next if restrict(:stream => stream, key => tuple.fetch(key)).none?
|
83
|
+
|
84
|
+
raise TupleUniquenessError.public_send(:"for_stream_and_#{key}", stream, tuple.fetch(key))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def normalize_stream_name(stream)
|
89
|
+
stream.global? ? SERIALIZED_GLOBAL_STREAM_NAME : stream.name
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module RubyEventStore
|
2
|
+
module ROM
|
3
|
+
module Memory
|
4
|
+
class UnitOfWork < ROM::UnitOfWork
|
5
|
+
def self.mutex
|
6
|
+
@mutex ||= Mutex.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def commit!(gateway, changesets, **options)
|
10
|
+
self.class.mutex.synchronize do
|
11
|
+
committed = []
|
12
|
+
|
13
|
+
begin
|
14
|
+
while changesets.size > 0
|
15
|
+
changeset = changesets.shift
|
16
|
+
relation = env.container.relations[changeset.relation.name]
|
17
|
+
|
18
|
+
case changeset
|
19
|
+
when ROM::Repositories::Events::Create
|
20
|
+
relation.by_pk(changeset.to_a.map{ |e| e[:id] }).each do |tuple|
|
21
|
+
raise TupleUniquenessError.for_event_id(tuple[:id])
|
22
|
+
end
|
23
|
+
when ROM::Repositories::StreamEntries::Create
|
24
|
+
changeset.to_a.each do |tuple|
|
25
|
+
relation.send(:verify_uniquness!, tuple)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
raise ArgumentError, 'Unknown changeset'
|
29
|
+
end
|
30
|
+
|
31
|
+
committed << [changeset, relation]
|
32
|
+
|
33
|
+
changeset.commit
|
34
|
+
end
|
35
|
+
rescue StandardError
|
36
|
+
committed.reverse.each do |changeset, relation|
|
37
|
+
relation
|
38
|
+
.restrict(id: changeset.to_a.map { |e| e[:id] })
|
39
|
+
.command(:delete, result: :many).call
|
40
|
+
end
|
41
|
+
|
42
|
+
raise
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -3,7 +3,9 @@ module RubyEventStore
|
|
3
3
|
module SQL
|
4
4
|
module Relations
|
5
5
|
class Events < ::ROM::Relation[:sql]
|
6
|
-
schema(:event_store_events, as: :events, infer: true)
|
6
|
+
schema(:event_store_events, as: :events, infer: true) do
|
7
|
+
attribute :created_at, ::ROM::Types::Strict::Time.default { Time.now }
|
8
|
+
end
|
7
9
|
end
|
8
10
|
end
|
9
11
|
end
|
@@ -4,6 +4,8 @@ module RubyEventStore
|
|
4
4
|
module Relations
|
5
5
|
class StreamEntries < ::ROM::Relation[:sql]
|
6
6
|
schema(:event_store_events_in_streams, as: :stream_entries, infer: true) do
|
7
|
+
attribute :created_at, ::ROM::Types::Strict::Time.default { Time.now }
|
8
|
+
|
7
9
|
associations do
|
8
10
|
belongs_to :events, as: :event, foreign_key: :event_id
|
9
11
|
end
|
@@ -11,10 +13,6 @@ module RubyEventStore
|
|
11
13
|
|
12
14
|
alias_method :take, :limit
|
13
15
|
|
14
|
-
def by_stream(stream)
|
15
|
-
where(stream: stream.name)
|
16
|
-
end
|
17
|
-
|
18
16
|
SERIALIZED_GLOBAL_STREAM_NAME = 'all'.freeze
|
19
17
|
|
20
18
|
def by_stream(stream)
|
@@ -47,8 +45,6 @@ module RubyEventStore
|
|
47
45
|
query.order { |r| order_columns.map { |c| r[:stream_entries][c].public_send(order) } }
|
48
46
|
end
|
49
47
|
|
50
|
-
alias_method :take, :limit
|
51
|
-
|
52
48
|
private
|
53
49
|
|
54
50
|
def normalize_stream_name(stream)
|
@@ -1,15 +1,18 @@
|
|
1
1
|
require 'ruby_event_store/rom/sql'
|
2
2
|
|
3
|
+
MIGRATIONS_PATH = 'db/migrate'
|
4
|
+
|
3
5
|
desc 'Setup ROM EventRespository environment'
|
4
6
|
task 'db:setup' do
|
7
|
+
Dir.chdir(Dir.pwd)
|
5
8
|
ROM::SQL::RakeSupport.env = ::RubyEventStore::ROM.configure(:sql).container
|
6
9
|
end
|
7
10
|
|
8
|
-
desc
|
9
|
-
task 'db:
|
10
|
-
Dir[File.join(File.dirname(__FILE__), '../../../../../../
|
11
|
+
desc 'Copy RubyEventStore SQL migrations to db/migrate'
|
12
|
+
task 'db:migrations:copy' => 'db:setup' do
|
13
|
+
Dir[File.join(File.dirname(__FILE__), '../../../../../../', MIGRATIONS_PATH, '/*.rb')].each do |input|
|
11
14
|
name = File.basename(input, '.*').sub(/\d+_/, '')
|
12
|
-
output = ROM::SQL::RakeSupport.create_migration(name)
|
15
|
+
output = ROM::SQL::RakeSupport.create_migration(name, path: File.join(Dir.pwd, MIGRATIONS_PATH))
|
13
16
|
|
14
17
|
File.write output, File.read(input)
|
15
18
|
|
@@ -1,9 +1,17 @@
|
|
1
1
|
require 'ruby_event_store/rom/unit_of_work'
|
2
|
+
require 'forwardable'
|
2
3
|
|
3
4
|
module RubyEventStore
|
4
5
|
module ROM
|
5
6
|
class EventRepository
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegator :@rom, :handle_error, :guard_for
|
10
|
+
def_delegators :@rom, :unit_of_work
|
11
|
+
|
6
12
|
def initialize(rom: ROM.env)
|
13
|
+
raise ArgumentError, "Must specify rom" unless rom && rom.instance_of?(Env)
|
14
|
+
|
7
15
|
@rom = rom
|
8
16
|
@events = Repositories::Events.new(rom.container)
|
9
17
|
@stream_entries = Repositories::StreamEntries.new(rom.container)
|
@@ -13,31 +21,44 @@ module RubyEventStore
|
|
13
21
|
events = normalize_to_array(events)
|
14
22
|
event_ids = events.map(&:event_id)
|
15
23
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
24
|
+
guard_for(:unique_violation) do
|
25
|
+
unit_of_work do |changesets|
|
26
|
+
# Create changesets inside transaction because
|
27
|
+
# we want to find the last position (a.k.a. version)
|
28
|
+
# again if the transaction is retried due to a
|
29
|
+
# deadlock in MySQL
|
30
|
+
changesets << @events.create_changeset(events)
|
31
|
+
changesets << @stream_entries.create_changeset(
|
32
|
+
event_ids,
|
33
|
+
stream,
|
34
|
+
@stream_entries.resolve_version(stream, expected_version),
|
35
|
+
global_stream: true
|
36
|
+
)
|
37
|
+
end
|
23
38
|
end
|
24
39
|
|
25
40
|
self
|
26
|
-
rescue => ex
|
27
|
-
@rom.handle_error(:unique_violation, ex)
|
28
41
|
end
|
29
42
|
|
30
43
|
def link_to_stream(event_ids, stream, expected_version)
|
31
44
|
event_ids = normalize_to_array(event_ids)
|
32
|
-
nonexistent_ids = @events.find_nonexistent_pks(event_ids)
|
33
|
-
|
34
|
-
nonexistent_ids.each { |id| raise EventNotFound.new(id) }
|
35
45
|
|
36
|
-
|
46
|
+
# Validate event IDs
|
47
|
+
@events
|
48
|
+
.find_nonexistent_pks(event_ids)
|
49
|
+
.each { |id| raise EventNotFound.new(id) }
|
50
|
+
|
51
|
+
guard_for(:unique_violation) do
|
52
|
+
unit_of_work do |changesets|
|
53
|
+
changesets << @stream_entries.create_changeset(
|
54
|
+
event_ids,
|
55
|
+
stream,
|
56
|
+
@stream_entries.resolve_version(stream, expected_version)
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
37
60
|
|
38
61
|
self
|
39
|
-
rescue => ex
|
40
|
-
@rom.handle_error(:unique_violation, ex)
|
41
62
|
end
|
42
63
|
|
43
64
|
def delete_stream(stream)
|
@@ -45,12 +66,8 @@ module RubyEventStore
|
|
45
66
|
end
|
46
67
|
|
47
68
|
def has_event?(event_id)
|
48
|
-
|
49
|
-
|
50
|
-
begin
|
51
|
-
@rom.handle_error(:not_found, ex, event_id)
|
52
|
-
rescue EventNotFound
|
53
|
-
false
|
69
|
+
!! guard_for(:not_found, event_id, swallow: EventNotFound) do
|
70
|
+
@events.exist?(event_id)
|
54
71
|
end
|
55
72
|
end
|
56
73
|
|
@@ -65,9 +82,9 @@ module RubyEventStore
|
|
65
82
|
end
|
66
83
|
|
67
84
|
def read_event(event_id)
|
68
|
-
|
69
|
-
|
70
|
-
|
85
|
+
guard_for(:not_found, event_id) do
|
86
|
+
@events.by_id(event_id)
|
87
|
+
end
|
71
88
|
end
|
72
89
|
|
73
90
|
def read(specification)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'ruby_event_store/rom'
|
2
|
+
require 'rom/memory'
|
3
|
+
require_relative 'adapters/memory/unit_of_work'
|
4
|
+
require_relative 'adapters/memory/relations/events'
|
5
|
+
require_relative 'adapters/memory/relations/stream_entries'
|
6
|
+
|
7
|
+
module RubyEventStore
|
8
|
+
module ROM
|
9
|
+
module Memory
|
10
|
+
class << self
|
11
|
+
def fetch_next_id
|
12
|
+
@last_id ||= 0
|
13
|
+
@mutex ||= Mutex.new
|
14
|
+
@mutex.synchronize { @last_id += 1 }
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup(config)
|
18
|
+
config.register_relation Relations::Events
|
19
|
+
config.register_relation Relations::StreamEntries
|
20
|
+
end
|
21
|
+
|
22
|
+
def configure(env)
|
23
|
+
env.register_unit_of_work_options(class: UnitOfWork)
|
24
|
+
|
25
|
+
env.register_error_handler :unique_violation, -> ex {
|
26
|
+
case ex
|
27
|
+
when TupleUniquenessError
|
28
|
+
raise EventDuplicatedInStream if ex.message =~ /event_id/
|
29
|
+
raise WrongExpectedEventVersion
|
30
|
+
end
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class SpecHelper
|
36
|
+
attr_reader :env
|
37
|
+
attr_reader :connection_pool_size, :close_pool_connection
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
@connection_pool_size = 5
|
41
|
+
@env = ROM.setup(:memory)
|
42
|
+
end
|
43
|
+
|
44
|
+
def run_lifecycle
|
45
|
+
yield
|
46
|
+
ensure
|
47
|
+
drop_gateway_schema
|
48
|
+
end
|
49
|
+
|
50
|
+
def gateway
|
51
|
+
env.container.gateways.fetch(:default)
|
52
|
+
end
|
53
|
+
|
54
|
+
def drop_gateway_schema
|
55
|
+
gateway.connection.data.values.each { |v| v.data.clear }
|
56
|
+
end
|
57
|
+
|
58
|
+
def close_gateway_connection
|
59
|
+
gateway.disconnect
|
60
|
+
end
|
61
|
+
|
62
|
+
def has_connection_pooling?
|
63
|
+
true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -47,7 +47,7 @@ module RubyEventStore
|
|
47
47
|
.offset(offset)
|
48
48
|
.take(limit)
|
49
49
|
.combine(:event)
|
50
|
-
.map_with(:stream_entry_to_serialized_record
|
50
|
+
.map_with(:stream_entry_to_serialized_record, auto_struct: false)
|
51
51
|
.to_ary
|
52
52
|
end
|
53
53
|
BatchEnumerator.new(batch_size, limit || Float::INFINITY, reader).each
|
@@ -56,7 +56,7 @@ module RubyEventStore
|
|
56
56
|
.ordered(direction, stream, offset_entry_id)
|
57
57
|
.take(limit)
|
58
58
|
.combine(:event)
|
59
|
-
.map_with(:stream_entry_to_serialized_record
|
59
|
+
.map_with(:stream_entry_to_serialized_record, auto_struct: false)
|
60
60
|
.each
|
61
61
|
end
|
62
62
|
end
|
@@ -12,22 +12,19 @@ module RubyEventStore
|
|
12
12
|
|
13
13
|
POSITION_SHIFT = 1.freeze
|
14
14
|
|
15
|
-
def create_changeset(event_ids, stream,
|
16
|
-
resolved_version = expected_version.resolve_for(stream, ->(_stream) {
|
17
|
-
(stream_entries.max_position(stream) || {})[:position]
|
18
|
-
})
|
19
|
-
|
15
|
+
def create_changeset(event_ids, stream, resolved_version, global_stream: nil)
|
20
16
|
tuples = []
|
21
|
-
|
17
|
+
|
22
18
|
event_ids.each_with_index do |event_id, index|
|
23
19
|
tuples << {
|
24
20
|
stream: stream.name,
|
25
21
|
position: resolved_version && resolved_version + index + POSITION_SHIFT,
|
26
22
|
event_id: event_id
|
27
|
-
|
23
|
+
} unless stream.global?
|
28
24
|
|
29
|
-
|
25
|
+
tuples << {
|
30
26
|
stream: stream_entries.class::SERIALIZED_GLOBAL_STREAM_NAME,
|
27
|
+
position: nil,
|
31
28
|
event_id: event_id
|
32
29
|
} if global_stream
|
33
30
|
end
|
@@ -36,11 +33,13 @@ module RubyEventStore
|
|
36
33
|
end
|
37
34
|
|
38
35
|
def delete(stream)
|
39
|
-
|
36
|
+
stream_entries.by_stream(stream).command(:delete).call
|
40
37
|
end
|
41
38
|
|
42
|
-
def
|
43
|
-
|
39
|
+
def resolve_version(stream, expected_version)
|
40
|
+
expected_version.resolve_for(stream, ->(_stream) {
|
41
|
+
(stream_entries.max_position(stream) || {})[:position]
|
42
|
+
})
|
44
43
|
end
|
45
44
|
end
|
46
45
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'rom/sql'
|
2
1
|
require 'ruby_event_store/rom'
|
2
|
+
require 'rom/sql'
|
3
3
|
require_relative 'adapters/sql/index_violation_detector'
|
4
4
|
require_relative 'adapters/sql/unit_of_work'
|
5
5
|
require_relative 'adapters/sql/relations/events'
|
@@ -44,14 +44,50 @@ module RubyEventStore
|
|
44
44
|
class SpecHelper
|
45
45
|
attr_reader :env
|
46
46
|
|
47
|
-
def initialize(
|
48
|
-
|
47
|
+
def initialize(database_uri = ENV['DATABASE_URL'])
|
48
|
+
config = ::ROM::Configuration.new(
|
49
|
+
:sql,
|
50
|
+
database_uri,
|
51
|
+
max_connections: database_uri =~ /sqlite/ ? 1 : 5,
|
52
|
+
preconnect: :concurrently,
|
53
|
+
# sql_mode: %w[NO_AUTO_VALUE_ON_ZERO STRICT_ALL_TABLES]
|
54
|
+
)
|
55
|
+
# $stdout.sync = true
|
56
|
+
# config.default.use_logger Logger.new(STDOUT)
|
57
|
+
# config.default.connection.pool.send(:preconnect, true)
|
58
|
+
config.default.run_migrations
|
59
|
+
|
60
|
+
@env = ROM.setup(config)
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_lifecycle
|
64
|
+
establish_gateway_connection
|
65
|
+
load_gateway_schema
|
66
|
+
|
67
|
+
yield
|
68
|
+
ensure
|
69
|
+
drop_gateway_schema
|
70
|
+
close_gateway_connection
|
49
71
|
end
|
50
72
|
|
51
73
|
def gateway
|
52
74
|
env.container.gateways.fetch(:default)
|
53
75
|
end
|
54
76
|
|
77
|
+
def has_connection_pooling?
|
78
|
+
!gateway.connection.database_type.eql?(:sqlite)
|
79
|
+
end
|
80
|
+
|
81
|
+
def connection_pool_size
|
82
|
+
gateway.connection.pool.size
|
83
|
+
end
|
84
|
+
|
85
|
+
def close_pool_connection
|
86
|
+
gateway.connection.pool.disconnect
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
55
91
|
def establish_gateway_connection
|
56
92
|
# Manually preconnect because disconnecting and reconnecting
|
57
93
|
# seems to lose the "preconnect concurrently" setting
|
@@ -1,6 +1,19 @@
|
|
1
1
|
module RubyEventStore
|
2
2
|
module ROM
|
3
3
|
class TupleUniquenessError < StandardError
|
4
|
+
class << self
|
5
|
+
def for_event_id(event_id)
|
6
|
+
new "Uniquness violated for event_id (#{event_id.inspect})"
|
7
|
+
end
|
8
|
+
|
9
|
+
def for_stream_and_event_id(stream_name, event_id)
|
10
|
+
new "Uniquness violated for stream (#{stream_name.inspect}) and event_id (#{event_id.inspect})"
|
11
|
+
end
|
12
|
+
|
13
|
+
def for_stream_and_position(stream_name, position)
|
14
|
+
new "Uniquness violated for stream (#{stream_name.inspect}) and position (#{position.inspect})"
|
15
|
+
end
|
16
|
+
end
|
4
17
|
end
|
5
18
|
end
|
6
19
|
end
|
data/lib/ruby_event_store/rom.rb
CHANGED
@@ -24,7 +24,7 @@ module RubyEventStore
|
|
24
24
|
container[:logger]
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
27
|
+
def unit_of_work(&block)
|
28
28
|
options = container[:unit_of_work_options].dup
|
29
29
|
options.delete(:class){UnitOfWork}.new(rom: self).call(**options, &block)
|
30
30
|
end
|
@@ -37,9 +37,15 @@ module RubyEventStore
|
|
37
37
|
container[:"#{type}_error_handlers"] << handler
|
38
38
|
end
|
39
39
|
|
40
|
-
def handle_error(type,
|
41
|
-
|
42
|
-
|
40
|
+
def handle_error(type, *args, swallow: [])
|
41
|
+
yield
|
42
|
+
rescue => ex
|
43
|
+
begin
|
44
|
+
container[:"#{type}_error_handlers"].each{ |h| h.call(ex, *args) }
|
45
|
+
raise ex
|
46
|
+
rescue *swallow
|
47
|
+
# swallow
|
48
|
+
end
|
43
49
|
end
|
44
50
|
end
|
45
51
|
|
@@ -51,8 +57,6 @@ module RubyEventStore
|
|
51
57
|
if adapter_name.is_a?(::ROM::Configuration)
|
52
58
|
# Call config block manually
|
53
59
|
Env.new ::ROM.container(adapter_name.tap(&block), &block)
|
54
|
-
elsif database_uri.nil?
|
55
|
-
raise ArgumentError.new('Missing database URI argument or DATABASE_URL environment variable')
|
56
60
|
else
|
57
61
|
Env.new ::ROM.container(adapter_name, database_uri, &block)
|
58
62
|
end
|