Rebrand sexp → sx across web platform (173 files)
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 11m37s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 11m37s
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>
This commit is contained in:
101
shared/sx/relations.py
Normal file
101
shared/sx/relations.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user