Preparations are the primary way of customizing read action behavior. If you are familiar with Plug
, you can think of an Ash.Resource.Preparation
as the equivalent of a Plug
for queries. At its most basic, a preparation will take a query and return a new query. Queries can be simple, like adding a filter or a sort, or more complex, attaching hooks to be executed within the lifecycle of the action.
There are builtin preparations that can be used, and are automatically imported into your resources. See Ash.Resource.Preparation.Builtins
for more.
The primary preparation you will use is build/1
, which passes the arguments through to Ash.Query.build/2
when the preparation is run. See that function for what options can be provided.
Some examples of usage of builtin preparations
# sort by inserted at descending
prepare build(sort: [inserted_at: :desc])
# only show the top 5 results
prepare build(sort: [total_points: :desc], limit: 5)
defmodule MyApp.Preparations.Top5 do
use Ash.Resource.Preparation
# transform and validate opts
@impl true
def init(opts) do
if is_atom(opts[:attribute]) do
{:ok, opts}
else
{:error, "attribute must be an atom!"}
end
end
@impl true
def prepare(query, opts, _context) do
attribute = opts[:attribute]
query
|> Ash.Query.sort([{attribute, :desc}])
|> Ash.Query.limit(5)
end
end
This could then be used in a resource via:
prepare {MyApp.Preparations.Top5, attribute: :foo}
You can also use anonymous functions for preparations. This is great for prototyping, but we generally recommend using a module for organizational purposes.
prepare fn query, _context ->
# put your code here
end
You can place a preparation on a read action, like so:
actions do
read :read do
prepare {Top5, attribute: :name}
end
end
Or you can use the global preparations block to apply to all read actions.
preparations do
prepare {Top5, attribute: :name}
end
The preparations section allows you to add preparations across multiple actions of a resource.