# `Laev.Scope`
[🔗](https://github.com/alexdont/laev/blob/main/lib/laev/scope.ex#L1)

Whose laev this is.

Everything laev knows — your keys, your marks, your watchlist — used to be
global: one data directory, one Real-Debrid token, one named ETS table, all
read straight out of the application environment. That is exactly right for a
command somebody runs on their own machine, and exactly wrong for a server
where two people are logged in at once, where it would mean one account's
token fetching another account's film.

A scope is a user's half of that: a directory to keep their state in, and the
keys to the services they have connected. It is bound to the *process* rather
than passed through every function, because the alternative is a parameter on
several hundred signatures, and because the BEAM already gives us the
isolation for free — Phoenix runs each request and each LiveView in its own
process, so binding there is the same thing as binding per user.

Nothing is bound by default, and everything falls back to the application
environment, so the CLI goes on behaving exactly as it did: one person, one
machine, no scope at all.

The one place this leaks is work handed to another process, which does not
inherit a process dictionary. `async_stream/3`, `async/1` and `spawn/1` here
carry the scope across for you; `Task.async` and `spawn` directly do not,
which is the single rule to remember when adding work that runs elsewhere.

# `t`

```elixir
@type t() :: %Laev.Scope{config: map(), dir: String.t()}
```

# `async`

Task.async, with the scope carried across.

# `async_stream`

Task.async_stream, with this process's scope bound inside each task.

# `clear`

Unbind.

# `current`

The scope bound to this process, or nil when there isn't one.

# `dir`

Where this user's state lives.

# `get`

A setting, for whoever this process belongs to.

The scope's own value where it has one, the application environment
otherwise — which is every value when nothing is bound, and is why the CLI
does not know this module exists.

# `load`

A scope over a directory, with whatever that user has already saved in it.

How a request should build one: the settings a user changed last time are in
their own directory, and anything the server holds for them — a token kept in
a database rather than on disk, say — is passed in and wins.

# `new`

A scope over a directory, with the keys that belong to whoever owns it.

`config` holds the same names the config file uses — `:rd_token`,
`:tmdb_key`, `:torbox_api_key` and the rest — and anything absent from it
falls back to the application environment, so a server can share, say, one
TMDB key across everybody while each person brings their own debrid account.

# `put`

Bind a scope to this process for good — for a LiveView's mount.

# `run`

Run `fun` with `scope` bound, restoring whatever was bound before.

The way a web request should enter laev: bind once at the edge, and every
call underneath is that user's without being told so.

# `set`

Change a setting for whoever this process belongs to.

Within a scope it changes that scope and nothing else — one user turning on
VLC is not every user turning on VLC. Unscoped it is the application
environment, which is what makes the Settings screen apply immediately.

# `spawn`

spawn, with the scope carried across.

# `tag`

A name unique to this scope, for the things that must not be shared by name —
the ETS tables holding the watchlist and the history.

---

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