Ash calculation that produces per-record boolean values for UI visibility.
Mirrors AshGrant.FilterCheck's logic to determine if the current actor
can perform a specific action on each record. The result compiles to SQL
via expression/2, running in a single query with no N+1.
Usage
DSL Sugar (Recommended)
Use can_perform entities or the can_perform_actions batch option in your
ash_grant block. The transformer auto-generates calculations at compile time:
ash_grant do
resolver MyApp.PermissionResolver
scope :always, true
scope :own, expr(author_id == ^actor(:id))
# Batch — generates :can_update? and :can_destroy?
can_perform_actions [:update, :destroy]
# Individual with custom name
can_perform :read, name: :visible?
endExplicit Module (Advanced)
For cases where you need full control (e.g., custom resource_name), you can
still declare the calculation explicitly:
calculations do
calculate :can_update?, :boolean,
{AshGrant.Calculation.CanPerform, action: "update", resource: __MODULE__},
public?: true
endDSL-generated and explicit calculations can coexist. If both declare the same
name, the explicit one takes precedence (the transformer uses add_new_calculation
which skips duplicates).
Querying
members = Member |> Ash.Query.load([:can_update?, :can_destroy?]) |> Ash.read!(actor: actor)In templates:
<.button :if={member.can_update?}>Edit</.button>Options (Explicit Module)
| Option | Type | Description |
|---|---|---|
:action | string | Required. Action name for permission matching (e.g., "update", "destroy") |
:resource | module | Required. The resource module. Use __MODULE__ in the calculations block |
:resource_name | string | Override resource name for permission matching |
How It Works
- Resolves actor's permissions via the configured
PermissionResolver - Gets RBAC scopes via
AshGrant.Evaluator.get_all_scopes/4 - Gets instance IDs via
AshGrant.Evaluator.get_matching_instance_ids/4 - Builds a combined boolean expression:
- "always"/"all"/"global" in scopes ->
true - No scopes AND no instances ->
false - RBAC scopes -> scope filters combined with OR
- Instance IDs ->
id in ^instance_ids - Both -> RBAC filter OR instance filter
- "always"/"all"/"global" in scopes ->
See Also
AshGrant.Transformers.AddCanPerformCalculations- The transformer that generates calculations from DSLAshGrant.FilterCheck- The policy check this mirrorsAshGrant.Evaluator- Permission evaluation logic
Summary
Functions
Callback implementation for Ash.Resource.Calculation.has_expression?/0.
Callback implementation for Ash.Resource.Calculation.load/3.
Callback implementation for Ash.Resource.Calculation.strict_loads?/0.
Functions
Callback implementation for Ash.Resource.Calculation.has_expression?/0.
Callback implementation for Ash.Resource.Calculation.load/3.
Callback implementation for Ash.Resource.Calculation.strict_loads?/0.