Rule Cascade en Python
Paquete: pip install rule-cascade (Python 3.11+; importar rule_cascade). Los bundles se generan con
rcas compile --all en build/rules/. Nunca evalúes el YAML en tiempo de ejecución ni copies una regla en el código.
Cargar una vez, evaluar por petición
import json
from rule_cascade import RuleSet, RequestError
# Al inicio (nivel de módulo, lifespan de FastAPI, AppConfig.ready de Django). Un bundle malo lanza excepción: no servir.
with open("build/rules/<ruleset-id>.bundle.json", encoding="utf-8") as f:
rules = RuleSet.from_bundle(json.load(f))
Preferir el decorador (desde 1.0.0-alpha.7): construye la solicitud, aplica restricciones antes de que la función se ejecute y lanza RuleViolation, cuyo problem() es la respuesta 422 siguiente.
from rule_cascade import enforce_rules, current_evaluation
from rule_cascade.fastapi import install # o rule_cascade.flask.init_app(app), o el middleware de Django
install(app) # RuleViolation -> 422, RequestError -> 400
@app.post("/orders", status_code=201)
@enforce_rules(rules, entity="Order", operation="create", data="order", actor="user") # debajo del decorador de ruta
def create_order(order: Order, user: User = Depends(current_user)):
evaluation = current_evaluation() # el resultado permitido: advertencias, valores calculados, comandos
rules.enforce(request, "server") hace lo mismo sin decorador. Internamente,
rules.evaluate(request, "server") devuelve un diccionario: decision (allow o deny), findings,
effects, commands. Lanza RequestError para una solicitud mal formada (respuesta 400). Cada
endpoint que cambia estado sigue cuatro pasos:
- Evaluar en el servidor con
{"entity", "operation", "data", "original", "actor", "resolutions"};actorproviene de la autenticación ({"id", "roles"}), nunca de un campo que envíe el llamante. - Rechazar si es deny con 422 detalles del problema (
application/problem+json,type: "urn:rule-cascade:rule-violation") que incluyen el resultado, para que los clientes puedan mapearfindings[].fieldsa las entradas. - Persistir, después de aplicar efectos
compute(effect["type"] == "value"): se almacena el valor del servidor. - Ejecutar
result["commands"]después del commit, una vez poridempotencyKey(una tabla de outbox, o una cola de tareas con deduplicación).
Ubicación en cada framework: FastAPI como dependencia (Depends(get_rules)) y un manejador de excepciones que convierte
RuleViolation en el 422; Django como función de servicio llamada por cada vista o validate() del serializador
de la entidad, más middleware o manejador de excepciones DRF para el 422; Flask como objeto de extensión
creado en la fábrica de la aplicación. Celery y trabajos por lotes evalúan antes de cada escritura de la misma manera.
RuleSetHolder(load_bundle, interval=300) (o cron="0 * * * *") recarga los bundles en segundo plano
y mantiene las últimas reglas correctas cuando falla una recarga.
Muchos registros
Para archivos y colas, inicia rcas engine una vez y envía solicitudes JSON Lines a través de él, o llama
a rules.evaluate en un bucle: carga el bundle una sola vez de cualquier manera. Ver
https://rulescascade.com/examples/batch/.
Reemplazar validación existente
rcas analyze --module <módulo> lista campos Field(le=...) y validadores de Pydantic, esquemas de marshmallow,
validadores de Django, métodos clean() y permisos, comprobaciones manuales con raise, límites
con nombre (MAX_QTY = 10), comparaciones con números y ramas match. Un candidato con drift significa que otro lenguaje del proyecto (a menudo el frontend) decide el mismo nombre con otro valor: un conjunto de reglas debe decidirlo para ambos. Mantén la validación de tipos y formas (modelos Pydantic) donde está; mueve las decisiones de negocio a las reglas. Agrega una prueba de paridad que ejecute la comprobación antigua y el motor en las mismas entradas y elimina la comprobación antigua solo cuando coincidan.
Pruebas
Prueba decision y códigos de findings, no los textos de los mensajes. Las reglas mismas se prueban mediante sus
pruebas golden (rcas check). En CI, ejecuta rcas check y rcas compile --all antes de pytest.
Referencia: https://rulescascade.com/usage/python/