Module: TypedEAV::Partition

Defined in:
lib/typed_eav/partition.rb

Overview

Partition-aware visibility for schema objects keyed by the canonical ‘(entity_type, scope, parent_scope)` tuple.

This module is deliberately explicit: callers pass already-resolved scope values. Ambient resolution (‘TypedEAV.current_scope`, `with_scope`, `unscoped`) stays with the adapters that know their calling context.

Class Method Summary collapse

Class Method Details

.definitions_by_name(defs) ⇒ Object

Indexes field definitions by name with deterministic three-way collision resolution: when global (scope=NULL, parent_scope=NULL), scope-only (scope set, parent_scope=NULL), and full-triple (both set) fields share a name, the most-specific row wins.

Sort key ‘[scope.nil? ? 0 : 1, parent_scope.nil? ? 0 : 1]` orders rows:

[0, 0] global              (least specific) -> comes first
[1, 0] scope-only          (middle)
[1, 1] full triple         (most specific)  -> comes last

‘index_by(&:name)` keeps the LAST entry on duplicate keys (Rails convention via `Array#to_h`), so most-specific wins. The two-key sort extends the prior “scoped beats global” rule into “two-key beats one-key beats global” without changing the index_by-last-wins mechanism. The `(scope=NULL, parent_scope=NOT NULL)` slot is unreachable by construction (orphan-parent invariant in Field::Base), so the ordering is exhaustive across the three valid shapes.

Shared by the class-query path (FilterQuery / BulkRead / EntityQuery) and the instance path (HasTypedEAV::InstanceMethods#typed_eav_defs_by_name) so the two cannot drift. Lives on Partition because partition-tuple precedence is a partition concept.



66
67
68
69
70
# File 'lib/typed_eav/partition.rb', line 66

def definitions_by_name(defs)
  defs.to_a
      .sort_by { |d| [d.scope.nil? ? 0 : 1, d.parent_scope.nil? ? 0 : 1] }
      .index_by(&:name)
end

.definitions_multimap_by_name(defs) ⇒ Object

Indexes field definitions by name into a multi-map (one name -> array of fields). Used by the class-query path under ‘TypedEAV.unscoped { }`, where the same field name may legitimately exist across multiple tenant partitions and we must OR-across all matching field_ids per filter rather than collapse to a single row.



77
78
79
# File 'lib/typed_eav/partition.rb', line 77

def definitions_multimap_by_name(defs)
  defs.to_a.group_by(&:name)
end

.effective_fields_by_name(entity_type:, scope: nil, parent_scope: nil, mode: :partition) ⇒ Object

One visible field per name after collision resolution. Most-specific wins: full tuple beats scope-only, scope-only beats global.



35
36
37
38
39
40
41
42
# File 'lib/typed_eav/partition.rb', line 35

def effective_fields_by_name(entity_type:, scope: nil, parent_scope: nil, mode: :partition)
  fields = visible_fields(entity_type: entity_type, scope: scope, parent_scope: parent_scope, mode: mode)
  if mode == :all_partitions
    definitions_multimap_by_name(fields)
  else
    definitions_by_name(fields)
  end
end

.find_visible_section!(id, entity_type:, scope: nil, parent_scope: nil, mode: :partition) ⇒ Object



91
92
93
# File 'lib/typed_eav/partition.rb', line 91

def find_visible_section!(id, entity_type:, scope: nil, parent_scope: nil, mode: :partition)
  visible_sections(entity_type: entity_type, scope: scope, parent_scope: parent_scope, mode: mode).find(id)
end

.visible_fields(entity_type:, scope: nil, parent_scope: nil, mode: :partition) ⇒ Object

All field definitions visible from a tuple: pure global rows, scope-only rows, and full-tuple rows. Passing mode: :all_partitions is the deliberate admin bypass; it is distinct from ‘scope: nil`, which means the global partition only.

Raises:

  • (ArgumentError)


24
25
26
27
28
29
30
31
# File 'lib/typed_eav/partition.rb', line 24

def visible_fields(entity_type:, scope: nil, parent_scope: nil, mode: :partition)
  validate_mode!(mode)
  return TypedEAV::Field::Base.where(entity_type: entity_type) if mode == :all_partitions

  raise ArgumentError, ORPHAN_PARENT_MESSAGE unless ScopeTuple.invariant_satisfied?(scope, parent_scope)

  TypedEAV::Field::Base.for_entity(entity_type, scope: scope, parent_scope: parent_scope)
end

.visible_sections(entity_type:, scope: nil, parent_scope: nil, mode: :partition) ⇒ Object

All sections visible from the same tuple as field definitions.

Raises:

  • (ArgumentError)


82
83
84
85
86
87
88
89
# File 'lib/typed_eav/partition.rb', line 82

def visible_sections(entity_type:, scope: nil, parent_scope: nil, mode: :partition)
  validate_mode!(mode)
  return TypedEAV::Section.where(entity_type: entity_type) if mode == :all_partitions

  raise ArgumentError, ORPHAN_PARENT_MESSAGE unless ScopeTuple.invariant_satisfied?(scope, parent_scope)

  TypedEAV::Section.for_entity(entity_type, scope: scope, parent_scope: parent_scope)
end