ruby_event_store-active_record 2.17.1 → 2.19.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b87772800a0cfa9041b04341fd4b652cb19ed7c651739986f80a7bf0bb17c873
4
- data.tar.gz: 351574e60ba2f36792f689a45fd0bac8503a69bb4622173742147034c58e5d3d
3
+ metadata.gz: 4e59b38492f8cd12d3131547e67dacc272ef2dd0e6df85934fabdf115ea9489f
4
+ data.tar.gz: 63e1cefc86fb9e06396ea293edc9e52e1b3ef6cc161a25cf90f3a5fd6c07f3af
5
5
  SHA512:
6
- metadata.gz: db286b07eac4402fd8a83a143d9501246b6cef625ee2bac0db42dc1a3dc979d48b3526e89ae990cda4202b2ac5187942e2356320fefaf915fec8a0f7c24530f8
7
- data.tar.gz: 3dc022ad1801d62547c91e7b0f7967bbc0a564a147b00d2a67c024bd8859fb00e8c7b3647423db6c5bcc447e2b644727b879f01accc67826e0989f2f0cc84439
6
+ metadata.gz: ca70c4f361e079a0ce1734a5acc246524a2b619f910a3fcacabe3beeb1bbc44b8bdf49c87b3c70ae68818e3eb569687180f24b6f7b6d8938de8843372a981473
7
+ data.tar.gz: 7691d7f87960edf26d4c5ef89087ae6faef317cd0f12455fd9fbd6cbdd3ab4fb6c1126cedd981846d33aa3c88a9ff432e6da13b19a46de7ee4846f49487aa631
@@ -2,4 +2,9 @@
2
2
 
3
3
  require "ruby_event_store/active_record"
4
4
 
5
+ warn <<~EOW
6
+ The 'rails_event_store_active_record' gem has been renamed and is deprecated.
7
+ Please change your Gemfile or gemspec to use 'ruby_event_store-active_record' instead.
8
+ EOW
9
+
5
10
  RailsEventStoreActiveRecord = RubyEventStore::ActiveRecord
@@ -7,48 +7,23 @@ module RubyEventStore
7
7
 
8
8
  def initialize(model_factory: WithDefaultModels.new, serializer:)
9
9
  @serializer = serializer
10
- @event_klass, @stream_klass = model_factory.call
11
- if serializer == NULL && json_data_type?
12
- warn <<~MSG
13
- The data or metadata column is of a JSON/B type and expects a JSON string.
14
-
15
- Yet the repository serializer is configured as #{serializer} and it would not
16
- produce the expected JSON string.
17
-
18
- In ActiveRecord there's an implicit serialization to JSON for JSON/B column types
19
- that made it work so far. This behaviour is unfortunately also a source of undesired
20
- double serialization — first in the EventRepository, second in the ActiveRecord.
21
-
22
- In the past we've advised workarounds that introduced configuration incosistency
23
- with other data types and serialization formats, i.e. explicitly passing NULL serializer
24
- just for the JSON/B data types.
25
-
26
- As of now this special ActiveRecord behaviour is disabled. You should be using JSON
27
- serializer back again:
28
-
29
- RubyEventStore::ActiveRecord::EventRepository.new(serializer: JSON)
30
- MSG
31
- else
32
- @event_klass.include(SkipJsonSerialization)
33
- end
34
- @repo_reader = EventRepositoryReader.new(@event_klass, @stream_klass, serializer)
35
- @index_violation_detector = IndexViolationDetector.new(@event_klass.table_name, @stream_klass.table_name)
10
+ @model_factory = model_factory
36
11
  end
37
12
 
38
13
  def rescue_from_double_json_serialization!
39
- if serializer == JSON && json_data_type?
40
- @repo_reader.instance_eval { alias __record__ record }
14
+ if @serializer == JSON && json_data_type?(event_klass)
15
+ repo_reader.instance_eval { alias __record__ record }
41
16
 
42
- @repo_reader.define_singleton_method :unwrap do |column_name, payload|
17
+ repo_reader.define_singleton_method :unwrap do |column_name, payload|
43
18
  if String === payload && payload.start_with?("\{")
44
19
  warn "Double serialization of #{column_name} column detected"
45
- serializer.load(payload)
20
+ @serializer.load(payload)
46
21
  else
