defmodule Spark.Dsl.Fragment do
@moduledoc """
Allows splitting up a DSL into multiple modules, potentially organizing large DSLs
Use the `of` option to expression what your fragment is a fragment of. You can add
extensions as you would normally to that resource, and they will be added to the
parent resource.
defmodule MyApp.Resource.Graphql do
use Spark.Dsl.Fragment, of: Ash.Resource, extensions: AshGraphql.Resource
graphql do
...
end
end
"""
defmacro __using__(opts) do
opts = Spark.Dsl.Extension.do_expand(opts, __CALLER__)
original_opts = opts
single_extension_kinds = opts[:of].single_extension_kinds()
many_extension_kinds = opts[:of].many_extension_kinds()
{opts, extensions} =
opts[:of].default_extension_kinds()
|> Enum.reduce(opts, fn {key, defaults}, opts ->
Keyword.update(opts, key, defaults, fn current_value ->
cond do
key in single_extension_kinds ->
current_value || defaults
key in many_extension_kinds || key == :extensions ->
List.wrap(current_value) ++ List.wrap(defaults)
true ->
current_value
end
end)
end)
|> Spark.Dsl.expand_modules(
[
single_extension_kinds: single_extension_kinds,
many_extension_kinds: many_extension_kinds
],
__CALLER__
)
Module.register_attribute(__CALLER__.module, :spark_extension_kinds, persist: true)
Module.register_attribute(__CALLER__.module, :spark_fragment_of, persist: true)
Module.put_attribute(__CALLER__.module, :spark_fragment_of, opts[:of])
Module.put_attribute(__CALLER__.module, :extensions, extensions)
Module.put_attribute(__CALLER__.module, :original_opts, original_opts)
Module.put_attribute(
__CALLER__.module,
:spark_extension_kinds,
List.wrap(many_extension_kinds) ++
List.wrap(single_extension_kinds)
)
quote do
require unquote(opts[:of])
unquote(Spark.Dsl.Extension.prepare(extensions))
@before_compile Spark.Dsl.Fragment
end
end
defmacro __before_compile__(_) do
quote do
Spark.Dsl.Extension.set_state([], false)
def extensions do
@extensions
end
def opts do
@original_opts
end
def spark_dsl_config do
@spark_dsl_config
end
end
end
end