typed_eav 0.4.0 → 0.5.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 +40 -0
- data/README.md +1 -0
- data/app/models/typed_eav/field/base.rb +15 -0
- data/db/migrate/20260507000000_add_label_to_typed_eav_fields.rb +13 -0
- data/lib/typed_eav/schema_portability.rb +13 -0
- data/lib/typed_eav/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2584a7a3e9eab294e874931c8202f2dae2082be6a036bd419fce7df3fdd94f56
|
|
4
|
+
data.tar.gz: 7d24090e4969e89bb268f89510b46ce034d8f9f045054c0d6d80828c4cd3006c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4aeaa932e2dff4d5ab22ae95c66d98953b93163fc9580445f05a365ce1489a56cfdbaf998e47548d5fe468227341eec170409eafad5191449cb96201039900d0
|
|
7
|
+
data.tar.gz: 1b626f58647506b2d0317e382d0fd2d8ebf7daae1c11e84dc9d58b1929107af9c62854c19c39a6244e9957d1e20ccb7b3c924c7aee9a77aa19414453c356e534
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,46 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - 2026-06-01
|
|
9
|
+
|
|
10
|
+
Adds a human display label for fields, distinct from the immutable machine
|
|
11
|
+
slug `name` (issue #21). Fully additive — every change defaults to the
|
|
12
|
+
pre-0.5.0 behavior. Existing rows (with `label` NULL) render exactly as
|
|
13
|
+
before, and consumers that never set a label observe no difference.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `label` — a nullable, free-text column on `typed_eav_fields`
|
|
18
|
+
(`20260507000000_add_label_to_typed_eav_fields.rb`). No index, no
|
|
19
|
+
default, no backfill: `label` never participates in uniqueness, lookup,
|
|
20
|
+
partitioning, or ordering, so an index would be dead weight. Run
|
|
21
|
+
`rails typed_eav:install:migrations` (or copy the migration) and migrate
|
|
22
|
+
to pick it up. `name` stays the immutable machine key.
|
|
23
|
+
|
|
24
|
+
- `TypedEAV::Field::Base#display_name` — the canonical human-facing
|
|
25
|
+
display string. Returns the free-text `label` when present, otherwise
|
|
26
|
+
falls back to `name.humanize`. A blank (`""`) label falls back too (via
|
|
27
|
+
`presence`). This is the ONE accessor all rendering should use; existing
|
|
28
|
+
rows render unchanged.
|
|
29
|
+
|
|
30
|
+
- `SchemaPortability` round-trips the label. `export_schema` emits the
|
|
31
|
+
**raw** `"label"` so `import_schema` reproduces it verbatim and
|
|
32
|
+
divergence detection treats a differing label as a difference; legacy
|
|
33
|
+
payloads with no `"label"` key import as NULL with no version gate.
|
|
34
|
+
`export_snapshot_schema` emits the **resolved** `"display_name"` instead
|
|
35
|
+
(render-oriented snapshots hand the consumer a ready-to-render string —
|
|
36
|
+
intentionally asymmetric to the raw-label regular export).
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- A label-only edit on a Field dispatches the `:update` event, not
|
|
41
|
+
`:rename`. `:rename` remains reserved for changes to the machine `name`.
|
|
42
|
+
Regression-pinned in `spec/regressions/issue_21_label_no_rename_spec.rb`.
|
|
43
|
+
|
|
44
|
+
### References
|
|
45
|
+
|
|
46
|
+
- Issue #21 — Field display label / `display_name` contract.
|
|
47
|
+
|
|
8
48
|
## [0.4.0] - 2026-05-26
|
|
9
49
|
|
|
10
50
|
Closes four follow-up gaps (PRD #15) surfaced when a downstream Rails app
|
data/README.md
CHANGED
|
@@ -810,6 +810,7 @@ A few non-obvious contracts worth knowing about up front:
|
|
|
810
810
|
- **Orphan-parent rows rejected**: a `Field` or `Section` row with `parent_scope` set but `scope` blank is invalid. The `Value`-side guard rejects cross-`(scope, parent_scope)` writes too.
|
|
811
811
|
- **Event hooks fire from `after_commit`**: the `on_value_change` and `on_field_change` callbacks fire after the database write is durable; their exceptions never break a save. See §"Event hooks" for the full contract.
|
|
812
812
|
- **Versioning is opt-in**: When enabled (`TypedEAV.config.versioning = true` on the gem; `versioned: true` per host), every `:create` / `:update` / `:destroy` event on a Value writes an append-only audit row in `typed_eav_value_versions`. See §"Versioning" for the full contract.
|
|
813
|
+
- **`label` is cosmetic, `name` is the machine key**: A field's optional `label` is free-text human display, independent of the slug `name`. Render via `display_name`, which returns `label` when present else `name.humanize`. `label` has no uniqueness or format constraints (only a 255-char max) and never affects ordering, lookup, partitioning, or rename detection — editing only `label` fires `on_field_change` with `:update`, never `:rename`. Existing rows (`label` NULL) render unchanged. Schema export round-trips the raw `label` (legacy payloads without a `label` key import as NULL); snapshot export carries the resolved `display_name`.
|
|
813
814
|
|
|
814
815
|
## Event hooks
|
|
815
816
|
|
|
@@ -45,6 +45,11 @@ module TypedEAV
|
|
|
45
45
|
|
|
46
46
|
validates :name, presence: true, uniqueness: { scope: %i[entity_type scope parent_scope] }
|
|
47
47
|
validates :name, exclusion: { in: RESERVED_NAMES, message: "is reserved" }
|
|
48
|
+
# `label` is optional free-text human display, independent of the machine
|
|
49
|
+
# slug `name` (issue #21). The ONLY guard is a max-length sanity bound —
|
|
50
|
+
# intentionally NO uniqueness and NO format/exclusion constraint. RESERVED_NAMES,
|
|
51
|
+
# slug-uniqueness, and rename detection all key on :name and never on :label.
|
|
52
|
+
validates :label, length: { maximum: 255 }, allow_nil: true
|
|
48
53
|
validates :type, presence: true
|
|
49
54
|
validates :entity_type, presence: true
|
|
50
55
|
validate :validate_default_value
|
|
@@ -262,6 +267,16 @@ module TypedEAV
|
|
|
262
267
|
self.class.name.demodulize.underscore
|
|
263
268
|
end
|
|
264
269
|
|
|
270
|
+
# Canonical human-facing display string (issue #21). Returns the
|
|
271
|
+
# free-text `label` when present, otherwise falls back to humanizing the
|
|
272
|
+
# machine slug `name`. This is the ONE accessor all rendering should use;
|
|
273
|
+
# `name` stays the immutable machine key. A blank ("") label falls back
|
|
274
|
+
# too (via `presence`), so existing rows (label NULL) render exactly as
|
|
275
|
+
# they did before this column existed.
|
|
276
|
+
def display_name
|
|
277
|
+
label.presence || name.humanize
|
|
278
|
+
end
|
|
279
|
+
|
|
265
280
|
def array_field?
|
|
266
281
|
false
|
|
267
282
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddLabelToTypedEAVFields < ActiveRecord::Migration[7.1]
|
|
4
|
+
# Additive, nullable, free-text display label distinct from the machine
|
|
5
|
+
# slug `name` (issue #21). No index, no default, no backfill: `label` never
|
|
6
|
+
# participates in uniqueness, lookup, partitioning, or ordering, so an index
|
|
7
|
+
# would be dead weight. Reversible via `change` because `add_column`
|
|
8
|
+
# auto-inverts. Existing rows keep label NULL and render unchanged through
|
|
9
|
+
# the new `Field#display_name` (label.presence || name.humanize).
|
|
10
|
+
def change
|
|
11
|
+
add_column :typed_eav_fields, :label, :string, null: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -113,6 +113,12 @@ module TypedEAV
|
|
|
113
113
|
"entity_type" => field.entity_type,
|
|
114
114
|
"scope" => field.scope,
|
|
115
115
|
"parent_scope" => field.parent_scope,
|
|
116
|
+
# Raw label (issue #21) — NOT the resolved display_name. The regular
|
|
117
|
+
# export round-trips the stored value verbatim so import reproduces
|
|
118
|
+
# it exactly and divergence detection (field_export_row_equal?) treats
|
|
119
|
+
# a differing label as a difference. Legacy payloads lack this key →
|
|
120
|
+
# entry["label"] is nil on import → label stays NULL (no version gate).
|
|
121
|
+
"label" => field.label,
|
|
116
122
|
"required" => field.required,
|
|
117
123
|
"sort_order" => field.sort_order,
|
|
118
124
|
"field_dependent" => field.field_dependent,
|
|
@@ -145,6 +151,12 @@ module TypedEAV
|
|
|
145
151
|
entry = {
|
|
146
152
|
"name" => field.name,
|
|
147
153
|
"field_type_name" => field.field_type_name,
|
|
154
|
+
# RESOLVED display_name (issue #21), NOT the raw label — snapshots are
|
|
155
|
+
# render-oriented (CONTEXT decision 3). This is intentionally
|
|
156
|
+
# asymmetric to the regular export's raw "label": a snapshot consumer
|
|
157
|
+
# gets the ready-to-render string (label when present, else
|
|
158
|
+
# name.humanize) without re-deriving it.
|
|
159
|
+
"display_name" => field.display_name,
|
|
148
160
|
"required" => field.required,
|
|
149
161
|
"sort_order" => field.sort_order,
|
|
150
162
|
"options" => field.options,
|
|
@@ -247,6 +259,7 @@ module TypedEAV
|
|
|
247
259
|
|
|
248
260
|
def overwrite_field!(existing, entry)
|
|
249
261
|
existing.assign_attributes(
|
|
262
|
+
label: entry["label"],
|
|
250
263
|
required: entry["required"],
|
|
251
264
|
sort_order: entry["sort_order"],
|
|
252
265
|
field_dependent: entry["field_dependent"],
|
data/lib/typed_eav/version.rb
CHANGED
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.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- dchuk
|
|
@@ -86,6 +86,7 @@ files:
|
|
|
86
86
|
- db/migrate/20260501000000_add_cascade_policy_to_typed_eav_fields.rb
|
|
87
87
|
- db/migrate/20260505000000_create_typed_eav_value_versions.rb
|
|
88
88
|
- db/migrate/20260506000001_add_version_group_id_to_typed_eav_value_versions.rb
|
|
89
|
+
- db/migrate/20260507000000_add_label_to_typed_eav_fields.rb
|
|
89
90
|
- lib/generators/typed_eav/install/install_generator.rb
|
|
90
91
|
- lib/generators/typed_eav/scaffold/scaffold_generator.rb
|
|
91
92
|
- lib/generators/typed_eav/scaffold/templates/config/initializers/typed_eav.rb
|