How should module calculations return errors?

Since the name of the function calculate/3 doesn’t end with ! and returns a raw value instead of an ok/error tuple, I’m just checking whether it’s OK for my custom calculation (defined in its own module) to raise an error which can be caused by, say, an attribute having a value of an unexpected type or an invalid string format (this would be caused by a bug in the app — I’m not trying to code a validation in a calculation).
3 Replies
ZachDaniel
ZachDaniel2y ago
no, you should return an {:error, ...} from the calculate function you can rescue your own exception if you're trying to clean it up though
def calculate(....) do
{:ok, Enum.each(...)}
rescue
e ->
{:error, e}
end
def calculate(....) do
{:ok, Enum.each(...)}
rescue
e ->
{:error, e}
end
Terris
TerrisOP2y ago
Oh, so calculations can return a raw value or a tuple.. did I not read the docs closely enough ? Is this the right way? Whatever calculate/3 returns seems to be used literally. I'd rather not end up with {:error, errormessage} as the value of a calculated field. ``` defmodule Example2.Calculations.ID do @moduledoc false use Ash.Calculation @impl true def calculate(records, , _) do Enum.map(records, fn record -> case Example2.UUID.fromid(record.id, record.struct) do {:ok, string} -> string # If I don't do this, the calculated field contains {:ok, string} -> {:error, 'Failed'} end end) end end
ZachDaniel
ZachDaniel2y ago
that is incorrect the {:ok, _} | {:error, _} needs to wrap the entire result Here is a template for a "failable map"
defmodule Example2.Calculations.ID do
@moduledoc false
use Ash.Calculation

@impl true
def calculate(records, , _) do
records
|> Enum.reduce_while({:ok, []}, fn record, {:ok, results} ->
case Example2.UUID.fromid(record.id, record.struct) do
{:ok, string} -> {:cont, {:ok, [string | results]}}
{:error, error} -> {:halt, {:error, error}}
end
end)
|> case do
{:ok, strings} -> {:ok, Enum.reverse(strings)}
{:error, error} -> {:error, error}
end
end
end
defmodule Example2.Calculations.ID do
@moduledoc false
use Ash.Calculation

@impl true
def calculate(records, , _) do
records
|> Enum.reduce_while({:ok, []}, fn record, {:ok, results} ->
case Example2.UUID.fromid(record.id, record.struct) do
{:ok, string} -> {:cont, {:ok, [string | results]}}
{:error, error} -> {:halt, {:error, error}}
end
end)
|> case do
{:ok, strings} -> {:ok, Enum.reverse(strings)}
{:error, error} -> {:error, error}
end
end
end

Did you find this page helpful?