Replace env free-variable threading with IO-primitive auto-fetch macros
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m38s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m38s
Layout components now self-resolve context (cart-mini, auth-menu, nav-tree, rights, URLs) via new IO primitives (root-header-ctx, select-colours, account-nav-ctx, app-rights) and defmacro wrappers (~root-header-auto, ~auth-header-row-auto, ~root-mobile-auto). This eliminates _ctx_to_env(), HELPER_CSS_CLASSES, and verbose :key threading across all 10 services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,10 @@ IO_PRIMITIVES: frozenset[str] = frozenset({
|
||||
"abort",
|
||||
"url-for",
|
||||
"route-prefix",
|
||||
"root-header-ctx",
|
||||
"select-colours",
|
||||
"account-nav-ctx",
|
||||
"app-rights",
|
||||
})
|
||||
|
||||
|
||||
@@ -378,6 +382,106 @@ async def _io_route_prefix(
|
||||
return route_prefix()
|
||||
|
||||
|
||||
async def _io_root_header_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(root-header-ctx)`` → dict with all root header values.
|
||||
|
||||
Fetches cart-mini, auth-menu, nav-tree fragments and computes
|
||||
settings-url / is-admin from rights. Result is cached on ``g``
|
||||
per request so multiple calls (e.g. header + mobile) are free.
|
||||
"""
|
||||
from quart import g, current_app, request
|
||||
cached = getattr(g, "_root_header_ctx", None)
|
||||
if cached is not None:
|
||||
return cached
|
||||
|
||||
from shared.infrastructure.fragments import fetch_fragments
|
||||
from shared.infrastructure.cart_identity import current_cart_identity
|
||||
from shared.infrastructure.urls import app_url
|
||||
from shared.config import config
|
||||
from .types import NIL
|
||||
|
||||
user = getattr(g, "user", None)
|
||||
ident = current_cart_identity()
|
||||
|
||||
cart_params: dict[str, Any] = {}
|
||||
if ident["user_id"] is not None:
|
||||
cart_params["user_id"] = ident["user_id"]
|
||||
if ident["session_id"] is not None:
|
||||
cart_params["session_id"] = ident["session_id"]
|
||||
|
||||
auth_params: dict[str, Any] = {}
|
||||
if user and getattr(user, "email", None):
|
||||
auth_params["email"] = user.email
|
||||
|
||||
nav_params = {"app_name": current_app.name, "path": request.path}
|
||||
|
||||
cart_mini, auth_menu, nav_tree = await fetch_fragments([
|
||||
("cart", "cart-mini", cart_params or None),
|
||||
("account", "auth-menu", auth_params or None),
|
||||
("blog", "nav-tree", nav_params),
|
||||
])
|
||||
|
||||
rights = getattr(g, "rights", None) or {}
|
||||
is_admin = (
|
||||
rights.get("admin", False)
|
||||
if isinstance(rights, dict)
|
||||
else getattr(rights, "admin", False)
|
||||
)
|
||||
|
||||
result = {
|
||||
"cart-mini": cart_mini or NIL,
|
||||
"blog-url": app_url("blog", ""),
|
||||
"site-title": config()["title"],
|
||||
"app-label": current_app.name,
|
||||
"nav-tree": nav_tree or NIL,
|
||||
"auth-menu": auth_menu or NIL,
|
||||
"nav-panel": NIL,
|
||||
"settings-url": app_url("blog", "/settings/") if is_admin else "",
|
||||
"is-admin": is_admin,
|
||||
}
|
||||
g._root_header_ctx = result
|
||||
return result
|
||||
|
||||
|
||||
async def _io_select_colours(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> str:
|
||||
"""``(select-colours)`` → the shared select/hover CSS class string."""
|
||||
from quart import current_app
|
||||
return current_app.jinja_env.globals.get("select_colours", "")
|
||||
|
||||
|
||||
async def _io_account_nav_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> Any:
|
||||
"""``(account-nav-ctx)`` → account nav fragments as SxExpr, or NIL.
|
||||
|
||||
Reads ``g.account_nav`` (set by account service's before_request hook),
|
||||
wrapping HTML strings in ``~rich-text`` for SX rendering.
|
||||
"""
|
||||
from quart import g
|
||||
from .types import NIL
|
||||
from .parser import SxExpr
|
||||
val = getattr(g, "account_nav", None)
|
||||
if not val:
|
||||
return NIL
|
||||
if isinstance(val, SxExpr):
|
||||
return val
|
||||
# HTML string → wrap for SX rendering
|
||||
escaped = str(val).replace("\\", "\\\\").replace('"', '\\"')
|
||||
return SxExpr(f'(~rich-text :html "{escaped}")')
|
||||
|
||||
|
||||
async def _io_app_rights(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(app-rights)`` → user rights dict from ``g.rights``."""
|
||||
from quart import g
|
||||
return getattr(g, "rights", None) or {}
|
||||
|
||||
|
||||
_IO_HANDLERS: dict[str, Any] = {
|
||||
"frag": _io_frag,
|
||||
"query": _io_query,
|
||||
@@ -394,4 +498,8 @@ _IO_HANDLERS: dict[str, Any] = {
|
||||
"abort": _io_abort,
|
||||
"url-for": _io_url_for,
|
||||
"route-prefix": _io_route_prefix,
|
||||
"root-header-ctx": _io_root_header_ctx,
|
||||
"select-colours": _io_select_colours,
|
||||
"account-nav-ctx": _io_account_nav_ctx,
|
||||
"app-rights": _io_app_rights,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user