Join table: links an Attribute to an AttributeSet with semantic identity.
The ASA is where a generic structural primitive (e.g. "text_input") becomes a named field with context (e.g. "title" in one set, "legend" in another).
Multi-handle support (v0.3.0+)
A single Attribute can appear multiple times within the same AttributeSet,
distinguished by handle. This enables structural primitives (string,
integer, currency, etc.) to be reused across named fields:
# Two ASAs in the same set, both pointing at the "string" Attribute
%ASA{handle: "first_name", attribute_id: string_attr_id, attribute_set_id: set_id}
%ASA{handle: "last_name", attribute_id: string_attr_id, attribute_set_id: set_id}The uniqueness invariant is (attribute_set_id, handle) — each handle
appears at most once per set. Handle is required and must be non-empty.
Config cascade — Layer 2
ASA carries its own data_config and ui_config that override the parent
Attribute's defaults. The full cascade:
- Attribute.data_config / ui_config (structural defaults)
- ASA.data_config / ui_config (semantic overrides) ← this layer
- ASD.ui_config (per-instance overrides)
- Runtime enrichment (PG functions, transforms)
Key fields
handle— the semantic name this attribute takes in this set (required, unique per set)data_config— type-specific config:%{"type" => "string", "localized" => true}ui_config— presentation overrides:%{"tag" => "h2", "classes" => "..."}sort— position within the set
Summary
Functions
Standard changeset — requires handle and both foreign keys.