ruby_event_store-rom 1.3.1 → 2.1.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/lib/ruby_event_store/rom/changesets/create_events.rb +10 -18
  4. data/lib/ruby_event_store/rom/changesets/create_stream_entries.rb +3 -12
  5. data/lib/ruby_event_store/rom/changesets/update_events.rb +33 -19
  6. data/lib/ruby_event_store/rom/event_repository.rb +65 -61
  7. data/lib/ruby_event_store/rom/index_violation_detector.rb +25 -0
  8. data/lib/ruby_event_store/rom/mappers/event_to_serialized_record.rb +10 -6
  9. data/lib/ruby_event_store/rom/mappers/stream_entry_to_serialized_record.rb +11 -6
  10. data/lib/ruby_event_store/rom/rake_task.rb +5 -0
  11. data/lib/ruby_event_store/rom/relations/events.rb +78 -0
  12. data/lib/ruby_event_store/rom/relations/stream_entries.rb +83 -0
  13. data/lib/ruby_event_store/rom/repositories/events.rb +55 -30
  14. data/lib/ruby_event_store/rom/repositories/stream_entries.rb +15 -16
  15. data/lib/ruby_event_store/rom/tasks/migration_tasks.rake +36 -0
  16. data/lib/ruby_event_store/rom/types.rb +16 -14
  17. data/lib/ruby_event_store/rom/unit_of_work.rb +28 -13
  18. data/lib/ruby_event_store/rom/version.rb +1 -1
  19. data/lib/ruby_event_store/rom.rb +28 -103
  20. data/lib/ruby_event_store-rom.rb +1 -1
  21. metadata +31 -49
  22. data/.rubocop.yml +0 -1
  23. data/.rubocop_todo.yml +0 -84
  24. data/CHANGELOG.md +0 -9
  25. data/Gemfile +0 -12
  26. data/Makefile +0 -57
  27. data/Rakefile +0 -20
  28. data/db/migrate/20180327044629_create_ruby_event_store_tables.rb +0 -54
  29. data/db/migrate/20181026152045_index_by_event_type.rb +0 -9
  30. data/lib/ruby_event_store/rom/adapters/memory/changesets/create_events.rb +0 -19
  31. data/lib/ruby_event_store/rom/adapters/memory/changesets/create_stream_entries.rb +0 -19
  32. data/lib/ruby_event_store/rom/adapters/memory/changesets/update_events.rb +0 -18
  33. data/lib/ruby_event_store/rom/adapters/memory/relations/events.rb +0 -56
  34. data/lib/ruby_event_store/rom/adapters/memory/relations/stream_entries.rb +0 -114
  35. data/lib/ruby_event_store/rom/adapters/memory/unit_of_work.rb +0 -36
  36. data/lib/ruby_event_store/rom/adapters/sql/changesets/create_events.rb +0 -15
  37. data/lib/ruby_event_store/rom/adapters/sql/changesets/update_events.rb +0 -41
  38. data/lib/ruby_event_store/rom/adapters/sql/index_violation_detector.rb +0 -31
  39. data/lib/ruby_event_store/rom/adapters/sql/rake_task.rb +0 -5
  40. data/lib/ruby_event_store/rom/adapters/sql/relations/events.rb +0 -27
  41. data/lib/ruby_event_store/rom/adapters/sql/relations/stream_entries.rb +0 -72
  42. data/lib/ruby_event_store/rom/adapters/sql/tasks/migration_tasks.rake +0 -36
  43. data/lib/ruby_event_store/rom/memory.rb +0 -82
  44. data/lib/ruby_event_store/rom/sql.rb +0 -169
  45. data/lib/ruby_event_store/rom/tuple_uniqueness_error.rb +0 -21
  46. data/lib/ruby_event_store/spec/rom/event_repository_lint.rb +0 -176
  47. data/lib/ruby_event_store/spec/rom/relations/events_lint.rb +0 -75
  48. data/lib/ruby_event_store/spec/rom/relations/stream_entries_lint.rb +0 -198
  49. data/lib/ruby_event_store/spec/rom/spec_helper_lint.rb +0 -15
  50. data/lib/ruby_event_store/spec/rom/unit_of_work_lint.rb +0 -37
  51. data/ruby_event_store-rom.gemspec +0 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 301643330ee509f7bb50b8eaab0c25ae261f57fbaeb8e6482c0c5e08e963aa6a