47
22
  payload
48
23
  end
49
24
  end
50
25
 
51
- @repo_reader.define_singleton_method :record do |record|
26
+ repo_reader.define_singleton_method :record do |record|
52
27
  r = __record__(record)
53
28
 
54
29
  Record.new(
@@ -76,31 +51,31 @@ module RubyEventStore
76
51
  end
77
52
 
78
53
  def delete_stream(stream)
79
- @stream_klass.where(stream: stream.name).delete_all
54
+ stream_klass.where(stream: stream.name).delete_all
80
55
  end
81
56
 
82
57
  def has_event?(event_id)
83
- @repo_reader.has_event?(event_id)
58
+ repo_reader.has_event?(event_id)
84
59
  end
85
60
 
86
61
  def last_stream_event(stream)
87
- @repo_reader.last_stream_event(stream)
62
+ repo_reader.last_stream_event(stream)
88
63
  end
89
64
 
90
65
  def read(specification)
91
- @repo_reader.read(specification)
66
+ repo_reader.read(specification)
92
67
  end
93
68
 
94
69
  def count(specification)
95
- @repo_reader.count(specification)
70
+ repo_reader.count(specification)
96
71
  end
97
72
 
98
73
  def update_messages(records)
99
- hashes = records.map { |record| upsert_hash(record, record.serialize(serializer)) }
74
+ hashes = records.map { |record| upsert_hash(record, record.serialize(@serializer)) }
100
75
  for_update = records.map(&:event_id)
101
76
  start_transaction do
102
77
  existing =
103
- @event_klass
78
+ event_klass
104
79
  .where(event_id: for_update)
105
80
  .pluck(:event_id, :id, :created_at)
106
81
  .reduce({}) { |acc, (event_id, id, created_at)| acc.merge(event_id => [id, created_at]) }
@@ -109,33 +84,31 @@ module RubyEventStore
109
84
  h[:id] = existing.fetch(h.fetch(:event_id)).at(0)
110
85
  h[:created_at] = existing.fetch(h.fetch(:event_id)).at(1)
111
86
  end
112
- @event_klass.upsert_all(hashes)
87
+ event_klass.upsert_all(hashes)
113
88
  end
114
89
  end
115
90
 
116
91
  def streams_of(event_id)
117
- @repo_reader.streams_of(event_id)
92
+ repo_reader.streams_of(event_id)
118
93
  end
119
94
 
120
95
  def position_in_stream(event_id, stream)
121
- @repo_reader.position_in_stream(event_id, stream)
96
+ repo_reader.position_in_stream(event_id, stream)
122
97
  end
123
98
 
124
99
  def global_position(event_id)
125
- @repo_reader.global_position(event_id)
100
+ repo_reader.global_position(event_id)
126
101
  end
127
102
 
128
103
  def event_in_stream?(event_id, stream)
129
- @repo_reader.event_in_stream?(event_id, stream)
104
+ repo_reader.event_in_stream?(event_id, stream)
130
105
  end
131
106
 
132
107
  private
133
108
 
134
- attr_reader :serializer
135
-
136
109
  def add_to_stream(event_ids, stream, expected_version)
137
110
  last_stream_version = ->(stream_) do
138
- @stream_klass.where(stream: stream_.name).order("position DESC").first.try(:position)
111
+ stream_klass.where(stream: stream_.name).order("position DESC").first.try(:position)
139
112
  end
140
113
  resolved_version = expected_version.resolve_for(stream, last_stream_version)
141
114
 
@@ -150,7 +123,7 @@ module RubyEventStore
150
123
  created_at: Time.now.utc,
151
124
  }
152
125
  end
153
- @stream_klass.insert_all!(in_stream) unless stream.global?
126
+ stream_klass.insert_all!(in_stream) unless stream.global?
154
127
  end
155
128
  self
156
129
  rescue ::ActiveRecord::RecordNotUnique => e
@@ -167,7 +140,7 @@ module RubyEventStore
167
140
  end
168
141
 
169
142
  def detect_index_violated(message)
170
- @index_violation_detector.detect(message)
143
+ index_violation_detector.detect(message)
171
144
  end
172
145
 
173
146
  def insert_hash(record, serialized_record)
@@ -196,11 +169,11 @@ module RubyEventStore
196
169
  end
197
170
 
198
171
  def start_transaction(&block)
199
- @event_klass.transaction(requires_new: true, &block)
172
+ event_klass.transaction(requires_new: true, &block)
200
173
  end
201
174
 
202
175
  def link_to_stream_(event_ids, stream, expected_version)
203
- (event_ids - @event_klass.where(event_id: event_ids).pluck(:event_id)).each { |id| raise EventNotFound.new(id) }
176
+ (event_ids - event_klass.where(event_id: event_ids).pluck(:event_id)).each { |id| raise EventNotFound.new(id) }
204
177
  add_to_stream(event_ids, stream, expected_version)
205
178
  end
206
179
 
@@ -208,16 +181,53 @@ module RubyEventStore
208
181
  hashes = []
209
182
  event_ids = []
210
183
  records.each do |record|
211
- hashes << insert_hash(record, record.serialize(serializer))
184
+ hashes << insert_hash(record, record.serialize(@serializer))
212
185
  event_ids << record.event_id
213
186
  end
214
- add_to_stream(event_ids, stream, expected_version) { @event_klass.insert_all!(hashes) }
187
+ add_to_stream(event_ids, stream, expected_version) { event_klass.insert_all!(hashes) }
215
188
  end
216
189
 
217
- private
190
+ def model_klasses
191
+ @model_klasses ||= @model_factory.call.tap do |event_model, stream_model|
192
+ if @serializer == NULL && json_data_type?(event_model)
193
+ warn <<~MSG
194
+ The data or metadata column is of a JSON/B type and expects a JSON string.
195
+
196
+ Yet the repository serializer is configured as #{@serializer} and it would not
197
+ produce the expected JSON string.
198
+
199
+ In ActiveRecord there's an implicit serialization to JSON for JSON/B column types
200
+ that made it work so far. This behaviour is unfortunately also a source of undesired
201
+ double serialization — first in the EventRepository, second in the ActiveRecord.
202
+
203
+ In the past we've advised workarounds that introduced configuration incosistency
204
+ with other data types and serialization formats, i.e. explicitly passing NULL serializer
205
+ just for the JSON/B data types.
206
+
207
+ As of now this special ActiveRecord behaviour is disabled. You should be using JSON
208
+ serializer back again:
209
+
210
+ RubyEventStore::ActiveRecord::EventRepository.new(serializer: JSON)
211
+ MSG
212
+ else
213
+ event_model.include(SkipJsonSerialization)
214
+ end
215
+ end
216
+ end
217
+
218
+ def event_klass = model_klasses.first
219
+ def stream_klass = model_klasses.last
220
+
221
+ def repo_reader
222
+ @repo_reader ||= EventRepositoryReader.new(event_klass, stream_klass, @serializer)
223
+ end
224
+
225
+ def index_violation_detector
226
+ @index_violation_detector ||= IndexViolationDetector.new(event_klass.table_name, stream_klass.table_name)
227
+ end
218
228
 
219
- def json_data_type?
220
- %i[data metadata].any? { |attr| @event_klass.column_for_attribute(attr).type.start_with?("json") }
229
+ def json_data_type?(klass)
230
+ %i[data metadata].any? { |attr| klass.column_for_attribute(attr).type.start_with?("json") }
221
231
  end
222
232
  end
223
233
  end
@@ -34,7 +34,8 @@ module RubyEventStore
34
34
  end
35
35
 
36
36
  def count(spec)
37
- read_scope(spec).count
37
+ scope_size = read_scope(spec).count # mutant:disable
38
+ scope_size
38
39
  end
39
40
 
40
41
  def streams_of(event_id)
@@ -27,7 +27,7 @@ module RubyEventStore
27
27
 
28
28
  @database_adapter = DatabaseAdapter.from_string(adapter_name, data_type)
29
29
  rescue UnsupportedAdapter => e
30
- raise Error, e.message
30
+ raise Error, e
31
31
  rescue InvalidDataTypeForAdapter
32
32
  raise Error,
