Module: TypedEAV::SchemaPortability
- Defined in:
- lib/typed_eav/schema_portability.rb,
lib/typed_eav/schema_portability/preview.rb,
lib/typed_eav/schema_portability/import_index.rb
Overview
Export and import field + section definitions for an exact partition tuple. Value rows are intentionally out of scope.
Defined Under Namespace
Classes: ImportIndex, Preview
Class Method Summary collapse
- .export_schema(entity_type:, scope: nil, parent_scope: nil) ⇒ Object
-
.export_snapshot_schema(entity_type:, scope: nil, parent_scope: nil) ⇒ Hash
Lean, restore-oriented projection of the field schema for a partition tuple.
- .import_schema(hash, on_conflict: :error) ⇒ Object
-
.preview_schema(hash, on_conflict: :error) ⇒ Object
Compare an exported schema with the exact target partition without invoking the import pipeline.
Class Method Details
.export_schema(entity_type:, scope: nil, parent_scope: nil) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/typed_eav/schema_portability.rb', line 11 def export_schema(entity_type:, scope: nil, parent_scope: nil) fields = TypedEAV::Field::Base .where(entity_type: entity_type, scope: scope, parent_scope: parent_scope) .includes(:field_options) .order(:sort_order) .map { |field| export_field_entry(field) } sections = TypedEAV::Section .where(entity_type: entity_type, scope: scope, parent_scope: parent_scope) .order(:sort_order) .map { |section| export_section_entry(section) } { "schema_version" => 1, "entity_type" => entity_type, "scope" => scope, "parent_scope" => parent_scope, "fields" => fields, "sections" => sections, } end |
.export_snapshot_schema(entity_type:, scope: nil, parent_scope: nil) ⇒ Hash
Lean, restore-oriented projection of the field schema for a partition tuple. Sibling to export_schema — same partition filter, narrower per-field surface, no sections, no partition-identity keys.
The envelope is:
{
"snapshot_schema_version" => 1,
"fields" => [ <snapshot_field_entry>, ... ] # ordered by sort_order
}
The snapshot_schema_version integer will be bumped explicitly when
the inner per-field shape evolves in a non-additive way — it is NOT
frozen forever. Consumers should branch on the version to handle
cross-version snapshots.
Each per-field entry is a strict subset of the full export_field_entry shape:
{
"name" => field.name,
"field_type_name" => field.field_type_name,
"required" => field.required,
"sort_order" => field.sort_order,
"options" => field.,
"options_data" => [...] # ONLY present when field.optionable?
}
Omitted vs the full schema export: entity_type, scope,
parent_scope, type (the AR STI class name), field_dependent,
and default_value_meta. Non-optionable fields omit options_data
entirely (absent, not nil, not an empty array).
The field_type_name value is the documented field-type dispatch
identifier — robust to namespace relocations of the field class
because it strips the namespace via demodulize before
underscore-ing. It is NOT robust to renames of the leaf class
itself: Field::Select → "select", but renaming the class to
Field::Status would change the dispatch identifier to "status".
77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/typed_eav/schema_portability.rb', line 77 def export_snapshot_schema(entity_type:, scope: nil, parent_scope: nil) fields = TypedEAV::Field::Base .where(entity_type: entity_type, scope: scope, parent_scope: parent_scope) .includes(:field_options) .order(:sort_order) .map { |field| export_snapshot_field_entry(field) } { "snapshot_schema_version" => 1, "fields" => fields, } end |
.import_schema(hash, on_conflict: :error) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/typed_eav/schema_portability.rb', line 109 def import_schema(hash, on_conflict: :error) validate_schema_version!(hash) validate_conflict_policy!(on_conflict) result = { "created" => 0, "updated" => 0, "skipped" => 0, "unchanged" => 0, "errors" => [] } field_entries = Array(hash["fields"]) section_entries = Array(hash["sections"]) TypedEAV::Field::Base.transaction do import_index = ImportIndex.new(field_entries, section_entries) field_entries.each do |entry| import_field_entry(entry, on_conflict, result, import_index) end section_entries.each do |entry| import_section_entry(entry, on_conflict, result, import_index) end end result end |
.preview_schema(hash, on_conflict: :error) ⇒ Object
Compare an exported schema with the exact target partition without invoking the import pipeline. The result is a JSON-safe, read-only snapshot of the definitions currently in the database and the conditional action the requested conflict policy would take.
:error marks divergent definitions as blocked, :skip predicts
leaving them unchanged, and :overwrite predicts replacement. Type
changes are always blocked because the importer refuses unsafe typed
value conversions under every policy. Omitted target definitions are
intentionally absent from the result: import_schema never deletes
them. A preview is advisory and does not lock or reserve the target;
custom validations and concurrent changes can still affect a later
import. Every field/section entry must also repeat the exact
entity_type/scope/parent_scope envelope identity; inconsistent
payloads are rejected instead of being silently retargeted.
105 106 107 |
# File 'lib/typed_eav/schema_portability.rb', line 105 def preview_schema(hash, on_conflict: :error) TypedEAV::SchemaPortability::Preview.new(hash, on_conflict: on_conflict).call end |