4
- data.tar.gz: fb44e5a4cea7fb9fc1ddcc2d20b3e9f2e1ca31f4d55d3d12385ddc3c5ceb7ed9
3
+ metadata.gz: 3af23fb98d5ccf4b0730bfd4da1dfea36b511d5b7d00e82158daaa7ed3ba6979
4
+ data.tar.gz: 741f05db9dc98c607888e5a3a07a8289a3d02094e8deecfcf48da150d6388954
5
5
  SHA512:
6
- metadata.gz: d0678314a60a994e96459db4016d5f5d3b5282a2bef564e1f77ed038d47640882739aa5190913aca160707ca8e63c68ac8597419199c1cea8fdf0e0bcf4e605a
7
- data.tar.gz: b5b4749ffc9e53e85f9eba739777b23340bd8e628cb3d10a204cf42c33876ea0989a7dc58674b2912ac8dd0c936672c0fef5c5c2992dc70225788d746fadd4f4
6
+ metadata.gz: f6846ccb455e16a59fe7a3e655f912f77998af55527d7e0c49278f52454d3d283e9113b70d7fff4289268dc13018d3b0ab4e0dcb774767a313078c8081e7d7fa
7
+ data.tar.gz: b02ae36dbe6641fda9bb4005821f220954f1ff09672736f2f895963ba0fa366442b1aa21100fee0b20663d9b718d5f66f2a28461419caf1b45c6589cefd92bc3
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # RubyEventStore ROM Event Repository
2
2
 
