AshGrant.Calculation.CanPerform (AshGrant v0.14.1)

Copy Markdown View Source

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

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?
end

Explicit 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
end

DSL-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)

OptionTypeDescription
:actionstringRequired. Action name for permission matching (e.g., "update", "destroy")
:resourcemoduleRequired. The resource module. Use __MODULE__ in the calculations block
:resource_namestringOverride resource name for permission matching

How It Works

  1. Resolves actor's permissions via the configured PermissionResolver
  2. Gets RBAC scopes via AshGrant.Evaluator.get_all_scopes/4
  3. Gets instance IDs via AshGrant.Evaluator.get_matching_instance_ids/4
  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

See Also

Summary

Functions

has_calculate?()

has_expression?()

Callback implementation for Ash.Resource.Calculation.has_expression?/0.

load(query, opts, context)

Callback implementation for Ash.Resource.Calculation.load/3.

strict_loads?()

Callback implementation for Ash.Resource.Calculation.strict_loads?/0.