33
33
  "Invalid value for --data-type option. Supported for options are: #{DatabaseAdapter.from_string(adapter_name).supported_data_types.join(", ")}."
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "rails/generators"
5
+ rescue LoadError
6
+ end
7
+
8
+ if defined?(Rails::Generators::Base)
9
+ module RubyEventStore
10
+ module ActiveRecord
11
+ class RailsValidAtIndexMigrationGenerator < Rails::Generators::Base
12
+ class Error < Thor::Error
13
+ end
14
+
15
+ namespace "rails_event_store_active_record:migration_for_valid_at_index"
16
+
17
+ source_root File.expand_path(File.join(File.dirname(__FILE__), "../generators/templates"))
18
+
19
+ def create_migration
20
+ template "add_valid_at_index_to_event_store_events_template.erb",
21
+ "db/migrate/#{timestamp}_add_valid_at_index_to_event_store_events.rb"
22
+ end
23
+
24
+ private
25
+
26
+ def migration_version
27
+ ::ActiveRecord::Migration.current_version
28
+ end
29
+
30
+ def timestamp
31
+ Time.now.strftime("%Y%m%d%H%M%S")
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddValidAtIndexToEventStoreEvents < ActiveRecord::Migration[<%= migration_version %>]
4
+ def change
5
+ return if index_exists?(:event_store_events, nil, name: "index_event_store_events_on_as_of")
6
+
7
+ add_index :event_store_events, "COALESCE(valid_at, created_at)", name: "index_event_store_events_on_as_of"
8
+ end
9
+ end
@@ -11,6 +11,8 @@ class CreateEventStoreEvents < ActiveRecord::Migration[<%= migration_version %>]
11
11
  t.datetime :valid_at, null: true, type: :timestamp, precision: 6, index: true
12
12
  end
13
13
 
14
+ add_index :event_store_events, "COALESCE(valid_at, created_at)", name: "index_event_store_events_on_as_of"
15
+
14
16
  create_table(:event_store_events_in_streams, id: :bigserial, force: false) do |t|
15
17
  t.string :stream, null: false
16
18
  t.integer :position, null: true
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ module ActiveRecord
5
+ class ValidAtIndexMigrationGenerator
6
+ def call(migration_path)
7
+ path = build_path(migration_path)
8
+ write_to_file(path)
9
+ path
10
+ end
11
+
12
+ private
13
+
14
+ def absolute_path(path)
15
+ File.expand_path(path, __dir__)
16
+ end
17
+
18
+ def migration_code
19
+ migration_template.result_with_hash(migration_version: migration_version)
20
+ end
21
+
22
+ def migration_template
23
+ ERB.new(
24
+ File.read(
25
+ File.join(absolute_path("./templates"), "add_valid_at_index_to_event_store_events_template.erb"),
26
+ ),
27
+ )
28
+ end
29
+
30
+ def migration_version
31
+ ::ActiveRecord::Migration.current_version
32
+ end
33
+
34
+ def timestamp
35
+ Time.now.strftime("%Y%m%d%H%M%S")
36
+ end
37
+
38
+ def write_to_file(path)
39
+ File.write(path, migration_code)
40
+ end
41
+
42
+ def build_path(migration_path)
43
+ File.join("#{migration_path}", "#{timestamp}_add_valid_at_index_to_event_store_events.rb")
44
+ end
45
+ end
46
+ end
47
+ end
@@ -3,6 +3,12 @@
3
3
  module RubyEventStore
4
4
  module ActiveRecord
5
5
  class Railtie < ::Rails::Railtie
6
+ initializer "ruby_event_store-active_record" do
7
+ ActiveSupport.on_load(:active_record) do
8
+ require_relative "../active_record/skip_json_serialization"
9
+ require_relative "../active_record/event"
10
+ end
11
+ end
6
12
  end
7
13
  end
8
14
  end
@@ -23,6 +23,16 @@ task "db:migrations:fix_missing_event_id_index" do
23
23
  puts "Migration file created #{path}"
24
24
  end
25
25
 
26
+ desc "Generate migration for adding valid_at index on event_store_events"
27
+ task "db:migrations:add_valid_at_index" do
28
+ task = MigrationTask.new("db:migrations:add_valid_at_index")
29
+ task.establish_connection
30
+
31
+ path = RubyEventStore::ActiveRecord::ValidAtIndexMigrationGenerator.new.call(task.migration_path)
32
+
33
+ puts "Migration file created #{path}"
34
+ end
35
+
26
36
  desc "Generate migration for adding foreign key on event_store_events_in_streams.event_id"
