Packages
spark
1.1.52
2.7.2
2.7.1
2.7.0
2.6.1
2.6.0
2.5.0
2.4.1
2.4.0
2.3.14
2.3.13
2.3.12
2.3.11
2.3.10
2.3.9
2.3.8
2.3.7
2.3.6
2.3.5
2.3.4
2.3.3
2.3.2
2.3.1
2.3.0
2.2.69
2.2.68
2.2.67
2.2.66
2.2.65
2.2.64
2.2.63
2.2.62
2.2.61
2.2.60
2.2.59
2.2.58
2.2.57
2.2.56
2.2.55
2.2.54
2.2.53
2.2.52
2.2.51
2.2.50
2.2.49
2.2.48
2.2.47
2.2.46
2.2.45
2.2.44
2.2.43
2.2.42
2.2.41
2.2.40
2.2.39
2.2.38
2.2.37
2.2.36
2.2.35
2.2.34
2.2.33
2.2.32
2.2.31
2.2.30
2.2.29
2.2.28
2.2.27
2.2.26
2.2.25
2.2.24
2.2.23
2.2.22
2.2.21
2.2.20
2.2.19
2.2.18
2.2.17
2.2.16
2.2.15
2.2.14
2.2.13
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.24
2.1.23
2.1.22
2.1.21
2.1.20
2.1.19
2.1.18
2.1.17
2.1.16
2.1.15
2.1.14
2.1.13
2.1.12
2.1.11
2.1.10
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.1
2.0.0
1.1.55
1.1.54
1.1.53
1.1.52
1.1.51
1.1.50
1.1.49
1.1.48
1.1.47
1.1.46
1.1.45
1.1.44
1.1.43
1.1.42
1.1.41
1.1.40
1.1.39
1.1.38
1.1.37
1.1.36
1.1.35
1.1.34
1.1.32
1.1.31
1.1.30
1.1.29
1.1.28
1.1.27
1.1.26
1.1.25
1.1.24
1.1.22
1.1.21
1.1.20
1.1.19
1.1.18
1.1.17
1.1.16
1.1.15
1.1.14
retired
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.29
0.1.28
0.1.27
0.1.26
0.1.25
0.1.24
0.1.23
0.1.22
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Generic tooling for building DSLs
Current section
Files
Jump to
Current section
Files
lib/spark/dsl/builder.ex
defmodule Spark.Dsl.Builder do
@moduledoc """
Utilities for building DSL objects programatically, generally used in transformers.
"""
defmacro __using__(_) do
quote do
import Spark.Dsl.Builder
end
end
@type result :: {:ok, Spark.Dsl.t()} | {:error, term()}
@type input :: {:ok, Spark.Dsl.t()} | {:error, term()} | Spark.Dsl.t()
defmacro defbuilder({func, _, [dsl_state | rest_args]}, do: body) do
def_head? = Enum.any?(rest_args, &match?({:\\, _, _}, &1))
rest_args_with_defaults = rest_args
rest_args =
Enum.map(rest_args, fn
{:\\, _, [expr, _default]} ->
expr
other ->
other
end)
quote generated: true,
location: :keep,
bind_quoted: [
def_head?: def_head?,
rest_args: Macro.escape(rest_args),
rest_args_with_defaults: Macro.escape(rest_args_with_defaults),
dsl_state: Macro.escape(dsl_state),
func: Macro.escape(func),
body: Macro.escape(body)
] do
if def_head? do
def unquote(func)(unquote(dsl_state), unquote_splicing(rest_args_with_defaults))
end
def unquote(func)({:ok, unquote(dsl_state)}, unquote_splicing(rest_args)) do
case unquote(body) do
{:ok, result} ->
{:ok, result}
{:error, error} ->
{:error, error}
body ->
{:ok, body}
end
end
def unquote(func)(
{:error, error},
unquote_splicing(
Enum.map(rest_args, fn _ ->
{:_, [], Elixir}
end)
)
) do
{:error, error}
end
def unquote(func)(unquote(dsl_state), unquote_splicing(rest_args)) do
case unquote(body) do
{:ok, result} ->
{:ok, result}
{:error, error} ->
{:error, error}
body ->
{:ok, body}
end
end
end
end
defmacro defbuilderp({func, _, [dsl_state | rest_args]}, do: body) do
def_head? = Enum.any?(rest_args, &match?({:\\, _, _}, &1))
rest_args_with_defaults = rest_args
rest_args =
Enum.map(rest_args, fn
{:\\, _, [expr, _default]} ->
expr
other ->
other
end)
quote generated: true,
location: :keep,
bind_quoted: [
def_head?: def_head?,
rest_args: Macro.escape(rest_args),
rest_args_with_defaults: Macro.escape(rest_args_with_defaults),
dsl_state: Macro.escape(dsl_state),
func: Macro.escape(func),
body: Macro.escape(body)
] do
if def_head? do
defp unquote(func)(unquote(dsl_state), unquote_splicing(rest_args_with_defaults))
end
defp unquote(func)({:ok, unquote(dsl_state)}, unquote_splicing(rest_args)) do
case unquote(body) do
{:ok, result} ->
{:ok, result}
{:error, error} ->
{:error, error}
body ->
{:ok, body}
end
end
defp unquote(func)(
{:error, error},
unquote_splicing(
Enum.map(rest_args, fn _ ->
{:_, [], Elixir}
end)
)
) do
{:error, error}
end
defp unquote(func)(unquote(dsl_state), unquote_splicing(rest_args)) do
case unquote(body) do
{:ok, result} ->
{:ok, result}
{:error, error} ->
{:error, error}
body ->
{:ok, body}
end
end
end
end
@doc """
Handles nested values that may be `{:ok, result}` or `{:error, term}`, returning any errors and unwrapping any ok values
This allows users of builders to do things like:
```elixir
dsl_state
|> Ash.Resource.Builder.add_new_action(:update, :publish,
changes: [
Ash.Resource.Builder.build_action_change(
Ash.Resource.Change.Builtins.set_attribute(:state, :published)
)
]
)
```
If your builder function calls `handle_nested_builders/2` with their input before building the thing its building.
"""
def handle_nested_builders(opts, nested) do
Enum.reduce_while(nested, {:ok, opts}, fn nested, {:ok, opts} ->
case Keyword.get(opts, nested) do
nil ->
{:cont, {:ok, opts}}
values when is_list(values) ->
Enum.reduce_while(values, {:ok, []}, fn
{:ok, value}, {:ok, values} ->
{:cont, {:ok, [value | values]}}
{:error, error}, _ ->
{:halt, {:error, error}}
value, {:ok, values} ->
{:cont, {:ok, [value | values]}}
end)
|> case do
{:ok, values} -> {:cont, {:ok, Keyword.put(opts, nested, Enum.reverse(values))}}
other -> other
end
{:ok, value} ->
{:cont, {:ok, Keyword.put(opts, nested, value)}}
{:error, error} ->
{:halt, {:error, error}}
_value ->
{:cont, {:ok, opts}}
end
end)
end
end