Aurora. Uix. Integration. Ash. Crud
(Aurora UIX v0.1.4-rc.2)
Copy Markdown
Ash Framework implementation of CRUD operations.
Provides CRUD operations for Ash resources using the Ash Framework API. Supports both paginated and non-paginated listing, along with standard create, read, update, delete operations.
Key Features
- Automatic action discovery and execution for Ash resources
- Support for paginated and non-paginated queries
- Query parsing with filters, sorting, and preloading
- Primary action detection with fallback to first available action
- AshPhoenix form integration for changesets
- Actor threading for policy-protected resources via
socket_opts/2(see "Authorization" below)
Key Constraints
- Requires valid Ash resource module with defined actions
- Pagination requires Ash action configured with
paginationoption - Preloading handled differently: via Ash.Query.load for queries, Ecto repo for new structs
Authorization
Every CRUD call accepts an optional :actor keyword in its opts. When present
(and non-nil) the actor is forwarded to the underlying Ash call (Ash.read/2,
Ash.get/3, Ash.create/3, Ash.update/3, Ash.destroy/2, Ash.load/3,
AshPhoenix.Form.for_update/3). When absent, no actor: is added — the host's
Ash domain authorize config (default :by_default) decides whether policies run.
authorize?: is never set explicitly by this module. The actor is resolved at
the call site by socket_opts/2, which reads CrudSpec.actor_assign (the atom
configured via auix_resource_metadata ..., ash_actor_assign: :current_user) and
pulls the actor from socket.assigns.
Resolution example
Given auix_resource_metadata :template, ash_resource: Tpl, ash_actor_assign: :current_user
and a LiveView with socket.assigns.current_user = %User{id: 1}:
iex> socket_opts(crud_spec, socket)
[actor: %User{id: 1}]Handlers extending Aurora UIX should not call socket_opts/2 directly — use
Aurora.Uix.Templates.Basic.Helpers.backend_socket_opts/2 instead, which is
backend-agnostic and accepts both full sockets and bare assigns maps.
See the Ash integration guide — Authorization & policies
for the end-to-end worked example, alias (:actor_assign), behaviour matrix,
and troubleshooting.
Summary
Functions
Creates an AshPhoenix form for updating an entity.
Creates a new resource in the database.
Default new function for initializing Ash resource structs.
Deletes a resource from the database.
Retrieves a single resource by ID.
Lists resources from an Ash action with optional query parameters.
Creates a new Ash resource struct with optional preloading.
Resolves per-call options from a LiveView socket for a given Ash CrudSpec.
Loads a specific page of results for paginated data.
Updates an existing resource in the database.
Functions
@spec change( Aurora.Uix.Integration.Ash.CrudSpec.t(), struct(), atom() | binary(), map(), keyword() ) :: AshPhoenix.Form.t()
Creates an AshPhoenix form for updating an entity.
Parameters
crud_spec(CrudSpec.t()) - The CrudSpec containing action configuration.entity(struct()) - The Ash resource struct to update.form_name(atom() | binary()) - Underlying form name.attrs(map()) - Attributes to apply to the form.opts(keyword()) - Additional options::actor(term()) - Actor forwarded toAshPhoenix.Form.for_update/3.
Returns
AshPhoenix.Form.t() - The form structure for the update operation.
Examples
iex> crud_spec = %CrudSpec{action: %{name: :update}}
iex> change(crud_spec, %MyApp.User{}, "user", %{name: "John"})
%AshPhoenix.Form{...}
@spec create(Aurora.Uix.Integration.Ash.CrudSpec.t(), map(), keyword()) :: tuple()
Creates a new resource in the database.
Parameters
crud_spec(CrudSpec.t()) - The CrudSpec containing resource and action configuration.params(map()) - Parameters for the new resource.opts(keyword()) - Additional options::actor(term()) - Actor forwarded toAsh.create/3.
Returns
tuple() - Result tuple, typically {:ok, struct()} or {:error, struct()}.
Examples
iex> crud_spec = %CrudSpec{resource: MyApp.User, action: %{name: :create}}
iex> create(crud_spec, %{name: "Alice", email: "alice@example.com"})
{:ok, %MyApp.User{name: "Alice", email: "alice@example.com"}}
Default new function for initializing Ash resource structs.
Returns the entity struct as-is without modifications. This function serves as the
default implementation for the :new_function operation when no custom function is
provided via :ash_new_function option.
Parameters
entity(struct()) - The Ash resource struct to initialize.opts(keyword()) - Options (currently unused).
Returns
struct() - The unmodified entity struct.
Examples
iex> post = %Post{status: :draft}
iex> default_new_function(post, [])
%Post{status: :draft}
@spec delete(Aurora.Uix.Integration.Ash.CrudSpec.t(), struct(), keyword()) :: tuple()
Deletes a resource from the database.
Parameters
crud_spec(CrudSpec.t()) - The CrudSpec containing action configuration.entity(struct()) - The resource to delete.opts(keyword()) - Additional options::actor(term()) - Actor forwarded toAsh.destroy/2.
Returns
tuple() - Result tuple, typically {:ok, struct()} or {:error, struct()}.
Examples
iex> crud_spec = %CrudSpec{action: %{name: :destroy}}
iex> delete(crud_spec, %MyApp.User{id: 1})
{:ok, %MyApp.User{id: 1}}
@spec get(Aurora.Uix.Integration.Ash.CrudSpec.t(), term(), keyword()) :: struct() | nil
Retrieves a single resource by ID.
Parameters
crud_spec(CrudSpec.t()) - The CrudSpec containing resource and action configuration.id(term()) - The resource identifier.opts(keyword()) - Query options::actor(term()) - Actor forwarded toAsh.get/3andAsh.load/3.:preload(term()) - Associations to load.
Returns
struct() | nil - The matching resource or nil if not found or error occurs.
Examples
iex> crud_spec = %CrudSpec{resource: MyApp.User, action: %{name: :read}}
iex> get(crud_spec, "123", preload: [:posts])
%MyApp.User{id: "123", ...}
iex> get(crud_spec, "missing-id", [])
nil
@spec list( Aurora.Uix.Integration.Ash.CrudSpec.t(), keyword() ) :: Aurora.Ctx.Pagination.t()
Lists resources from an Ash action with optional query parameters.
Parameters
crud_spec(CrudSpec.t()) - The CrudSpec containing resource and action configuration.opts(keyword()) - Query options::actor(term()) - Actor forwarded toAsh.Query.for_read/3andAsh.read/2for policy-protected resources.:where(list()) - Filter clauses.:order_by(term()) - Sort specification.:preload(term()) - Associations to load.:paginate(Pagination.t()) - Pagination configuration (for paginated action).
Returns
Pagination.t() - %Pagination{} structure containing query results and metadata.
Examples
iex> crud_spec = %CrudSpec{resource: MyApp.User, action: %{name: :read, pagination: false}}
iex> list(crud_spec, where: [{:status, :eq, "active"}])
%Pagination{entries: [...], pages_count: 1, per_page: :infinity}
iex> crud_spec = %CrudSpec{resource: MyApp.Post, action: %{name: :read, pagination: true},
...> auix_action_name: :list_function_paginated}
iex> list(crud_spec, paginate: %Pagination{page: 1, per_page: 20})
%Pagination{entries: [...], page: 1, pages_count: 5, per_page: 20}
@spec new(Aurora.Uix.Integration.Ash.CrudSpec.t(), map(), keyword()) :: struct()
Creates a new Ash resource struct with optional preloading.
Parameters
crud_spec(CrudSpec.t()) - The CrudSpec containing resource configuration.attrs(map()) - Initial attributes for the new resource.opts(keyword()) - Options::actor(term()) - Actor forwarded toAsh.load/3during preload.:preload(list()) - Associations to preload.
Returns
struct() - A new resource struct with the provided attributes and preloaded associations.
Examples
iex> crud_spec = %CrudSpec{resource: MyApp.User}
iex> new(crud_spec, %{name: "Jane"}, preload: [:profile])
%MyApp.User{name: "Jane", profile: %MyApp.Profile{}}
iex> new(crud_spec, %{title: "Hello"}, [])
%MyApp.Post{title: "Hello"}
@spec socket_opts( Aurora.Uix.Integration.Ash.CrudSpec.t() | term(), Phoenix.LiveView.Socket.t() | map() ) :: keyword()
Resolves per-call options from a LiveView socket for a given Ash CrudSpec.
Reads crud_spec.actor_assign, looks up that key on socket.assigns, and returns
[actor: actor] when the assign holds a non-nil value. Otherwise returns [].
Safe to merge into any CRUD call's opts via ++.
Parameters
crud_spec(CrudSpec.t() | term()) - The CrudSpec carrying:actor_assign. Non%CrudSpec{}values are treated as not-applicable and return[].socket(Phoenix.LiveView.Socket.t() | map()) - A LiveView socket or any map with an:assignsfield.
Returns
keyword() - [actor: actor] when configured and resolved, otherwise [].
Examples
iex> socket_opts(%CrudSpec{actor_assign: nil}, %{assigns: %{current_user: %{id: 1}}})
[]
iex> socket_opts(%CrudSpec{actor_assign: :current_user},
...> %{assigns: %{current_user: %{id: 1}}})
[actor: %{id: 1}]
iex> socket_opts(%CrudSpec{actor_assign: :current_user}, %{assigns: %{}})
[]
@spec to_page( Aurora.Uix.Integration.Ash.CrudSpec.t(), Aurora.Ctx.Pagination.t(), integer(), keyword() ) :: Aurora.Ctx.Pagination.t()
Loads a specific page of results for paginated data.
Parameters
crud_spec(CrudSpec.t()) - The CrudSpec (currently unused for page bounds checking).pagination(Pagination.t()) - The current pagination structure.page(integer()) - The page number to load (must be >= 1 and <= pages_count).opts(keyword()) - Additional options. Honours:actorfor policy-protected reads.
Returns
Pagination.t() - Updated %Pagination{} structure with the requested page data, or
unchanged pagination if page is out of bounds.
Examples
iex> crud_spec = %CrudSpec{resource: MyApp.User, action: %{name: :read}}
iex> to_page(crud_spec, %Pagination{page: 1, pages_count: 5, per_page: 20}, 3)
%Pagination{entries: [...], page: 3, pages_count: 5, per_page: 20}
iex> to_page(crud_spec, %Pagination{page: 1, pages_count: 5}, 10)
%Pagination{page: 1, pages_count: 5}
@spec update(Aurora.Uix.Integration.Ash.CrudSpec.t(), struct(), map(), keyword()) :: tuple()
Updates an existing resource in the database.
Parameters
crud_spec(CrudSpec.t()) - The CrudSpec containing action configuration.entity(struct()) - The resource to update.params(map()) - Parameters to update.opts(keyword()) - Additional options::actor(term()) - Actor forwarded toAsh.update/3.
Returns
tuple() - Result tuple, typically {:ok, struct()} or {:error, struct()}.
Examples
iex> crud_spec = %CrudSpec{action: %{name: :update}}
iex> update(crud_spec, %MyApp.User{id: 1}, %{name: "Bob"})
{:ok, %MyApp.User{id: 1, name: "Bob"}}