27
37
  task "db:migrations:add_foreign_key_on_event_id" do
28
38
  task = MigrationTask.new("db:migrations:add_foreign_key_on_event_id")
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyEventStore
4
4
  module ActiveRecord
5
- VERSION = "2.17.1"
5
+ VERSION = "2.19.0"
6
6
  end
7
7
  end
@@ -7,6 +7,8 @@ require_relative "active_record/generators/event_id_index_migration_generator"
7
7
  require_relative "active_record/generators/rails_event_id_index_migration_generator"
8
8
  require_relative "active_record/generators/foreign_key_on_event_id_migration_generator"
9
9
  require_relative "active_record/generators/rails_foreign_key_on_event_id_migration_generator"
10
+ require_relative "active_record/generators/valid_at_index_migration_generator"
11
+ require_relative "active_record/generators/rails_valid_at_index_migration_generator"
10
12
  require_relative "active_record/with_default_models"
11
13
  require_relative "active_record/with_abstract_base_class"
12
14
  require_relative "active_record/event_repository"
@@ -15,6 +17,9 @@ require_relative "active_record/event_repository_reader"
15
17
  require_relative "active_record/index_violation_detector"
16
18
  require_relative "active_record/pg_linearized_event_repository"
17
19
  require_relative "active_record/version"
18
- require_relative "active_record/skip_json_serialization"
19
- require_relative "active_record/event"
20
- require_relative "active_record/railtie" if defined?(Rails::Engine)
20
+ if defined?(Rails::Engine)
21
+ require_relative "active_record/railtie"
22
+ else
23
+ require_relative "active_record/skip_json_serialization"
24
+ require_relative "active_record/event"
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_event_store-active_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.17.1
4
+ version: 2.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arkency
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 2.17.1
18
+ version: 2.19.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 2.17.1
25
+ version: 2.19.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: activerecord
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -63,14 +63,17 @@ files:
63
63
  - lib/ruby_event_store/active_record/generators/rails_event_id_index_migration_generator.rb
64
64
  - lib/ruby_event_store/active_record/generators/rails_foreign_key_on_event_id_migration_generator.rb
65
65
  - lib/ruby_event_store/active_record/generators/rails_migration_generator.rb
66
+ - lib/ruby_event_store/active_record/generators/rails_valid_at_index_migration_generator.rb
66
67
  - lib/ruby_event_store/active_record/generators/templates/add_event_id_index_to_event_store_events_in_streams_template.erb
67
68
  - lib/ruby_event_store/active_record/generators/templates/add_foreign_key_on_event_id_to_event_store_events_in_streams_template.erb
69
+ - lib/ruby_event_store/active_record/generators/templates/add_valid_at_index_to_event_store_events_template.erb
68
70
  - lib/ruby_event_store/active_record/generators/templates/create_event_store_events_template.erb
69
71
  - lib/ruby_event_store/active_record/generators/templates/mysql/add_foreign_key_on_event_id_to_event_store_events_in_streams_template.erb
70
72
  - lib/ruby_event_store/active_record/generators/templates/mysql/create_event_store_events_template.erb
71
73
  - lib/ruby_event_store/active_record/generators/templates/postgres/add_foreign_key_on_event_id_to_event_store_events_in_streams_template.erb
72
74
  - lib/ruby_event_store/active_record/generators/templates/postgres/create_event_store_events_template.erb
73
75
  - lib/ruby_event_store/active_record/generators/templates/postgres/validate_add_foreign_key_on_event_id_to_event_store_events_in_streams_template.erb
76
+ - lib/ruby_event_store/active_record/generators/valid_at_index_migration_generator.rb
74
77
  - lib/ruby_event_store/active_record/index_violation_detector.rb
75
78
  - lib/ruby_event_store/active_record/pg_linearized_event_repository.rb
76
79
  - lib/ruby_event_store/active_record/railtie.rb
@@ -103,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
106
  - !ruby/object:Gem::Version
104
107
  version: '0'
105
108
  requirements: []
106
- rubygems_version: 3.6.9
109
+ rubygems_version: 4.0.6
107
110
  specification_version: 4
108
111
  summary: Persistent event repository implementation for RubyEventStore based on ActiveRecord
109
112
  test_files: []