Compare commits
No commits in common. "c91b1cd35c548dcc6eb91c9ed2fd4dd4ef7ad53a" and "dc2ed27da4ba3ae8ca9dca67592686af49f17830" have entirely different histories.
c91b1cd35c
...
dc2ed27da4
2 changed files with 8 additions and 94 deletions
|
|
@ -24,13 +24,12 @@
|
||||||
//!
|
//!
|
||||||
//! - **Acceso**: [`DatabaseConnection`], [`dbconn`] (para obtener el pool de conexiones).
|
//! - **Acceso**: [`DatabaseConnection`], [`dbconn`] (para obtener el pool de conexiones).
|
||||||
//! - **Consultas**: [`EntityTrait`], [`QueryFilter`], [`QueryOrder`], [`QuerySelect`].
|
//! - **Consultas**: [`EntityTrait`], [`QueryFilter`], [`QueryOrder`], [`QuerySelect`].
|
||||||
//! - **Transacciones**: [`TransactionTrait`], [`DatabaseTransaction`], [`flatten_txn_err`]
|
//! - **Transacciones**: [`TransactionTrait`], [`DatabaseTransaction`].
|
||||||
//! (para convertir [`TransactionError<E>`] a `E`).
|
|
||||||
//! - **Modelos activos**: [`ActiveModelTrait`], [`ActiveValue`] ([`ActiveValue::Set`],
|
//! - **Modelos activos**: [`ActiveModelTrait`], [`ActiveValue`] ([`ActiveValue::Set`],
|
||||||
//! [`ActiveValue::Unchanged`], [`ActiveValue::NotSet`]).
|
//! [`ActiveValue::Unchanged`], [`ActiveValue::NotSet`]).
|
||||||
//! - **Macros de derivación**: [`DeriveEntityModel`], [`DeriveColumn`], [`DerivePrimaryKey`],
|
//! - **Macros de derivación**: [`DeriveEntityModel`], [`DeriveColumn`], [`DerivePrimaryKey`],
|
||||||
//! [`DeriveRelation`], [`EnumIter`].
|
//! [`DeriveRelation`], [`EnumIter`].
|
||||||
//! - **Errores**: [`DbErr`], [`TransactionError`].
|
//! - **Errores**: [`DbErr`].
|
||||||
//! - **Resultados**: [`QueryResult`] (filas sin tipar), [`ExecResult`] (INSERT/UPDATE/DELETE),
|
//! - **Resultados**: [`QueryResult`] (filas sin tipar), [`ExecResult`] (INSERT/UPDATE/DELETE),
|
||||||
//! [`Paginated`] (página de resultados).
|
//! [`Paginated`] (página de resultados).
|
||||||
//!
|
//!
|
||||||
|
|
@ -330,74 +329,6 @@ pub async fn fetch_one<Q: query::QueryStatementWriter>(
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convierte el error de una transacción (`TransactionError<E>`) al propio tipo `E`.
|
|
||||||
///
|
|
||||||
/// [`TransactionTrait::transaction`] puede fallar de dos formas distintas:
|
|
||||||
/// [`TransactionError::Connection`] si el fallo ocurre en la propia transacción (conexión,
|
|
||||||
/// `BEGIN`/`COMMIT`/`ROLLBACK`...), o [`TransactionError::Transaction`] si el fallo es el error que
|
|
||||||
/// devolvió la clausura. `flatten_txn_err()` convierte ambos casos al mismo tipo `E` (usando
|
|
||||||
/// `From<DbErr>` para el primero), de modo que el resultado se pueda propagar con
|
|
||||||
/// `.map_err(flatten_txn_err)?` sin distinguir el origen del error.
|
|
||||||
///
|
|
||||||
/// Requiere que el tipo de error propio de la aplicación implemente `From<DbErr>`, lo habitual con
|
|
||||||
/// `#[derive(thiserror::Error)]` y `#[from]`.
|
|
||||||
///
|
|
||||||
/// Uso directo, en el punto donde se resuelve la transacción, sin nada más que declarar:
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// use pagetop_seaorm::db::*;
|
|
||||||
///
|
|
||||||
/// async fn example() -> Result<(), DbErr> {
|
|
||||||
/// dbconn()
|
|
||||||
/// .transaction::<_, (), DbErr>(|_txn| Box::pin(async move { Ok(()) }))
|
|
||||||
/// .await
|
|
||||||
/// .map_err(flatten_txn_err)
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Si una aplicación hace transacciones en muchos puntos con su propio tipo de error, puede delegar
|
|
||||||
/// en `flatten_txn_err()` una sola vez mediante `impl From<TransactionError<E>> for E` (legal
|
|
||||||
/// porque `E` es un tipo local de la aplicación, no genérico) y despreocuparse de `.map_err()` en
|
|
||||||
/// el resto de llamadas, propagando el error con `?` directamente:
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// use pagetop_seaorm::db::*;
|
|
||||||
///
|
|
||||||
/// #[derive(Debug)]
|
|
||||||
/// struct MyError(DbErr);
|
|
||||||
///
|
|
||||||
/// impl std::fmt::Display for MyError {
|
|
||||||
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
/// write!(f, "database error: {}", self.0)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl From<DbErr> for MyError {
|
|
||||||
/// fn from(err: DbErr) -> Self {
|
|
||||||
/// MyError(err)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl From<TransactionError<MyError>> for MyError {
|
|
||||||
/// fn from(err: TransactionError<MyError>) -> Self {
|
|
||||||
/// flatten_txn_err(err)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// async fn example() -> Result<(), MyError> {
|
|
||||||
/// dbconn()
|
|
||||||
/// .transaction::<_, (), MyError>(|_txn| Box::pin(async move { Ok(()) }))
|
|
||||||
/// .await?;
|
|
||||||
/// Ok(())
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn flatten_txn_err<E: From<DbErr>>(err: TransactionError<E>) -> E {
|
|
||||||
match err {
|
|
||||||
TransactionError::Connection(db_err) => db_err.into(),
|
|
||||||
TransactionError::Transaction(err) => err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// **< Paginated / paginate >***********************************************************************
|
// **< Paginated / paginate >***********************************************************************
|
||||||
|
|
||||||
/// Página de resultados de una consulta paginada.
|
/// Página de resultados de una consulta paginada.
|
||||||
|
|
@ -452,7 +383,7 @@ impl<T> Paginated<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ejecuta una consulta paginada con el sistema de entidades y retorna la página solicitada.
|
/// Ejecuta una consulta paginada con el sistema de entidades y devuelve la página solicitada.
|
||||||
///
|
///
|
||||||
/// Añade la metadata de paginación (`total`, `total_pages`); `page` y `per_page` se ajustan a un
|
/// Añade la metadata de paginación (`total`, `total_pages`); `page` y `per_page` se ajustan a un
|
||||||
/// mínimo de `1`, ya que no existe la página `0` ni un tamaño de página vacío.
|
/// mínimo de `1`, ya que no existe la página `0` ni un tamaño de página vacío.
|
||||||
|
|
|
||||||
|
|
@ -11,28 +11,11 @@
|
||||||
//! [`Context`](crate::core::component::Context), donde mantiene el tema activo, la plantilla
|
//! [`Context`](crate::core::component::Context), donde mantiene el tema activo, la plantilla
|
||||||
//! seleccionada y los componentes asociados a cada región a renderizar.
|
//! seleccionada y los componentes asociados a cada región a renderizar.
|
||||||
//!
|
//!
|
||||||
//! # Temas hijo, herencia y componentes
|
//! Además, PageTop permite crear **temas hijo** que refinan el comportamiento de su tema padre. Un
|
||||||
//!
|
//! tema hijo hereda automáticamente todos los métodos del padre y puede sobrescribirlos
|
||||||
//! PageTop permite crear **temas hijo** que refinan el comportamiento de su tema padre. Un tema
|
//! selectivamente. Por ejemplo, puede redefinir el renderizado de un componente a través de
|
||||||
//! hijo hereda automáticamente todos los métodos del padre y puede sobrescribirlos selectivamente.
|
//! [`Theme::handle_component()`] sin cambiar el resto del comportamiento heredado. Un tema hijo
|
||||||
//! Esta herencia sólo determina qué implementación de sus métodos se usa cuando el tema hijo no los
|
//! puede ser a su vez padre de otro, basta declararlo cada vez con [`Theme::parent()`].
|
||||||
//! sobrescribe (como el renderizado del `<body>` y del `<head>`, los recursos incorporados, el uso
|
|
||||||
//! de [`Theme::handle_component()`], las páginas de error, etc.). Un tema hijo puede ser a su vez
|
|
||||||
//! padre de otro, basta declararlo cada vez con [`Theme::parent()`].
|
|
||||||
//!
|
|
||||||
//! Sin embargo, no dice nada sobre los componentes. Aunque un tema puede exportar su propio
|
|
||||||
//! catálogo de componentes, realmente no pertenecen como tal a ningún tema ni dependen de esa
|
|
||||||
//! cadena de herencia. Una extensión puede existir únicamente para aportar un componente genérico
|
|
||||||
//! (por ejemplo, un editor de texto enriquecido) pensado para usarse en cualquier aplicación, con
|
|
||||||
//! independencia del tema activo. Que un tema decida capturar ese componente en
|
|
||||||
//! [`Theme::handle_component()`] para adaptarlo es una decisión propia del tema, no una relación de
|
|
||||||
//! parentesco: cualquier tema de la cadena de herencia puede interceptar cualquier componente,
|
|
||||||
//! venga de la extensión que venga, sin que exista ningún vínculo de diseño previo entre ambos.
|
|
||||||
//!
|
|
||||||
//! Lo que sí es responsabilidad del tema activo es garantizar que el componente disponga de los
|
|
||||||
//! recursos que necesita para verse y comportarse correctamente: sus propios estilos y JavaScript,
|
|
||||||
//! si los aporta, o los que ofrezca el tema. El componente genera su marcado igual aunque esos
|
|
||||||
//! recursos falten, pero el resultado seguramente no lucirá ni funcionará como se espera.
|
|
||||||
//!
|
//!
|
||||||
//! # Cómo crear un tema nuevo
|
//! # Cómo crear un tema nuevo
|
||||||
//!
|
//!
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue