Files
rose-ash/shared/sx/relations.py
giles e8bc228c7f
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 11m37s
Rebrand sexp → sx across web platform (173 files)
Rename all sexp directories, files, identifiers, and references to sx.
artdag/ excluded (separate media processing DSL).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:06:57 +00:00

102 lines
2.7 KiB
Python

"""
Relation registry — declarative entity relationship definitions.
Relations are defined as s-expressions using ``defrelation`` and stored
in a global registry. All services load the same definitions at startup
via ``load_relation_registry()``.
"""
from __future__ import annotations
from shared.sx.types import RelationDef
# ---------------------------------------------------------------------------
# Registry
# ---------------------------------------------------------------------------
_RELATION_REGISTRY: dict[str, RelationDef] = {}
def register_relation(defn: RelationDef) -> None:
"""Add a RelationDef to the global registry."""
_RELATION_REGISTRY[defn.name] = defn
def get_relation(name: str) -> RelationDef | None:
"""Look up a relation by name (e.g. ``"page->market"``)."""
return _RELATION_REGISTRY.get(name)
def relations_from(entity_type: str) -> list[RelationDef]:
"""All relations where *entity_type* is the ``from`` side."""
return [d for d in _RELATION_REGISTRY.values() if d.from_type == entity_type]
def relations_to(entity_type: str) -> list[RelationDef]:
"""All relations where *entity_type* is the ``to`` side."""
return [d for d in _RELATION_REGISTRY.values() if d.to_type == entity_type]
def all_relations() -> list[RelationDef]:
"""Return all registered relations."""
return list(_RELATION_REGISTRY.values())
def clear_registry() -> None:
"""Clear all registered relations (for testing)."""
_RELATION_REGISTRY.clear()
# ---------------------------------------------------------------------------
# Built-in relation definitions (s-expression source)
# ---------------------------------------------------------------------------
_BUILTIN_RELATIONS = '''
(begin
(defrelation :page->market
:from "page"
:to "market"
:cardinality :one-to-many
:inverse :market->page
:nav :submenu
:nav-icon "fa fa-shopping-bag"
:nav-label "markets")
(defrelation :page->calendar
:from "page"
:to "calendar"
:cardinality :one-to-many
:inverse :calendar->page
:nav :submenu
:nav-icon "fa fa-calendar"
:nav-label "calendars")
(defrelation :post->calendar_entry
:from "post"
:to "calendar_entry"
:cardinality :many-to-many
:inverse :calendar_entry->post
:nav :inline
:nav-icon "fa fa-file-alt"
:nav-label "events")
(defrelation :page->menu_node
:from "page"
:to "menu_node"
:cardinality :one-to-one
:nav :hidden)
)
'''
def load_relation_registry() -> None:
"""Parse built-in defrelation s-expressions and populate the registry."""
from shared.sx.evaluator import evaluate
from shared.sx.parser import parse
tree = parse(_BUILTIN_RELATIONS)
evaluate(tree)