ruby_event_store-rom 0.30.0 → 0.31.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/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
|