# `Permit.Phoenix.Actions`
[🔗](https://github.com/curiosum-dev/permit_phoenix/blob/v0.5.1/lib/permit_phoenix/actions.ex#L1)

Defines action names for your permissions schema, so that convenience functions
are generated for them.

## Recommended usage

```
defmodule MyApp.Actions do
  # Merge the actions from the router into the default grouping schema
  use Permit.Phoenix.Actions, router: MyApp.Router

  # Additional singular actions throughout the app
  def singular_actions, do: [:view]
end
```

## Overview

Default action grouping for Phoenix includes:
- `:create`, implying permission to `:new` and `:create` actions
- `:read`, implying permission to `:index` and `:show` actions
- `:update`, implying permission to `:edit` and `:update` actions
- `:delete`, implying permission to `:delete` action

By granting a permission to `:read`, you also allow `:index` and `:show`. You can
also grant permissions to individual actions specifically - if you only grant
`:index`, `:show` will not be authorized.

This is coded as:
```
%{
  create: [],
  read: [],
  update: [],
  delete: [],
  new: [:create],
  index: [:read],
  show: [:read],
  edit: [:update]
}
```

If you want to declare an action that requires more than one permission, you can
define a grouping:
```
%{
  access: [],
  view: [],
  open: [:accsess, :view] # if both are granted, :open will be authorized
}
```

## Singular vs plural actions

Singular actions are those that operate on a single resource, such as `:show` or `:update`.
Plural actions operate on a collection of resources, such as `:index`. Depending on the arity,
queries generated by Permit.Ecto will be executed as `LIMIT 1` (for singular actions) or with
any limit given in the `base_query` (for plural actions).

Each action in Permit is assumed to be plural by default. To declare an action as singular,
you can implement the `singular_actions/0` callback. This can be implemented in your actions module
and overridden in your controller or LiveView module.

Default singular actions are `:show`, `:edit`, `:new`, `:delete`, `:update` **and** those
inferred from the router - see section below. The `singular_actions/0` callback can be used to add more
- it does not require calling `super`.

## Example

    defmodule MyApp.Actions do
      use Permit.Phoenix.Actions

      # In addition to the default singular actions. If `:view` always deals with a single record
      # throughout the app, it can be declared as singular in this module.

      @impl true
      def singular_actions, do: [:view]
    end

Actions can also be configured as singular or plural in the controller or LiveView module
itself, which takes precedence over the actions module.

If a controller or LiveView contains a `:view` action that deals with an index of records,
it can be overridden as plural in the controller or LiveView module. `super()` will contain
the actions module's configuration.

```
defmodule MyApp.ArticleController do
  use Permit.Phoenix.Controller

  @impl true
  def singular_actions, do: super() -- [:view]
end
```

## Router

It is recommended to read action names from the router, so that all controller
action and `:live_action` names are automatically included and convenience functions
for them are generated. Moreover, actions are automatically inferred to be singular or plural based on the route definition.
An action is singular by default if:
- it's one of: `:show`, `:edit`, `:new`, `:delete`, `:update`, `:create`, or
- it is a POST request, or
- it's a route with an `:id`, `:uuid` or `:slug` parameter, e.g. `/items/:id/view` or `/items/:uuid/view`, or
- the route's last segment is a parameter, e.g. `/items/:name`, `/items/:identifier`.

The last rule does not apply to actions that are plural by convention. By
default this covers `:index`, so a route like `/datethings/:year/:month`
pointing at `:index` stays plural. Additional plural actions can be declared
through the `c:plural_actions/0` callback (see below), e.g. for custom
collection actions like `:list`, `:search` or `:feed`.

```
defmodule MyApp.Router do
  # ...

  get("/items/:id", MyApp.ItemController, :view)
end

defmodule MyApp.Actions do
  # Merge the actions from the router into the default grouping schema.
  use Permit.Phoenix.Actions, router: MyApp.Router

  # This doesn't need to be used - Permit automatically infers that the :view action is
  # singular.
  # You can use it if an explicit declaration is needed, e.g. if you use a different ID
  # parameter than `:id`, `:uuid` or `:slug`.
  @impl true
  def singular_actions, do: [:view]
end

defmodule MyApp.Permissions do
  # Use the actions module to define permissions.
  use Permit.Permissions, actions_module: MyApp.Actions

  def can(%User{role: :admin} = _user) do
    permit()
    |> all(Item)
  end

  # The `view` action is automatically added to the grouping schema
  # and hence available as a `view/2`function when defining permissions.
  def can(%User{role: :owner} = _user) do
    permit()
    |> view(Item)
    |> all(Item, fn user, item -> item.owner_id == user.id end)
  end
end
```

# `action_names_from_router`

# `crud_grouping`

Convenience function defining the basic CRUD (create, read, update, delete) actions.

# `crud_singular`

Convenience function returning actions that are singular in the most basic CRUD setup, in which case
all of: `:create`, `:read`, `:update` and `:delete` are singular.

# `filtered_routes_stream`

# `grouping_schema`

Returns the default action grouping schema for Phoenix applications.

# `merge_from_router`

# `paths_from_router`

# `singular_actions`

Returns the list of actions that operate on a single resource.

Actions listed as plural (either `@default_plural_actions` currently `[:index]`
or the user supplied `extra_plural`) are excluded from router based
promotion to singular even if their route would otherwise satisfy the
heuristic (e.g. `/datethings/:year/:month` for an `:index` action).

---

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