Packages
oban
2.16.2
2.23.0
2.22.1
2.22.0
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.4
2.19.3
2.19.2
2.19.1
2.19.0
2.18.3
2.18.2
2.18.1
2.18.0
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.3
2.16.2
2.16.1
2.16.0
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.2
2.14.1
2.14.0
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.3
2.11.2
2.11.1
2.11.0
2.10.1
2.10.0
retired
2.9.2
2.9.1
2.9.0
2.8.0
2.7.2
2.7.1
2.7.0
2.6.1
2.6.0
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.2
2.3.1
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
1.2.0
1.1.0
1.0.0
1.0.0-rc.2
1.0.0-rc.1
0.12.1
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.1
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Robust job processing, backed by modern PostgreSQL, SQLite3, and MySQL.
Current section
Files
Jump to
Current section
Files
lib/oban/validation.ex
defmodule Oban.Validation do
@moduledoc false
@type validator ::
({atom(), term()} ->
:ok
| {:unknown, atom() | {atom(), term()}, module()}
| {:error, term()})
@doc """
A utility to help validate options without resorting to `throw` or `raise` for control flow.
## Example
Ensure all keys are known and the correct type:
iex> Oban.Validation.validate(name: Oban, fn
...> {:conf, conf} when is_struct(conf) -> :ok
...> {:name, name} when is_atom(name) -> :ok
...> opt -> {:error, "unknown option: " <> inspect(opt)}
...> end)
:ok
"""
@spec validate(atom(), Keyword.t(), validator()) :: :ok | {:error, String.t()}
def validate(parent_key \\ nil, opts, validator)
def validate(_parent_key, opts, validator) when is_list(opts) and is_function(validator, 1) do
Enum.reduce_while(opts, :ok, fn opt, acc ->
case validator.(opt) do
:ok -> {:cont, acc}
{:error, _reason} = error -> {:halt, error}
{:unknown, field, module} -> {:halt, unknown_error(field, module)}
end
end)
end
def validate(parent_key, opts, _validator) do
{:error, "expected #{inspect(parent_key)} to be a list, got: #{inspect(opts)}"}
end
@doc """
Similar to `validate/2`, but it will raise an `ArgumentError` for any errors.
"""
@spec validate!(opts :: Keyword.t(), validator()) :: :ok
def validate!(opts, validator) do
with {:error, reason} <- validator.(opts), do: raise(ArgumentError, reason)
end
defp unknown_error({name, _value}, known), do: unknown_error(name, known)
defp unknown_error(name, module) when is_atom(module) do
name = to_string(name)
module
|> struct([])
|> Map.from_struct()
|> Enum.map(fn {known, _} -> {String.jaro_distance(name, to_string(known)), known} end)
|> Enum.sort(:desc)
|> case do
[{score, known} | _] when score > 0.7 ->
{:error, "unknown option :#{name}, did you mean :#{known}?"}
_ ->
{:error, "unknown option :#{name}"}
end
end
# Shared Validations
@doc false
def validate_integer(key, value, opts \\ []) do
min = Keyword.get(opts, :min, 1)
if is_integer(value) and value > min - 1 do
:ok
else
{:error, "expected #{inspect(key)} to be a positive integer, got: #{inspect(value)}"}
end
end
@doc false
def validate_timezone(key, value) do
if is_binary(value) and match?({:ok, _}, DateTime.now(value)) do
:ok
else
{:error, "expected #{inspect(key)} to be a known timezone, got: #{inspect(value)}"}
end
end
@doc false
def validate_timeout(key, value) do
if (is_integer(value) and value > 0) or value == :infinity do
:ok
else
{:error,
"expected #{inspect(key)} to be a positive integer or :infinity, got: #{inspect(value)}"}
end
end
end