# `AshGrant.Calculation.CanPerform`
[🔗](https://github.com/jhlee111/ash_grant/blob/v0.14.1/lib/ash_grant/calculations/can_perform.ex#L1)

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

| 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

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

- `AshGrant.Transformers.AddCanPerformCalculations` - The transformer that generates calculations from DSL
- `AshGrant.FilterCheck` - The policy check this mirrors
- `AshGrant.Evaluator` - Permission evaluation logic

# `has_calculate?`

# `has_expression?`

# `load`

# `strict_loads?`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
