docs: add some more docs on how error classes work

This commit is contained in:
Zach Daniel 2024-03-14 21:05:43 -04:00
parent 0d49772317
commit ff64778e68

View file

@ -100,6 +100,81 @@ def do_multiple_things(argument) do
end end
``` ```
## Error classes
When we combine errors into error classes, we choose the first error class for which there are any errors as the "class" of the combined error. For example, in Ash Framework, we have:
```elixir
use Splode,
error_classes: [
forbidden: Ash.Error.Forbidden,
invalid: Ash.Error.Invalid,
framework: Ash.Error.Framework,
unknown: Ash.Error.Unknown
],
unknown_error: Ash.Error.Unknown.UnknownError
```
What this means is that if there are any `Forbidden` errors, then the class is `Forbidden`. A `Forbidden` error _can_ contain any of the lower classed errors. This allows people to match on and/or rescue on "the general type of failure" that occurred. Given that you have many varied kinds of errors, you can use this to your advantage to have both detailed errors, but simple to match on errors. Here is an example:
```elixir
def get(conn, %{"user_id" => user_id}) do
user = MyApp.Accounts.get_user!()
render_user(conn, user)
rescue
e in Ash.Error.Forbidden ->
render_error(conn, %{error: "You can't do this"})
e in Ash.Error.Invalid ->
render_error(conn, %{error: "You did something wrong"})
e in [Ash.Error.Framework, Ash.Error.Unknown] ->
render_error(conn, %{error: "Something went wrong"})
end
```
Or, alternatively, you can pattern match on them given a non-raised error class
```elixir
def get(conn, %{"user_id" => user_id}) do
case MyApp.Accounts.get_user() do
{:ok, user} ->
render_user(conn, user)
{:error, %Ash.Error.Forbidden{}} ->
render_error(conn, %{error: "You can't do this"})
{:error, %Ash.Error.Invalid{}} ->
render_error(conn, %{error: "You did something wrong"})
{:error, %error{}} when error in [Ash.Error.Framework, Ash.Error.Unknown] ->
render_error(conn, %{error: "Something went wrong"})
end
end
```
# Raising Exceptions
To make a `!` version of a function that returns one of these errors is quite simple:
```elixir
def get_user!(user_id) do
with {:ok, user} <- get_user(user_id) do
{:ok, user}
else
{:error, error} -> raise MyApp.Errors.to_class(error)
end
end
def get_user(user_id) do
case Repo.get(user_id) do
nil ->
{:error, MyApp.Error.NotFound.exception(resource: User, key: user_id)}
user ->
{:ok, user}
end
end
```
## Installation ## Installation
```elixir ```elixir