# `Ash.Scope`
[🔗](https://github.com/ash-project/ash/blob/v3.23.1/lib/ash/scope.ex#L5)

Determines how the `actor`, `tenant` and `context` are extracted from a data structure.

This is inspired by the same feature in `Phoenix`, however the `actor`, `tenant` and `context`
options will always remain available, as they are standardized representations of things that
actions can use to do their work.

When you have a scope, you can group up actor/tenant/context into one struct and pass that around,
for example:

```elixir
scope = %MyApp.Scope{current_user: user, current_tenant: tenant, locale: "en"}

# instead of
MyDomain.create_thing(actor: current_user, tenant: tenant)

# you can do
MyDomain.create_thing(scope: scope)
```

> ### Scope is left at the front door {: .info}
>
> Your scope is "left at the front door". That is, when you pass a scope to an action, the options
> are extracted and the scope is removed from those options. Within hooks, you are meant to use
> the `context` provided to your functions as the new `scope`. This is very important, because
> you don't want a bunch of your code or extension code having to switch on `if opts[:scope]`,
> extracting the things that it needs, etc.
>
> See [the actions guide](/documentation/topics/actions/actions.md#context) for more information.

## Setup

If you are using Phoenix, you will want to assign your `scope` module in a plug that runs
after your plugs that determine actor/tenant/context. Then, you will want to add an `on_mount`
hook for LiveViews that sets your `scope` assign. This is especially true for `AshAuthentication`,
as it does not currently have a concept of scopes.

## Passing scope and options

For the `actor`, `tenant` and `authorize?`, extracted from scopes, the values from the scope are *discarded* if also present in `opts`.

i.e `scope: scope, actor: nil` will remove the set actor. `scope: scope, actor: some_other_actor` will set the actor to `some_other_actor`.

For `context`, the values are deep merged.

For `tracer`, the value(s) are concatenated into a single list.

## Example

You would implement `Ash.Scope.ToOpts` for a module like so:

```elixir
defmodule MyApp.Scope do
  defstruct [:current_user, :current_tenant, :locale]

  defimpl Ash.Scope.ToOpts do
    def get_actor(%{current_user: current_user}), do: {:ok, current_user}
    def get_tenant(%{current_tenant: current_tenant}), do: {:ok, current_tenant}
    def get_context(%{locale: locale}), do: {:ok, %{shared: %{locale: locale}}}
    # You typically configure tracers in config files
    # so this will typically return :error
    def get_tracer(_), do: :error

    # This should likely always return :error
    # unless you want a way to bypass authorization configured in your scope
    def get_authorize?(_), do: :error
  end
end
```

For more on context, and what the `shared` key is used for, see the [actions guide](/documentation/topics/actions/actions.md#context)

You could then use this in various places by passing the `scope` option.

For example:

```elixir
scope = %MyApp.Scope{...}
# with code interfaces
MyApp.Blog.create_post!("new post", scope: scope)

# with changesets and queries
MyApp.Blog
|> Ash.Changeset.for_create(:create, %{title: "new post"}, scope: scope)
|> Ash.create!()

# with the context structs that we provide

def change(changeset, _, context) do
  Ash.Changeset.after_action(changeset, fn changeset, result ->
    MyApp.Domain.do_something_else(..., scope: context)
    # if not using as a scope, the alternative is this
    # in the future this will be deprecated
    MyApp.Domain.do_something_else(..., Ash.Context.to_opts(context))
  end)
end
```

Extensions should not use this option, only end users.

# `t`

```elixir
@type t() :: Ash.Scope.ToOpts.t()
```

# `to_opts`

---

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