typed_eav 0.5.0 → 0.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 +207 -0
- data/README.md +271 -61
- data/app/models/typed_eav/field/base.rb +90 -33
- data/app/models/typed_eav/field/currency.rb +43 -0
- data/app/models/typed_eav/field/file.rb +1 -1
- data/app/models/typed_eav/field/image.rb +1 -1
- data/app/models/typed_eav/field/reference.rb +11 -0
- data/app/models/typed_eav/option.rb +0 -8
- data/app/models/typed_eav/section.rb +11 -5
- data/app/models/typed_eav/value.rb +95 -32
- data/db/migrate/20260430000000_add_parent_scope_to_typed_eav_partitions.rb +1 -1
- data/db/migrate/20260712000000_enforce_parent_scope_invariant.rb +54 -0
- data/db/migrate/20260816000000_use_partial_covering_scalar_indexes.rb +198 -0
- data/lib/generators/typed_eav/scaffold/templates/controllers/typed_eav_controller.rb +1 -9
- data/lib/typed_eav/bulk_read.rb +41 -9
- data/lib/typed_eav/bulk_upsert.rb +141 -0
- data/lib/typed_eav/bulk_write.rb +65 -39
- data/lib/typed_eav/config.rb +19 -21
- data/lib/typed_eav/csv_mapper.rb +1 -1
- data/lib/typed_eav/engine.rb +16 -27
- data/lib/typed_eav/entity_query.rb +42 -8
- data/lib/typed_eav/event_dispatcher.rb +21 -30
- data/lib/typed_eav/field/typed_storage.rb +48 -0
- data/lib/typed_eav/field_deletion.rb +75 -0
- data/lib/typed_eav/filter_query.rb +9 -7
- data/lib/typed_eav/has_typed_eav/instance_methods.rb +11 -8
- data/lib/typed_eav/partition.rb +8 -3
- data/lib/typed_eav/query_builder.rb +17 -27
- data/lib/typed_eav/registry.rb +7 -8
- data/lib/typed_eav/schema_portability/import_index.rb +58 -0
- data/lib/typed_eav/schema_portability.rb +22 -25
- data/lib/typed_eav/version.rb +1 -1
- data/lib/typed_eav/versioning/subscriber.rb +25 -29
- data/lib/typed_eav/versioning.rb +59 -41
- data/lib/typed_eav.rb +2 -0
- metadata +19 -5
data/lib/typed_eav/versioning.rb
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module TypedEAV
|
|
4
|
-
#
|
|
5
|
-
# TypedEAV::ValueVersion rows
|
|
6
|
-
# dispatched by EventDispatcher.
|
|
4
|
+
# Transactional versioning namespace. Houses the Subscriber that writes
|
|
5
|
+
# TypedEAV::ValueVersion rows from Value lifecycle callbacks.
|
|
7
6
|
#
|
|
8
7
|
# ## Architecture
|
|
9
8
|
#
|
|
10
9
|
# - TypedEAV::Versioning::Subscriber.call(value, change_type, context)
|
|
11
|
-
# is conditionally
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# the `config.after_initialize` block in lib/typed_eav/engine.rb.
|
|
15
|
-
# When TypedEAV.config.versioning is false (default), the helper
|
|
16
|
-
# returns early — no callable is added to the dispatcher chain.
|
|
17
|
-
# When true, the subscriber registers and runs FIRST in the value-
|
|
18
|
-
# change subscriber chain (slot 0 by `after_initialize` block
|
|
19
|
-
# declaration order — Phase 07 will declare its matview block LATER
|
|
20
|
-
# in the same engine to keep matview at slot ≥ 1).
|
|
10
|
+
# is conditionally installed on Value's transactional callbacks at engine
|
|
11
|
+
# boot via `TypedEAV::Versioning.register_if_enabled`. The version row
|
|
12
|
+
# therefore shares the source transaction.
|
|
21
13
|
#
|
|
22
14
|
# - The subscriber is gated by TWO checks at call time (both must
|
|
23
15
|
# pass for a version row to be written):
|
|
@@ -30,10 +22,9 @@ module TypedEAV
|
|
|
30
22
|
# the callable — when false, the subscriber is never registered in
|
|
31
23
|
# the first place.
|
|
32
24
|
#
|
|
33
|
-
# - Errors raised by Subscriber.call
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# leaves the audit log inconsistent with the live row.
|
|
25
|
+
# - Errors raised by Subscriber.call propagate. Versioning corruption must
|
|
26
|
+
# be loud — silent failure leaves the audit log inconsistent with the live
|
|
27
|
+
# row.
|
|
37
28
|
#
|
|
38
29
|
# ## Public API surface
|
|
39
30
|
#
|
|
@@ -52,43 +43,70 @@ module TypedEAV
|
|
|
52
43
|
# this namespace shell — it does NOT recursively autoload nested
|
|
53
44
|
# constants. Without the explicit declaration below, the engine's
|
|
54
45
|
# config.after_initialize block (which references
|
|
55
|
-
# `TypedEAV::Versioning::Subscriber
|
|
46
|
+
# `TypedEAV::Versioning::Subscriber`) raises
|
|
56
47
|
# `NameError: uninitialized constant TypedEAV::Versioning::Subscriber`
|
|
57
48
|
# at boot time, breaking every host that enables versioning.
|
|
58
49
|
autoload :Subscriber, "typed_eav/versioning/subscriber"
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
CALLBACKS = {
|
|
52
|
+
create: %i[_write_version_create after].freeze,
|
|
53
|
+
update: %i[_write_version_update after].freeze,
|
|
54
|
+
destroy: %i[_write_version_destroy before].freeze,
|
|
55
|
+
}.freeze
|
|
56
|
+
|
|
57
|
+
# Conditionally install the Subscriber on Value's transactional callbacks.
|
|
58
|
+
# Called by the engine's `config.after_initialize` block.
|
|
63
59
|
#
|
|
64
60
|
# Extracted into a class method (not inlined inside the after_initialize
|
|
65
|
-
# block) for testability: specs can call this
|
|
66
|
-
#
|
|
67
|
-
# (versioning on/off) in-process, without booting the engine. The
|
|
68
|
-
# slot-0 regression spec (plan 04-03 P03) and the zero-overhead
|
|
69
|
-
# verification spec (this plan, subscriber_spec engine-boot block) both
|
|
70
|
-
# rely on this seam.
|
|
61
|
+
# block) for testability: specs can call this seam in-process without
|
|
62
|
+
# booting a second Rails application.
|
|
71
63
|
#
|
|
72
|
-
# Idempotent — safe to call multiple times.
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
# `Array#include?` against `Subscriber.method(:call)`; `Method#==`
|
|
76
|
-
# compares receiver+name (semantic equality), so two fresh
|
|
77
|
-
# `Subscriber.method(:call)` instances compare equal even though they
|
|
78
|
-
# are different Method objects. The engine block runs this exactly
|
|
79
|
-
# once per boot in production; the idempotency guard protects future
|
|
80
|
-
# code paths that might re-invoke for any reason.
|
|
64
|
+
# Idempotent — safe to call multiple times. Pool validation happens before
|
|
65
|
+
# any callback is installed, so a multi-database misconfiguration fails
|
|
66
|
+
# closed without partial activation.
|
|
81
67
|
#
|
|
82
68
|
# When `TypedEAV.config.versioning` is false (default), this method is
|
|
83
|
-
# a no-op:
|
|
84
|
-
#
|
|
69
|
+
# a no-op: no callback is installed and the disabled path adds no
|
|
70
|
+
# per-write predicate or dispatcher work.
|
|
85
71
|
def self.register_if_enabled
|
|
86
72
|
return unless TypedEAV.config.versioning
|
|
87
73
|
|
|
88
|
-
|
|
89
|
-
|
|
74
|
+
value_pool = TypedEAV::Value.connection_pool
|
|
75
|
+
version_pool = TypedEAV::ValueVersion.connection_pool
|
|
76
|
+
unless value_pool.equal?(version_pool)
|
|
77
|
+
raise ArgumentError, "TypedEAV versioning requires Value and ValueVersion to share a connection pool"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
callback_chains = callback_chains_for_installation
|
|
81
|
+
|
|
82
|
+
callback_chains.each do |event, (filter, kind, chain)|
|
|
83
|
+
next if chain.any? { |callback| callback.filter == filter && callback.kind == kind }
|
|
84
|
+
|
|
85
|
+
TypedEAV::Value.set_callback(event, kind, filter, prepend: true)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.callback_chains_for_installation
|
|
90
|
+
CALLBACKS.to_h do |event, (filter, kind)|
|
|
91
|
+
chain = TypedEAV::Value.send(:get_callbacks, event).to_a
|
|
92
|
+
wrong_kind = chain.find { |callback| callback.filter == filter && callback.kind != kind }
|
|
93
|
+
if wrong_kind
|
|
94
|
+
raise ArgumentError,
|
|
95
|
+
"TypedEAV versioning callback #{filter.inspect} on #{event} has kind " \
|
|
96
|
+
"#{wrong_kind.kind.inspect}; expected #{kind.inspect}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
[event, [filter, kind, chain]]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
private_class_method :callback_chains_for_installation
|
|
90
103
|
|
|
91
|
-
|
|
104
|
+
def self.atomic_callbacks_installed?
|
|
105
|
+
CALLBACKS.all? do |event, (filter, kind)|
|
|
106
|
+
TypedEAV::Value.send(:get_callbacks, event).to_a.any? do |callback|
|
|
107
|
+
callback.filter == filter && callback.kind == kind
|
|
108
|
+
end
|
|
109
|
+
end
|
|
92
110
|
end
|
|
93
111
|
end
|
|
94
112
|
end
|
data/lib/typed_eav.rb
CHANGED
|
@@ -18,6 +18,7 @@ module TypedEAV
|
|
|
18
18
|
autoload :FilterQuery
|
|
19
19
|
autoload :BulkRead
|
|
20
20
|
autoload :BulkWrite
|
|
21
|
+
autoload :BulkUpsert
|
|
21
22
|
autoload :Partition
|
|
22
23
|
autoload :QueryBuilder
|
|
23
24
|
autoload :SchemaPortability
|
|
@@ -27,6 +28,7 @@ module TypedEAV
|
|
|
27
28
|
autoload :ValueVersion
|
|
28
29
|
autoload :Versioned
|
|
29
30
|
autoload :Versioning
|
|
31
|
+
autoload :FieldDeletion
|
|
30
32
|
|
|
31
33
|
# Raised when a model declared `has_typed_eav scope_method: ...` but no
|
|
32
34
|
# scope can be resolved at query time and `config.require_scope` is truthy.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: typed_eav
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- dchuk
|
|
@@ -15,14 +15,20 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '7.
|
|
18
|
+
version: '7.2'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '8.2'
|
|
19
22
|
type: :runtime
|
|
20
23
|
prerelease: false
|
|
21
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
25
|
requirements:
|
|
23
26
|
- - ">="
|
|
24
27
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '7.
|
|
28
|
+
version: '7.2'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '8.2'
|
|
26
32
|
- !ruby/object:Gem::Dependency
|
|
27
33
|
name: csv
|
|
28
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -87,6 +93,8 @@ files:
|
|
|
87
93
|
- db/migrate/20260505000000_create_typed_eav_value_versions.rb
|
|
88
94
|
- db/migrate/20260506000001_add_version_group_id_to_typed_eav_value_versions.rb
|
|
89
95
|
- db/migrate/20260507000000_add_label_to_typed_eav_fields.rb
|
|
96
|
+
- db/migrate/20260712000000_enforce_parent_scope_invariant.rb
|
|
97
|
+
- db/migrate/20260816000000_use_partial_covering_scalar_indexes.rb
|
|
90
98
|
- lib/generators/typed_eav/install/install_generator.rb
|
|
91
99
|
- lib/generators/typed_eav/scaffold/scaffold_generator.rb
|
|
92
100
|
- lib/generators/typed_eav/scaffold/templates/config/initializers/typed_eav.rb
|
|
@@ -138,6 +146,7 @@ files:
|
|
|
138
146
|
- lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_url.html.erb
|
|
139
147
|
- lib/typed_eav.rb
|
|
140
148
|
- lib/typed_eav/bulk_read.rb
|
|
149
|
+
- lib/typed_eav/bulk_upsert.rb
|
|
141
150
|
- lib/typed_eav/bulk_write.rb
|
|
142
151
|
- lib/typed_eav/config.rb
|
|
143
152
|
- lib/typed_eav/csv_mapper.rb
|
|
@@ -145,6 +154,7 @@ files:
|
|
|
145
154
|
- lib/typed_eav/entity_query.rb
|
|
146
155
|
- lib/typed_eav/event_dispatcher.rb
|
|
147
156
|
- lib/typed_eav/field/typed_storage.rb
|
|
157
|
+
- lib/typed_eav/field_deletion.rb
|
|
148
158
|
- lib/typed_eav/filter_query.rb
|
|
149
159
|
- lib/typed_eav/has_typed_eav.rb
|
|
150
160
|
- lib/typed_eav/has_typed_eav/instance_methods.rb
|
|
@@ -152,6 +162,7 @@ files:
|
|
|
152
162
|
- lib/typed_eav/query_builder.rb
|
|
153
163
|
- lib/typed_eav/registry.rb
|
|
154
164
|
- lib/typed_eav/schema_portability.rb
|
|
165
|
+
- lib/typed_eav/schema_portability/import_index.rb
|
|
155
166
|
- lib/typed_eav/scope_tuple.rb
|
|
156
167
|
- lib/typed_eav/version.rb
|
|
157
168
|
- lib/typed_eav/versioned.rb
|
|
@@ -174,14 +185,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
174
185
|
requirements:
|
|
175
186
|
- - ">="
|
|
176
187
|
- !ruby/object:Gem::Version
|
|
177
|
-
version: '3.
|
|
188
|
+
version: '3.3'
|
|
189
|
+
- - "<"
|
|
190
|
+
- !ruby/object:Gem::Version
|
|
191
|
+
version: '4.1'
|
|
178
192
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
193
|
requirements:
|
|
180
194
|
- - ">="
|
|
181
195
|
- !ruby/object:Gem::Version
|
|
182
196
|
version: '0'
|
|
183
197
|
requirements: []
|
|
184
|
-
rubygems_version: 3.
|
|
198
|
+
rubygems_version: 3.6.9
|
|
185
199
|
specification_version: 4
|
|
186
200
|
summary: Typed custom fields for ActiveRecord models
|
|
187
201
|
test_files: []
|