3
+ ![RubyEventStore ROM Event Repository](https://github.com/RailsEventStore/rails_event_store/workflows/ruby_event_store-rom/badge.svg)
4
+
3
5
  A Ruby Object Model (ROM) implementation of events repository for [Ruby Event Store](https://github.com/RailsEventStore/rails_event_store).
4
6
 
5
7
  This version of the ROM adapter supports [rom-sql](https://github.com/rom-rb/rom-sql) at this time. It is an alternative to the ActiveRecord `EventRepository` implementation used in `rails_event_store` gem.
6
8
 
7
9
  [Read the docs to get started.](http://railseventstore.org/docs/repository/)
8
-
9
- _Additonal backing stores via ROM are being tracked here: [#299](https://github.com/RailsEventStore/rails_event_store/issues/299)._
@@ -4,27 +4,19 @@ module RubyEventStore
4
4
  module ROM
5
5
  module Changesets
6
6
  class CreateEvents < ::ROM::Changeset::Create
7
- module Defaults
8
- def self.included(base)
9
- base.class_eval do
10
- relation :events
7
+ relation :events
11
8
 
12
- # Convert to Hash
13
- map(&:to_h)
14
-
15
- map do
16
- rename_keys event_id: :id
17
- accept_keys %i[id data metadata event_type]
18
- end
19
-
20
- map do |tuple|
21
- Hash(created_at: RubyEventStore::ROM::Types::DateTime.call(nil)).merge(tuple)
22
- end
23
- end
24
- end
9
+ map(&:to_h)
10
+ map do
11
+ rename_keys timestamp: :created_at
12
+ map_value :created_at, ->(time) { Time.iso8601(time).localtime }
13
+ map_value :valid_at, ->(time) { Time.iso8601(time).localtime }
14
+ accept_keys %i[event_id data metadata event_type created_at valid_at]
25
15
  end
26
16
 
27
- include Defaults
17
+ def commit
18
+ relation.multi_insert(to_a)
19
+ end
28
20
  end
29
21
  end
30
22
  end
@@ -4,19 +4,10 @@ module RubyEventStore
4
4
  module ROM
5
5
  module Changesets
6
6
  class CreateStreamEntries < ::ROM::Changeset::Create
7
- module Defaults
8
- def self.included(base)
9
- base.class_eval do
10
- relation :stream_entries
7
+ relation :stream_entries
11
8
 
12
- map do |tuple|
13
- Hash(created_at: RubyEventStore::ROM::Types::DateTime.call(nil)).merge(tuple)
14
- end
15
- end
16
- end
17
- end
18
-
19
- include Defaults
9
+ map { |tuple| Hash(created_at: RubyEventStore::ROM::Types::DateTime.call(nil)).merge(tuple) }
10
+ map { map_value :created_at, ->(datetime) { datetime.to_time.localtime } }
20
11
  end
21
12
  end
22
13
  end
@@ -4,27 +4,41 @@ module RubyEventStore
4
4
  module ROM
5
5
  module Changesets
6
6
  class UpdateEvents < ::ROM::Changeset::Update
7
- module Defaults
8
- def self.included(base)
9
- base.class_eval do
10
- relation :events
11
-
12
- # Convert to Hash
13
- map(&:to_h)
14
-
15
- map do
16
- rename_keys event_id: :id
17
- accept_keys %i[id data metadata event_type created_at]
18
- end
19
-
20
- map do |tuple|
21
- Hash(created_at: RubyEventStore::ROM::Types::DateTime.call(nil)).merge(tuple)
22
- end
23
- end
24
- end
7
+ relation :events
8
+
9
+ map(&:to_h)
10
+ map do
11
+ rename_keys timestamp: :created_at
12
+ map_value :created_at, ->(time) { Time.iso8601(time).localtime }
13
+ map_value :valid_at, ->(time) { Time.iso8601(time).localtime }
14
+ accept_keys %i[event_id data metadata event_type created_at valid_at]
15
+ end
16
+
17
+ UPSERT_COLUMNS = %i[event_type data metadata valid_at].freeze
18
+
19
+ def commit
20
+ supports_on_duplicate_key_update? ? commit_on_duplicate_key_update : commit_insert_conflict_update
21
+ end
22
+
23
+ private
24
+
25
+ def supports_on_duplicate_key_update?
26
+ relation.dataset.db.adapter_scheme =~ /mysql/
25
27
  end
26
28
 
27
- include Defaults
29
+ def commit_on_duplicate_key_update
30
+ relation.dataset.on_duplicate_key_update(*UPSERT_COLUMNS).multi_insert(to_a)
31
+ end
32
+
33
+ def commit_insert_conflict_update
34
+ relation
35
+ .dataset
36
+ .insert_conflict(
37
+ target: :event_id,
38
+ update: UPSERT_COLUMNS.each_with_object({}) { |column, memo| memo[column] = Sequel[:excluded][column] }
39
+ )
40
+ .multi_insert(to_a)
41
+ end
28
42
  end
29
43
  end
30
44
  end
@@ -1,41 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ruby_event_store/rom/unit_of_work'
4
- require 'forwardable'
5
-
6
3
  module RubyEventStore
7
4
  module ROM
8
5
  class EventRepository
9
- extend Forwardable
10
-
11
- def_delegator :@rom, :handle_error, :guard_for
12
- def_delegators :@rom, :unit_of_work
13
-
14
- def initialize(rom: ROM.env)
15
- raise ArgumentError, 'Must specify rom' unless rom && rom.instance_of?(Env)
16
-
17
- @rom = rom
18
- @events = Repositories::Events.new(rom.rom_container)
19
- @stream_entries = Repositories::StreamEntries.new(rom.rom_container)
6
+ def initialize(rom:, serializer:)
7
+ @serializer = serializer
8
+ @events = Repositories::Events.new(rom)
9
+ @stream_entries = Repositories::StreamEntries.new(rom)
10
+ @unit_of_work = UnitOfWork.new(rom.gateways.fetch(:default))
20
11
  end
21
12
 
22
- def append_to_stream(events, stream, expected_version)
23
- events = Array(events)
24
- event_ids = events.map(&:event_id)
25
-
26
- guard_for(:unique_violation) do
27
- unit_of_work do |changesets|
28
- # Create changesets inside transaction because
29
- # we want to find the last position (a.k.a. version)
30
- # again if the transaction is retried due to a
31
- # deadlock in MySQL
32
- changesets << @events.create_changeset(events)
33
- changesets << @stream_entries.create_changeset(
34
- event_ids,
35
- stream,
36
- @stream_entries.resolve_version(stream, expected_version),
37
- global_stream: true
38
- )
13
+ def append_to_stream(records, stream, expected_version)
14
+ serialized_records = records.map { |record| record.serialize(@serializer) }
15
+ event_ids = records.map(&:event_id)
16
+
17
+ handle_unique_violation do
18
+ @unit_of_work.call do |changesets|
19
+ changesets << @events.create_changeset(serialized_records)
20
+ changesets <<
21
+ @stream_entries.create_changeset(
22
+ event_ids,
23
+ stream,
24
+ @stream_entries.resolve_version(stream, expected_version)
25
+ )
39
26
  end
40
27
  end
41
28
 
@@ -43,64 +30,81 @@ module RubyEventStore
43
30
  end
44
31
 
45
32
  def link_to_stream(event_ids, stream, expected_version)
46
- event_ids = Array(event_ids)
47
-
48
- # Validate event IDs
49
- @events
50
- .find_nonexistent_pks(event_ids)
51
- .each { |id| raise EventNotFound, id }
52
-
53
- guard_for(:unique_violation) do
54
- unit_of_work do |changesets|
55
- changesets << @stream_entries.create_changeset(
56
- event_ids,
57
- stream,
58
- @stream_entries.resolve_version(stream, expected_version)
59
- )
33
+ validate_event_ids(event_ids)
34
+
35
+ handle_unique_violation do
36
+ @unit_of_work.call do |changesets|
37
+ changesets <<
38
+ @stream_entries.create_changeset(
39
+ event_ids,
40
+ stream,
41
+ @stream_entries.resolve_version(stream, expected_version)
42
+ )
60
43
  end
61
44
  end
62
45
 
63
46
  self
64
47
  end
65
48
 
49
+ def position_in_stream(event_id, stream)
50
+ @stream_entries.position_in_stream(event_id, stream)
51
+ end
52
+
53
+ def global_position(event_id)
54
+ @events.global_position(event_id)
55
+ end
56
+
57
+ def event_in_stream?(event_id, stream)
58
+ @stream_entries.event_in_stream?(event_id, stream)
59
+ end
60
+
66
61
  def delete_stream(stream)
67
62
  @stream_entries.delete(stream)
68
63
  end
69
64
 
70
65
  def has_event?(event_id)
71
- guard_for(:not_found, event_id, swallow: EventNotFound) { @events.exist?(event_id) } || false
66
+ @events.exist?(event_id)
67
+ rescue Sequel::DatabaseError => doh
68
+ raise doh unless doh.message =~ /PG::InvalidTextRepresentation.*uuid/
69
+ false
72
70
  end
73
71
 
74
72
  def last_stream_event(stream)
75
- @events.last_stream_event(stream)
73
+ @events.last_stream_event(stream, @serializer)
76
74
  end
77
75
 
78
76
  def read(specification)
79
- raise ReservedInternalName if specification.stream.name.eql?(@stream_entries.stream_entries.class::SERIALIZED_GLOBAL_STREAM_NAME)
80
-
81
- @events.read(specification)
77
+ @events.read(specification, @serializer)
82
78
  end
83
79
 
84
80
  def count(specification)
85
- raise ReservedInternalName if specification.stream.name.eql?(@stream_entries.stream_entries.class::SERIALIZED_GLOBAL_STREAM_NAME)
86
-
87
81
  @events.count(specification)
88
82
  end
89
83
 
90
- def update_messages(messages)
91
- # Validate event IDs
92
- @events
93
- .find_nonexistent_pks(messages.map(&:event_id))
94
- .each { |id| raise EventNotFound, id }
84
+ def update_messages(records)
85
+ validate_event_ids(records.map(&:event_id))
95
86
 
96
- unit_of_work do |changesets|
97
- changesets << @events.update_changeset(messages)
87
+ @unit_of_work.call do |changesets|
88
+ serialized_records = records.map { |record| record.serialize(@serializer) }
89
+ changesets << @events.update_changeset(serialized_records)
98
90
  end
99
91
  end
100
92
 
101
93
  def streams_of(event_id)
102
- @stream_entries.streams_of(event_id)
103
- .map { |name| Stream.new(name) }
94
+ @stream_entries.streams_of(event_id).map { |name| Stream.new(name) }
95
+ end
96
+
97
+ private
98
+
99
+ def validate_event_ids(event_ids)
100
+ @events.find_nonexistent_pks(event_ids).each { |id| raise EventNotFound, id }
101
+ end
102
+
103
+ def handle_unique_violation
104
+ yield
105
+ rescue ::ROM::SQL::UniqueConstraintError, Sequel::UniqueConstraintViolation => doh
106
+ raise ::RubyEventStore::EventDuplicatedInStream if IndexViolationDetector.new.detect(doh.message)
107
+ raise ::RubyEventStore::WrongExpectedEventVersion
104
108
  end
105
109
  end
106
110
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ module ROM
5
+ class IndexViolationDetector
6
+ MYSQL5_PKEY_ERROR = "for key 'index_event_store_events_on_event_id'".freeze
7
+ MYSQL8_PKEY_ERROR = "for key 'event_store_events.index_event_store_events_on_event_id'".freeze
8
+ POSTGRES_PKEY_ERROR = "Key (event_id)".freeze
9
+ SQLITE3_PKEY_ERROR = "event_store_events.event_id".freeze
10
+
11
+ MYSQL5_INDEX_ERROR = "for key 'index_event_store_events_in_streams_on_stream_and_event_id'".freeze
12
+ MYSQL8_INDEX_ERROR =
13
+ "for key 'event_store_events_in_streams.index_event_store_events_in_streams_on_stream_and_event_id'".freeze
14
+ POSTGRES_INDEX_ERROR = "Key (stream, event_id)".freeze
15
+ SQLITE3_INDEX_ERROR = "event_store_events_in_streams.stream, event_store_events_in_streams.event_id".freeze
16
+
17
+ def detect(message)
18
+ message.include?(MYSQL5_PKEY_ERROR) || message.include?(MYSQL8_PKEY_ERROR) ||
19
+ message.include?(POSTGRES_PKEY_ERROR) || message.include?(SQLITE3_PKEY_ERROR) ||
20
+ message.include?(MYSQL5_INDEX_ERROR) || message.include?(MYSQL8_INDEX_ERROR) ||
21
+ message.include?(POSTGRES_INDEX_ERROR) || message.include?(SQLITE3_INDEX_ERROR)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rom/transformer'
4
-
5
3
  module RubyEventStore
6
4
  module ROM
7
5
  module Mappers
@@ -9,10 +7,16 @@ module RubyEventStore
9
7
  relation :events
10
8
  register_as :event_to_serialized_record
11
9
 
12
- map_array do
13
- rename_keys id: :event_id
14
- accept_keys %i[event_id data metadata event_type]
15
- constructor_inject RubyEventStore::SerializedRecord
10
+ map do
11
+ map_value :created_at, ->(time) { time.iso8601(TIMESTAMP_PRECISION) }
12
+ map_value :valid_at, ->(time) { time.iso8601(TIMESTAMP_PRECISION) }
13
+ rename_keys created_at: :timestamp
14
+ accept_keys %i[event_id data metadata event_type timestamp valid_at]
15
+ create_serialized_record
16
+ end
17
+
18
+ def create_serialized_record(attributes)
19
+ RubyEventStore::SerializedRecord.new(**attributes)
16
20
  end
17
21
  end
18
22
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rom/transformer'
4
-
5
3
  module RubyEventStore
6
4
  module ROM
7
5
  module Mappers
@@ -9,10 +7,17 @@ module RubyEventStore
9
7
  relation :stream_entries
10
8
  register_as :stream_entry_to_serialized_record
11
9
 
12
- map_array do
13
- unwrap :event, %i[data metadata event_type]
14
- accept_keys %i[event_id data metadata event_type]
15
- constructor_inject RubyEventStore::SerializedRecord
10
+ map do
11
+ unwrap :event, %i[event_id data metadata event_type created_at valid_at]
12
+ map_value :created_at, ->(time) { time.iso8601(TIMESTAMP_PRECISION) }
13
+ map_value :valid_at, ->(time) { time.iso8601(TIMESTAMP_PRECISION) }
14
+ rename_keys created_at: :timestamp
15
+ accept_keys %i[event_id data metadata event_type timestamp valid_at]
16
+ create_serialized_record
17
+ end
18
+
19
+ def create_serialized_record(attributes)
20
+ RubyEventStore::SerializedRecord.new(**attributes)
16
21
  end
17
22
  end
18
23
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/sql/rake_task"
4
+ require "ruby_event_store/rom"
5
+ load "ruby_event_store/rom/tasks/migration_tasks.rake"
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ module ROM
5
+ module Relations
6
+ class Events < ::ROM::Relation[:sql]
7
+ schema(:event_store_events, as: :events, infer: true) do
8
+ attribute :event_id, ::ROM::Types::String
9
+ attribute :data,
10
+ RubyEventStore::ROM::Types::RecordSerializer,
11
+ read: RubyEventStore::ROM::Types::RecordDeserializer
12
+ attribute :metadata,
13
+ RubyEventStore::ROM::Types::RecordSerializer,
14
+ read: RubyEventStore::ROM::Types::RecordDeserializer
15
+ attribute :created_at, RubyEventStore::ROM::Types::DateTime
16
+ attribute :valid_at, RubyEventStore::ROM::Types::DateTime
17
+
18
+ primary_key :event_id
19
+ end
20
+
21
+ def create_changeset(tuples)
22
+ events.changeset(Changesets::CreateEvents, tuples)
23
+ end
24
+
25
+ def update_changeset(tuples)
26
+ events.changeset(Changesets::UpdateEvents, tuples)
27
+ end
28
+
29
+ def by_event_id(event_id)
30
+ where(event_id: event_id)
31
+ end
32
+
33
+ def by_event_type(types)
34
+ where(event_type: types)
35
+ end
36
+
37
+ def newer_than(time)
38
+ where { |r| r.events[:created_at] > time.localtime }
39
+ end
40
+
41
+ def newer_than_or_equal(time)
42
+ where { |r| r.events[:created_at] >= time.localtime }
43
+ end
44
+
45
+ def older_than(time)
46
+ where { |r| r.events[:created_at] < time.localtime }
47
+ end
48
+
49
+ def older_than_or_equal(time)
50
+ where { |r| r.events[:created_at] <= time.localtime }
51
+ end
52
+
53
+ DIRECTION_MAP = { forward: %i[asc > <], backward: %i[desc < >] }.freeze
54
+
55
+ def ordered(direction, offset_entry_id = nil, stop_entry_id = nil, time_sort_by = nil)
56
+ order, operator_offset, operator_stop = DIRECTION_MAP[direction]
57
+
58
+ raise ArgumentError, "Direction must be :forward or :backward" if order.nil?
59
+
60
+ event_order_columns = [:id]
61
+
62
+ case time_sort_by
63
+ when :as_at
64
+ event_order_columns.unshift :created_at
65
+ when :as_of
66
+ event_order_columns.unshift :valid_at
67
+ end
68
+
69
+ query = self
70
+ query = query.where { id.public_send(operator_offset, offset_entry_id) } if offset_entry_id
71
+ query = query.where { id.public_send(operator_stop, stop_entry_id) } if stop_entry_id
72
+
73
+ query.order(*event_order_columns.map { |c| events[c].public_send(order) })
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ module ROM
5
+ module Relations
6
+ class StreamEntries < ::ROM::Relation[:sql]
7
+ schema(:event_store_events_in_streams, as: :stream_entries, infer: true) do
8
+ attribute :created_at, RubyEventStore::ROM::Types::DateTime
9
+
10
+ associations { belongs_to :events, as: :event, foreign_key: :event_id }
11
+ end
12
+
13
+ def create_changeset(tuples)
14
+ changeset(ROM::Changesets::CreateStreamEntries, tuples)
15
+ end
16
+
17
+ def by_stream(stream)
18
+ where(stream: stream.name)
19
+ end
20
+
21
+ def by_event_id(event_id)
22
+ where(event_id: event_id)
23
+ end
24
+
25
+ def by_event_type(types)
26
+ join(:events).where(event_type: types)
27
+ end
28
+
29
+ def by_stream_and_event_id(stream, event_id)
30
+ where(stream: stream.name, event_id: event_id).one!
31
+ end
32
+
33
+ def max_position(stream)
34
+ by_stream(stream).select(:position).order(Sequel.desc(:position)).first
35
+ end
36
+
37
+ def newer_than(time)
38
+ join(:events).where { |r| r.events[:created_at] > time.localtime }
39
+ end
40
+
41
+ def newer_than_or_equal(time)
42
+ join(:events).where { |r| r.events[:created_at] >= time.localtime }
43
+ end
44
+
45
+ def older_than(time)
46
+ join(:events).where { |r| r.events[:created_at] < time.localtime }
47
+ end
48
+
49
+ def older_than_or_equal(time)
50
+ join(:events).where { |r| r.events[:created_at] <= time.localtime }
51
+ end
52
+
53
+ DIRECTION_MAP = { forward: %i[asc > <], backward: %i[desc < >] }.freeze
54
+
55
+ def ordered(direction, stream, offset_entry_id = nil, stop_entry_id = nil, time_sort_by = nil)
56
+ order, operator_offset, operator_stop = DIRECTION_MAP[direction]
57
+
58
+ raise ArgumentError, "Direction must be :forward or :backward" if order.nil?
59
+
60
+ event_order_columns = []
61
+ stream_order_columns = %i[id]
62
+
63
+ case time_sort_by
64
+ when :as_at
65
+ event_order_columns.unshift :created_at
66
+ when :as_of
67
+ event_order_columns.unshift :valid_at
68
+ end
69
+
70
+ query = by_stream(stream)
71
+ query = query.where { id.public_send(operator_offset, offset_entry_id) } if offset_entry_id
72
+ query = query.where { id.public_send(operator_stop, stop_entry_id) } if stop_entry_id
73
+
74
+ if event_order_columns.empty?
75
+ query.order { |r| stream_order_columns.map { |c| r[:stream_entries][c].public_send(order) } }
76
+ else
77
+ query.join(:events).order { |r| event_order_columns.map { |c| r.events[c].public_send(order) } }
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end