typed_eav 0.6.0 → 0.7.1
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 +187 -0
- data/README.md +258 -62
- data/app/models/typed_eav/field/base.rb +77 -27
- 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/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 +4 -0
- data/db/migrate/20260816000000_use_partial_covering_scalar_indexes.rb +198 -0
- data/lib/typed_eav/bulk_read.rb +13 -14
- data/lib/typed_eav/bulk_upsert.rb +137 -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 +34 -9
- 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 +2 -2
- data/lib/typed_eav/has_typed_eav/instance_methods.rb +2 -2
- data/lib/typed_eav/has_typed_eav.rb +1 -1
- data/lib/typed_eav/partition/definition_batch.rb +70 -0
- data/lib/typed_eav/partition.rb +2 -0
- data/lib/typed_eav/query_builder.rb +17 -27
- data/lib/typed_eav/registry.rb +7 -8
- data/lib/typed_eav/version.rb +1 -1
- data/lib/typed_eav/versioned.rb +8 -6
- 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 +5 -1
|
@@ -3,16 +3,10 @@
|
|
|
3
3
|
module TypedEAV
|
|
4
4
|
# Replaces the per-type Finder class hierarchy from active_fields.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# This means:
|
|
11
|
-
# where(integer_value: "42") -> Rails casts "42" to 42 automatically
|
|
12
|
-
# arel[:date_value].gt(value) -> Rails casts string dates to Date objects
|
|
13
|
-
#
|
|
14
|
-
# No manual CAST() calls. No per-type caster classes for queries.
|
|
15
|
-
# One module handles all field types.
|
|
6
|
+
# Field owns operand casting and operator validation before this layer builds
|
|
7
|
+
# Arel. Active Record supplies bind plumbing for the already-normalized
|
|
8
|
+
# typed value; no manual SQL CAST() calls or per-type query caster classes
|
|
9
|
+
# are needed here.
|
|
16
10
|
#
|
|
17
11
|
# Usage:
|
|
18
12
|
# QueryBuilder.filter(field, :gt, 42)
|
|
@@ -53,6 +47,8 @@ module TypedEAV
|
|
|
53
47
|
arel_col = values_table[col]
|
|
54
48
|
|
|
55
49
|
base = value_scope(field)
|
|
50
|
+
excluded = %i[is_null is_not_null contains not_contains starts_with ends_with]
|
|
51
|
+
operand = field.cast_query_operand(operator, value) unless excluded.include?(operator)
|
|
56
52
|
|
|
57
53
|
case operator
|
|
58
54
|
when :eq, :currency_eq
|
|
@@ -64,9 +60,9 @@ module TypedEAV
|
|
|
64
60
|
# dispatch resolved correctly. The operator-validation gate at
|
|
65
61
|
# the top of #filter still narrows :currency_eq to Field::Currency
|
|
66
62
|
# only — no other field type accepts it.
|
|
67
|
-
eq_predicate(base, arel_col, col,
|
|
63
|
+
eq_predicate(base, arel_col, col, operand)
|
|
68
64
|
when :not_eq
|
|
69
|
-
not_eq_predicate(base, arel_col, col,
|
|
65
|
+
not_eq_predicate(base, arel_col, col, operand)
|
|
70
66
|
when :references
|
|
71
67
|
# Phase 5 Reference field. `value` may be an Integer FK OR an
|
|
72
68
|
# AR record instance — `field.cast` normalizes both to an
|
|
@@ -79,27 +75,21 @@ module TypedEAV
|
|
|
79
75
|
# The :references operator is registered ONLY on Field::Reference
|
|
80
76
|
# (the operator-validation gate above keeps it from leaking to
|
|
81
77
|
# other types).
|
|
82
|
-
|
|
83
|
-
if invalid || fk.nil?
|
|
78
|
+
if operand.nil?
|
|
84
79
|
base.none
|
|
85
80
|
else
|
|
86
|
-
base.where(arel_col.eq(
|
|
81
|
+
base.where(arel_col.eq(operand))
|
|
87
82
|
end
|
|
88
83
|
when :gt
|
|
89
|
-
base.where(arel_col.gt(
|
|
84
|
+
base.where(arel_col.gt(operand))
|
|
90
85
|
when :gteq
|
|
91
|
-
base.where(arel_col.gteq(
|
|
86
|
+
base.where(arel_col.gteq(operand))
|
|
92
87
|
when :lt
|
|
93
|
-
base.where(arel_col.lt(
|
|
88
|
+
base.where(arel_col.lt(operand))
|
|
94
89
|
when :lteq
|
|
95
|
-
base.where(arel_col.lteq(
|
|
90
|
+
base.where(arel_col.lteq(operand))
|
|
96
91
|
when :between
|
|
97
|
-
|
|
98
|
-
raise ArgumentError,
|
|
99
|
-
":between expects a Range or two-element Array"
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
base.where(arel_col.between(value.first..value.last))
|
|
92
|
+
base.where(arel_col.between(operand))
|
|
103
93
|
when :contains
|
|
104
94
|
base.where(arel_col.matches("%#{sanitize_like(value)}%"))
|
|
105
95
|
when :not_contains
|
|
@@ -114,10 +104,10 @@ module TypedEAV
|
|
|
114
104
|
base.where.not(col => nil)
|
|
115
105
|
when :any_eq
|
|
116
106
|
# For json_value arrays: contains the given element
|
|
117
|
-
base.where("#{col} @> ?", [
|
|
107
|
+
base.where("#{col} @> ?", [operand].to_json)
|
|
118
108
|
when :all_eq
|
|
119
109
|
# For json_value arrays: contains all given elements
|
|
120
|
-
base.where("#{col} @> ?",
|
|
110
|
+
base.where("#{col} @> ?", operand.to_json)
|
|
121
111
|
else
|
|
122
112
|
raise ArgumentError, "Unhandled operator: #{operator}"
|
|
123
113
|
end
|
data/lib/typed_eav/registry.rb
CHANGED
|
@@ -25,12 +25,11 @@ module TypedEAV
|
|
|
25
25
|
# Register an entity type with optional type restrictions and optional
|
|
26
26
|
# versioning opt-in.
|
|
27
27
|
#
|
|
28
|
-
# `versioned:` is the per-entity
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# `Config.versioning = true`, nothing when off).
|
|
28
|
+
# `versioned:` is the per-entity opt-in flag. When true and the
|
|
29
|
+
# boot-latched transactional callbacks are installed, the version
|
|
30
|
+
# writer records a TypedEAV::ValueVersion row per Value mutation on
|
|
31
|
+
# this entity_type. Default false — apps not using versioning pay zero
|
|
32
|
+
# version-row work.
|
|
34
33
|
#
|
|
35
34
|
# Backward compat: existing callers `register(name, types: types)`
|
|
36
35
|
# continue to work — the new kwarg defaults to false. The entry hash
|
|
@@ -69,8 +68,8 @@ module TypedEAV
|
|
|
69
68
|
# Returns the stored boolean for opted-in entities; false for
|
|
70
69
|
# unregistered entities (defensive — callers might query before
|
|
71
70
|
# `has_typed_eav` runs in a particular load order). The Phase 04
|
|
72
|
-
#
|
|
73
|
-
#
|
|
71
|
+
# transactional writer calls this only after its boot-latched callback
|
|
72
|
+
# has been installed; this lookup remains a small Hash#dig per write.
|
|
74
73
|
#
|
|
75
74
|
# `entities.dig(entity_type, :versioned)` returns nil when
|
|
76
75
|
# `entities[entity_type]` is missing (no register call) OR when the
|
data/lib/typed_eav/version.rb
CHANGED
data/lib/typed_eav/versioned.rb
CHANGED
|
@@ -62,12 +62,14 @@ module TypedEAV
|
|
|
62
62
|
|
|
63
63
|
# Re-register with versioned: true. Preserve the existing types:
|
|
64
64
|
# restriction by reading the current Registry entry.
|
|
65
|
-
# has_typed_eav already
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
# — shouldn't happen post-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
# has_typed_eav already registered the canonical Rails polymorphic
|
|
66
|
+
# name. Reuse it here so including this concern on an STI subclass
|
|
67
|
+
# enables versioning for the base entity_type actually stored on Value.
|
|
68
|
+
# If the entry doesn't exist (defensive — shouldn't happen post-
|
|
69
|
+
# has_typed_eav), default types to nil.
|
|
70
|
+
entity_type = polymorphic_name
|
|
71
|
+
existing = TypedEAV.registry.entities[entity_type] || {}
|
|
72
|
+
TypedEAV.registry.register(entity_type, types: existing[:types], versioned: true)
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
end
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module TypedEAV
|
|
4
4
|
module Versioning
|
|
5
|
-
# The
|
|
6
|
-
#
|
|
7
|
-
# `TypedEAV::Versioning.register_if_enabled
|
|
8
|
-
#
|
|
5
|
+
# The transactional version writer. Conditionally installed on Value's
|
|
6
|
+
# create/update `after` and destroy `before` callbacks at engine boot via
|
|
7
|
+
# `TypedEAV::Versioning.register_if_enabled`; every write is therefore
|
|
8
|
+
# part of the source transaction.
|
|
9
9
|
#
|
|
10
10
|
# ## Contract
|
|
11
11
|
#
|
|
12
|
-
# `call(value, change_type, context)` — called by
|
|
13
|
-
# Returns nil (return value is ignored
|
|
12
|
+
# `call(value, change_type, context)` — called by Value's lifecycle
|
|
13
|
+
# callback. Returns nil (the return value is ignored; the
|
|
14
14
|
# method's job is the side effect of writing a ValueVersion row).
|
|
15
15
|
#
|
|
16
16
|
# Two-gate short-circuit (the master switch is enforced at
|
|
@@ -54,16 +54,13 @@ module TypedEAV
|
|
|
54
54
|
# nil").
|
|
55
55
|
module Subscriber
|
|
56
56
|
class << self
|
|
57
|
-
# Public entry point.
|
|
58
|
-
# 3-arg signature `(value, change_type, context)`.
|
|
57
|
+
# Public entry point. Value's transactional callbacks call this with
|
|
58
|
+
# the locked 3-arg signature `(value, change_type, context)`.
|
|
59
59
|
#
|
|
60
|
-
# NOTE: there is NO `Config.versioning` gate here. The subscriber
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
# lib/typed_eav/engine.rb's `config.after_initialize` block). If
|
|
65
|
-
# versioning is off, the subscriber is never registered and never
|
|
66
|
-
# reached. The remaining gates are:
|
|
60
|
+
# NOTE: there is NO `Config.versioning` gate here. The subscriber is
|
|
61
|
+
# installed only when `Config.versioning` was true at engine
|
|
62
|
+
# `config.after_initialize` time. If versioning is off, the callback
|
|
63
|
+
# is never installed and this method is never reached. The remaining gates are:
|
|
67
64
|
# 1. field-presence (orphan guard — Value's field_id may have
|
|
68
65
|
# been NULLed by Phase 02's ON DELETE SET NULL cascade).
|
|
69
66
|
# 2. per-entity opt-in (Registry.versioned?).
|
|
@@ -72,6 +69,8 @@ module TypedEAV
|
|
|
72
69
|
return unless TypedEAV.registry.versioned?(value.entity_type)
|
|
73
70
|
|
|
74
71
|
write_version_row(value, change_type, context)
|
|
72
|
+
ensure
|
|
73
|
+
value.consume_pending_version_group_id if value.respond_to?(:consume_pending_version_group_id)
|
|
75
74
|
end
|
|
76
75
|
|
|
77
76
|
private
|
|
@@ -85,15 +84,9 @@ module TypedEAV
|
|
|
85
84
|
before_value = field.before_snapshot(value, change_type)
|
|
86
85
|
after_value = field.after_snapshot(value, change_type)
|
|
87
86
|
|
|
88
|
-
# CRITICAL: for :destroy events, write `value_id: nil`.
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
# commits the DELETE before invoking after_commit callbacks).
|
|
92
|
-
# The FK is `ON DELETE SET NULL`, but at the moment we INSERT
|
|
93
|
-
# the version row, Postgres validates the FK against the
|
|
94
|
-
# current state of typed_eav_values — which no longer contains
|
|
95
|
-
# the parent. Writing `value.id` (still readable in-memory on
|
|
96
|
-
# the destroyed AR record) would FK-fail at INSERT.
|
|
87
|
+
# CRITICAL: for :destroy events, write `value_id: nil`. The row is
|
|
88
|
+
# written before the source DELETE and must not retain an identity
|
|
89
|
+
# that the FK will null when the Value is removed.
|
|
97
90
|
#
|
|
98
91
|
# The audit trail for destroy events stays queryable via:
|
|
99
92
|
# - entity_type + entity_id (host record identity)
|
|
@@ -102,8 +95,7 @@ module TypedEAV
|
|
|
102
95
|
# `field_id` remains populated because destroying a Value does
|
|
103
96
|
# not destroy its Field — `value.field_id` is a live reference.
|
|
104
97
|
#
|
|
105
|
-
# For :create and :update
|
|
106
|
-
# correct (parent row exists at after_commit time).
|
|
98
|
+
# For :create and :update, `value_id: value.id` is correct.
|
|
107
99
|
version_value_id = change_type == :destroy ? nil : value.id
|
|
108
100
|
|
|
109
101
|
# Phase 06 bulk-operations correlation tag. Two delivery paths:
|
|
@@ -113,7 +105,7 @@ module TypedEAV
|
|
|
113
105
|
# on each affected Value object BEFORE `record.save`, INSIDE
|
|
114
106
|
# the per-record `with_context` block. Reading the snapshot
|
|
115
107
|
# here (not `current_context`) guarantees the UUID survives
|
|
116
|
-
# the
|
|
108
|
+
# the source transaction boundary — by the time
|
|
117
109
|
# we run, the lexical `with_context` block has unwound and
|
|
118
110
|
# `current_context` would be empty, but the per-Value ivar
|
|
119
111
|
# persists. Mirrors the existing in-memory ivar pattern at
|
|
@@ -127,12 +119,16 @@ module TypedEAV
|
|
|
127
119
|
# request id). For those paths the Value ivar is unset; the
|
|
128
120
|
# `||` falls through to `context[:version_group_id]`. Also
|
|
129
121
|
# used as the belt-and-suspenders fallback for any future
|
|
130
|
-
#
|
|
122
|
+
# savepoint path that bulk writes
|
|
131
123
|
# might leverage.
|
|
132
124
|
#
|
|
133
125
|
# When neither path supplies a UUID the column (added in
|
|
134
126
|
# `db/migrate/20260506000001`) stays NULL — backward-compatible:
|
|
135
127
|
# unchanged subscribers and unchanged callers continue to work.
|
|
128
|
+
pending_group_id = if value.respond_to?(:consume_pending_version_group_id)
|
|
129
|
+
value.consume_pending_version_group_id
|
|
130
|
+
end
|
|
131
|
+
|
|
136
132
|
TypedEAV::ValueVersion.create!(
|
|
137
133
|
value_id: version_value_id,
|
|
138
134
|
field_id: value.field_id,
|
|
@@ -142,7 +138,7 @@ module TypedEAV
|
|
|
142
138
|
before_value: before_value,
|
|
143
139
|
after_value: after_value,
|
|
144
140
|
context: context.to_h, # frozen → unfrozen jsonb-serializable hash
|
|
145
|
-
version_group_id:
|
|
141
|
+
version_group_id: pending_group_id || context[:version_group_id],
|
|
146
142
|
change_type: change_type.to_s,
|
|
147
143
|
changed_at: Time.current,
|
|
148
144
|
)
|
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.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- dchuk
|
|
@@ -94,6 +94,7 @@ files:
|
|
|
94
94
|
- db/migrate/20260506000001_add_version_group_id_to_typed_eav_value_versions.rb
|
|
95
95
|
- db/migrate/20260507000000_add_label_to_typed_eav_fields.rb
|
|
96
96
|
- db/migrate/20260712000000_enforce_parent_scope_invariant.rb
|
|
97
|
+
- db/migrate/20260816000000_use_partial_covering_scalar_indexes.rb
|
|
97
98
|
- lib/generators/typed_eav/install/install_generator.rb
|
|
98
99
|
- lib/generators/typed_eav/scaffold/scaffold_generator.rb
|
|
99
100
|
- lib/generators/typed_eav/scaffold/templates/config/initializers/typed_eav.rb
|
|
@@ -145,6 +146,7 @@ files:
|
|
|
145
146
|
- lib/generators/typed_eav/scaffold/templates/views/typed_eav/values/inputs/_url.html.erb
|
|
146
147
|
- lib/typed_eav.rb
|
|
147
148
|
- lib/typed_eav/bulk_read.rb
|
|
149
|
+
- lib/typed_eav/bulk_upsert.rb
|
|
148
150
|
- lib/typed_eav/bulk_write.rb
|
|
149
151
|
- lib/typed_eav/config.rb
|
|
150
152
|
- lib/typed_eav/csv_mapper.rb
|
|
@@ -152,10 +154,12 @@ files:
|
|
|
152
154
|
- lib/typed_eav/entity_query.rb
|
|
153
155
|
- lib/typed_eav/event_dispatcher.rb
|
|
154
156
|
- lib/typed_eav/field/typed_storage.rb
|
|
157
|
+
- lib/typed_eav/field_deletion.rb
|
|
155
158
|
- lib/typed_eav/filter_query.rb
|
|
156
159
|
- lib/typed_eav/has_typed_eav.rb
|
|
157
160
|
- lib/typed_eav/has_typed_eav/instance_methods.rb
|
|
158
161
|
- lib/typed_eav/partition.rb
|
|
162
|
+
- lib/typed_eav/partition/definition_batch.rb
|
|
159
163
|
- lib/typed_eav/query_builder.rb
|
|
160
164
|
- lib/typed_eav/registry.rb
|
|
161
165
|
- lib/typed_eav/schema_portability.rb
|