eventsimple 2.5.0 → 2.7.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/CHANGELOG.md +22 -0
- data/Gemfile.lock +2 -2
- data/lib/eventsimple/entity.rb +11 -4
- data/lib/eventsimple/event.rb +12 -6
- data/lib/eventsimple/types/encrypted_type.rb +18 -5
- data/lib/eventsimple/types.rb +2 -2
- data/lib/eventsimple/version.rb +1 -1
- data/lib/eventsimple.rb +0 -7
- metadata +1 -2
- data/lib/eventsimple/readonly.rb +0 -116
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 44d245e314a08957bc83a97a929c2b8843966f5f435c91719c15d432a5437c9f
|
|
4
|
+
data.tar.gz: 9a6470ff2078eaae9a1d7b290d7e31277bd68f10f773cbed710a8581c3edf85d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b8458ce8ae73209f1724a825da379f49b5954353b63dbe48c79fc10802cc1667300314cfbd6defc32a07e482b39698fd32439c2acac8ee22df0e6702a5c2439e
|
|
7
|
+
data.tar.gz: 633b6e1d896e7f113574e36fcb57507a7977e3acf71d90a08a0bb6161c63257069fb5639ee88386a76d3bb93e056b24971607a511534ec028f2a068be9b2ab62
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 2.7.0 - 2026-05-03
|
|
10
|
+
### Removed
|
|
11
|
+
- Reverted the readonly-bypass enforcement and `Eventsimple.enable_writes! { }` API
|
|
12
|
+
introduced in 2.5.0. Persistence bypass methods (`delete`, `update_column(s)`,
|
|
13
|
+
`update_attribute(!)`, `touch`, `toggle!`, `increment!`, `decrement!`) and
|
|
14
|
+
relation-level bulk SQL (`insert*`, `upsert*`, `update_all`, `delete_all`,
|
|
15
|
+
`touch_all`) no longer raise `ActiveRecord::ReadOnlyRecord` on event-sourced
|
|
16
|
+
models. Model instance `#enable_writes!` is restored.
|
|
17
|
+
|
|
18
|
+
## 2.6.0 - 2026-05-01
|
|
19
|
+
### Added
|
|
20
|
+
- `Eventsimple::Types::*.encrypted` now accepts an optional
|
|
21
|
+
`key_provider:` keyword. When supplied, encryption/decryption goes
|
|
22
|
+
through that `ActiveRecord::Encryption::KeyProvider` instead of the
|
|
23
|
+
default keyed off `config.active_record.encryption.primary_key`.
|
|
24
|
+
Existing call sites (no `key_provider:`) keep using the global
|
|
25
|
+
config. Lets a single Rails app encrypt different event attributes
|
|
26
|
+
against different `KeyProvider`s — e.g. when collapsing a
|
|
27
|
+
microservice that used its own primary key (and possibly its own
|
|
28
|
+
derivation salt) into a host app whose globals are already used for
|
|
29
|
+
its own encrypted events.
|
|
30
|
+
|
|
9
31
|
## 2.5.0 - 2026-04-26
|
|
10
32
|
### Added
|
|
11
33
|
- Entities and events now raise `ActiveRecord::ReadOnlyRecord` when persistence methods that bypass ActiveRecord's built-in readonly check are invoked (`delete`, `update_column(s)`, `update_attribute(!)`, `touch`, `toggle!`, `increment!`, `decrement!`). Relation-level bulk SQL (`insert` / `insert_all` / `upsert` / `upsert_all` / `update_all` / `delete_all` / `touch_all`) is guarded the same way.
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
eventsimple (2.
|
|
4
|
+
eventsimple (2.7.0)
|
|
5
5
|
concurrent-ruby (>= 1.2.3)
|
|
6
6
|
dry-struct (>= 1.8.0)
|
|
7
7
|
dry-types (>= 1.7.0)
|
|
@@ -458,7 +458,7 @@ CHECKSUMS
|
|
|
458
458
|
dry-types (1.9.0) sha256=7b656fe0a78d2432500ae1f29fefd6762f5a032ca7000e4f36bc111453d45d4d
|
|
459
459
|
erb (6.0.1) sha256=28ecdd99c5472aebd5674d6061e3c6b0a45c049578b071e5a52c2a7f13c197e5
|
|
460
460
|
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
|
|
461
|
-
eventsimple (2.
|
|
461
|
+
eventsimple (2.7.0)
|
|
462
462
|
factory_bot (6.5.6) sha256=12beb373214dccc086a7a63763d6718c49769d5606f0501e0a4442676917e077
|
|
463
463
|
factory_bot_rails (6.5.1) sha256=d3cc4851eae4dea8a665ec4a4516895045e710554d2b5ac9e68b94d351bc6d68
|
|
464
464
|
ffi (1.17.3-aarch64-linux-gnu) sha256=28ad573df26560f0aedd8a90c3371279a0b2bd0b4e834b16a2baa10bd7a97068
|
data/lib/eventsimple/entity.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Eventsimple
|
|
|
25
25
|
has_many :events, class_name: event_klass.name.to_s,
|
|
26
26
|
foreign_key: :aggregate_id,
|
|
27
27
|
primary_key: aggregate_id,
|
|
28
|
-
dependent: :
|
|
28
|
+
dependent: :delete_all,
|
|
29
29
|
inverse_of: model_name.element.to_sym,
|
|
30
30
|
autosave: false,
|
|
31
31
|
validate: false
|
|
@@ -45,11 +45,8 @@ module Eventsimple
|
|
|
45
45
|
|
|
46
46
|
Eventsimple.configuration.ui_visible_model_names |= [name]
|
|
47
47
|
|
|
48
|
-
include Readonly
|
|
49
48
|
include InstanceMethods
|
|
50
49
|
extend ClassMethods
|
|
51
|
-
|
|
52
|
-
Readonly.install_relation_guards!(self)
|
|
53
50
|
end
|
|
54
51
|
|
|
55
52
|
module InstanceMethods
|
|
@@ -59,6 +56,16 @@ module Eventsimple
|
|
|
59
56
|
attributes == reprojected.attributes
|
|
60
57
|
end
|
|
61
58
|
|
|
59
|
+
def enable_writes!(&block)
|
|
60
|
+
was_readonly = @readonly
|
|
61
|
+
@readonly = false
|
|
62
|
+
|
|
63
|
+
return unless block
|
|
64
|
+
|
|
65
|
+
yield self
|
|
66
|
+
@readonly = was_readonly
|
|
67
|
+
end
|
|
68
|
+
|
|
62
69
|
def reproject(at: nil, skip_deleted: false)
|
|
63
70
|
event_history = at ? events.where('created_at <= ?', at).load : events.load
|
|
64
71
|
|
data/lib/eventsimple/event.rb
CHANGED
|
@@ -75,11 +75,8 @@ module Eventsimple
|
|
|
75
75
|
after_create :dispatch
|
|
76
76
|
after_create :readonly!
|
|
77
77
|
|
|
78
|
-
include Readonly
|
|
79
78
|
include InstanceMethods
|
|
80
79
|
extend ClassMethods
|
|
81
|
-
|
|
82
|
-
Readonly.install_relation_guards!(self)
|
|
83
80
|
end
|
|
84
81
|
|
|
85
82
|
module InstanceMethods
|
|
@@ -126,9 +123,8 @@ module Eventsimple
|
|
|
126
123
|
apply_timestamps(aggregate)
|
|
127
124
|
apply(aggregate)
|
|
128
125
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
end
|
|
126
|
+
aggregate.enable_writes!
|
|
127
|
+
aggregate.save!
|
|
132
128
|
aggregate.readonly!
|
|
133
129
|
|
|
134
130
|
self.aggregate = aggregate
|
|
@@ -145,6 +141,16 @@ module Eventsimple
|
|
|
145
141
|
def aggregate=(aggregate)
|
|
146
142
|
public_send(:"#{_aggregate_klass.model_name.element}=", aggregate)
|
|
147
143
|
end
|
|
144
|
+
|
|
145
|
+
def enable_writes!(&block)
|
|
146
|
+
was_readonly = @readonly
|
|
147
|
+
@readonly = false
|
|
148
|
+
|
|
149
|
+
return unless block
|
|
150
|
+
|
|
151
|
+
yield self
|
|
152
|
+
@readonly = was_readonly
|
|
153
|
+
end
|
|
148
154
|
end
|
|
149
155
|
|
|
150
156
|
module ClassMethods
|
|
@@ -9,16 +9,17 @@ module Eventsimple
|
|
|
9
9
|
include Dry::Types::Decorator
|
|
10
10
|
include Dry::Types::Builder
|
|
11
11
|
|
|
12
|
-
def initialize(type, **options)
|
|
12
|
+
def initialize(type, key_provider: nil, **options)
|
|
13
13
|
super
|
|
14
14
|
@type = type
|
|
15
|
+
@key_provider = key_provider
|
|
15
16
|
freeze
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def meta(data = nil)
|
|
19
20
|
return { eventsimple: true } if data.nil?
|
|
20
21
|
|
|
21
|
-
self.class.new(@type.meta(data))
|
|
22
|
+
self.class.new(@type.meta(data), key_provider: @key_provider)
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def encrypt(value)
|
|
@@ -26,9 +27,13 @@ module Eventsimple
|
|
|
26
27
|
return value if value == '' || (value.is_a?(::String) && value.blank?)
|
|
27
28
|
|
|
28
29
|
string_value = serialize_value(value)
|
|
29
|
-
return string_value if
|
|
30
|
+
return string_value if encryptor.encrypted?(string_value)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
if @key_provider
|
|
33
|
+
encryptor.encrypt(string_value, key_provider: @key_provider)
|
|
34
|
+
else
|
|
35
|
+
encryptor.encrypt(string_value)
|
|
36
|
+
end
|
|
32
37
|
end
|
|
33
38
|
|
|
34
39
|
def decrypt(value)
|
|
@@ -36,7 +41,11 @@ module Eventsimple
|
|
|
36
41
|
return value if value == '' || (value.is_a?(::String) && value.blank?)
|
|
37
42
|
|
|
38
43
|
decrypted = begin
|
|
39
|
-
|
|
44
|
+
if @key_provider
|
|
45
|
+
encryptor.decrypt(value, key_provider: @key_provider)
|
|
46
|
+
else
|
|
47
|
+
encryptor.decrypt(value)
|
|
48
|
+
end
|
|
40
49
|
rescue StandardError
|
|
41
50
|
return value # Return original if decryption fails
|
|
42
51
|
end
|
|
@@ -46,6 +55,10 @@ module Eventsimple
|
|
|
46
55
|
|
|
47
56
|
private
|
|
48
57
|
|
|
58
|
+
def encryptor
|
|
59
|
+
ActiveRecord::Encryption::Encryptor.new
|
|
60
|
+
end
|
|
61
|
+
|
|
49
62
|
def serialize_value(value)
|
|
50
63
|
case value
|
|
51
64
|
when Array, Hash
|
data/lib/eventsimple/types.rb
CHANGED
|
@@ -26,12 +26,12 @@ module Eventsimple
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def encrypted
|
|
29
|
+
def encrypted(key_provider: nil)
|
|
30
30
|
if respond_to?(:name) && name.to_s.include?('TrueClass') && name.to_s.include?('FalseClass')
|
|
31
31
|
raise ArgumentError, 'Bool type does not support encryption'
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
EncryptedType.new(self)
|
|
34
|
+
EncryptedType.new(self, key_provider: key_provider)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
data/lib/eventsimple/version.rb
CHANGED
data/lib/eventsimple.rb
CHANGED
|
@@ -21,7 +21,6 @@ require 'eventsimple/event_dispatcher'
|
|
|
21
21
|
require 'eventsimple/reactor'
|
|
22
22
|
require 'eventsimple/invalid_transition'
|
|
23
23
|
|
|
24
|
-
require 'eventsimple/readonly'
|
|
25
24
|
require 'eventsimple/entity'
|
|
26
25
|
require 'eventsimple/event'
|
|
27
26
|
|
|
@@ -37,11 +36,5 @@ module Eventsimple
|
|
|
37
36
|
def configure
|
|
38
37
|
yield(configuration)
|
|
39
38
|
end
|
|
40
|
-
|
|
41
|
-
# Unlocks writes for event-sourced models for the duration of the block (instance +save+,
|
|
42
|
-
# relation +update_all+, +upsert+, +delete_all+, etc.).
|
|
43
|
-
def enable_writes!(&block)
|
|
44
|
-
Readonly.enable_writes!(&block)
|
|
45
|
-
end
|
|
46
39
|
end
|
|
47
40
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: eventsimple
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zulfiqar Ali
|
|
@@ -363,7 +363,6 @@ files:
|
|
|
363
363
|
- lib/eventsimple/outbox/models/cursor.rb
|
|
364
364
|
- lib/eventsimple/reactor.rb
|
|
365
365
|
- lib/eventsimple/reactor_worker.rb
|
|
366
|
-
- lib/eventsimple/readonly.rb
|
|
367
366
|
- lib/eventsimple/support/spec_helpers.rb
|
|
368
367
|
- lib/eventsimple/types.rb
|
|
369
368
|
- lib/eventsimple/types/encrypted_type.rb
|
data/lib/eventsimple/readonly.rb
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Eventsimple
|
|
4
|
-
# Raised when +#enable_writes!+ is called on a model instance. Use
|
|
5
|
-
# {Eventsimple.enable_writes!} instead.
|
|
6
|
-
DeprecatedInstanceEnableWrites = Class.new(StandardError)
|
|
7
|
-
|
|
8
|
-
# Closes gaps where writes bypass +readonly!+ (instance helpers like +delete+,
|
|
9
|
-
# +update_column+, relation SQL like +upsert+ / +delete_all+ / +update_all+).
|
|
10
|
-
# Raises +ActiveRecord::ReadOnlyRecord+ unless {Eventsimple.enable_writes!} is active.
|
|
11
|
-
module Readonly
|
|
12
|
-
THREAD_KEY = :eventsimple_class_writes_depth
|
|
13
|
-
|
|
14
|
-
module RelationMethods
|
|
15
|
-
RELATION_WRITE_METHODS = %i[
|
|
16
|
-
insert insert!
|
|
17
|
-
insert_all insert_all!
|
|
18
|
-
upsert upsert_all
|
|
19
|
-
update_all delete_all touch_all
|
|
20
|
-
].freeze
|
|
21
|
-
|
|
22
|
-
RELATION_WRITE_METHODS.each do |method_name|
|
|
23
|
-
define_method(method_name) do |*args, **kwargs, &block|
|
|
24
|
-
_raise_relation_readonly! unless Readonly.class_writes_enabled?
|
|
25
|
-
super(*args, **kwargs, &block)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
def _raise_relation_readonly!
|
|
32
|
-
raise ActiveRecord::ReadOnlyRecord, "#{model.name} is marked as readonly"
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
class << self
|
|
37
|
-
def install_relation_guards!(model_class)
|
|
38
|
-
ActiveRecord::Delegation.delegated_classes.each do |relation_base|
|
|
39
|
-
delegate_class = model_class.relation_delegate_class(relation_base)
|
|
40
|
-
next if delegate_class.ancestors.include?(RelationMethods)
|
|
41
|
-
|
|
42
|
-
delegate_class.prepend(RelationMethods)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def enable_writes!
|
|
47
|
-
Thread.current[THREAD_KEY] = Thread.current[THREAD_KEY].to_i + 1
|
|
48
|
-
yield
|
|
49
|
-
ensure
|
|
50
|
-
d = Thread.current[THREAD_KEY].to_i - 1
|
|
51
|
-
Thread.current[THREAD_KEY] = d.positive? ? d : nil
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def class_writes_enabled?
|
|
55
|
-
Thread.current[THREAD_KEY].to_i.positive?
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def readonly?
|
|
60
|
-
return false if Readonly.class_writes_enabled?
|
|
61
|
-
|
|
62
|
-
super
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def enable_writes!(&_block)
|
|
66
|
-
raise DeprecatedInstanceEnableWrites, <<~MSG.squish
|
|
67
|
-
Use Eventsimple.enable_writes! { ... } instead of #{self.class.name}#enable_writes!
|
|
68
|
-
MSG
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def delete
|
|
72
|
-
_raise_readonly_record_error if readonly?
|
|
73
|
-
super
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def update_column(...)
|
|
77
|
-
_raise_readonly_record_error if readonly?
|
|
78
|
-
super
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def update_columns(...)
|
|
82
|
-
_raise_readonly_record_error if readonly?
|
|
83
|
-
super
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def update_attribute(...)
|
|
87
|
-
_raise_readonly_record_error if readonly?
|
|
88
|
-
super
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def update_attribute!(...)
|
|
92
|
-
_raise_readonly_record_error if readonly?
|
|
93
|
-
super
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def increment!(...)
|
|
97
|
-
_raise_readonly_record_error if readonly?
|
|
98
|
-
super
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def decrement!(...)
|
|
102
|
-
_raise_readonly_record_error if readonly?
|
|
103
|
-
super
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def toggle!(...)
|
|
107
|
-
_raise_readonly_record_error if readonly?
|
|
108
|
-
super
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def touch(...)
|
|
112
|
-
_raise_readonly_record_error if readonly?
|
|
113
|
-
super
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
end
|