Как сделать модуль-обертку, включающий функции из других модулей? - PullRequest
0 голосов
/ 01 февраля 2019

Работает над веб-приложением, используя шаблон CQRS (через Comaged ), и хотел бы представить функции из модулей Read и Write в одном модуле.Например, чтобы скрыть детали реализации контекста от контроллеров Phoenix.


Я знаю, мог бы просто разделить модуль контекста (например, Accounts) на две части.

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Ответ, опубликованный ранее, чрезвычайно сложен.Для этой цели у нас есть Kernel.defdelegate/2.Кроме того, все еще неясно, как обрабатывать модули с функциями с одинаковыми именами .

defmodule M1, do: def f1, do: 42
defmodule M2, do: def f2(_a1, _a2), do: 42

defmodule Wrapper do
  defmacro __using__(modules) do
    user_defs =
      modules
      |> Enum.map(&Macro.expand(&1, __ENV__))
      |> Enum.map(&{&1, &1.module_info(:exports)})

    for {module, exports} <- user_defs do
      for {func, arity} <- exports, func not in ~w|module_info __info__|a do
        args = for i <- 0..arity, i > 0,
          do: Macro.var(:"arg#{i}", __MODULE__)

        quote do
          # Use as: unquote("#{func}_#{module}") to resolve dups
          defdelegate unquote(func)(unquote_splicing(args)),
            to: unquote(module), as: unquote(func)
        end
      end
    end
  end
end

defmodule Test, do: use Wrapper, [M1,M2]
0 голосов
/ 01 февраля 2019

Основная заслуга @ Велимира за его 5-летний ответ , но ресурсы в конце также были неоценимы.Отличия от решения @ velimir заключаются в том, что переданные модули Elixir должны быть Macro.expand/2 -едными (поскольку они не являются простыми атомами), и что он может обрабатывать несколько аргументов 1 .

[1] По какой-то причине, когда я использовал Enum.each/2 вместо понимания списка ниже для блока quote, это не сработало бы.

Wrapper повторяет всеэкспортирует предоставленные модули и создает для каждого функцию, которая вызывает функцию с тем же именем соответствующего модуля.

Например, use Wrapper, [A,B] создаст следующие функции (где A имеетlofa/0 и B имеет miez/0):

def lofa, do: A.lofa()
def miez, do: B.miez()

Модуль Wrapper:

defmodule Wrapper do

  defmacro __using__(modules) do

    user_defs =
      Enum.reduce(modules, [], fn(mod_ast, acc) ->
        exports =
          mod_ast
          |> Macro.expand(__ENV__)
          |> apply(:module_info, [:exports])

        pre_defs = [module_info: 0, module_info: 1, __info__: 1]

        [ {mod_ast, exports -- pre_defs} | acc]
      end)

    for {module, exports} <- user_defs do
      for {func_name, arity} <- exports do
        args = make_args(arity)
        quote do
          def unquote(func_name)(unquote_splicing(args)) do
            unquote(module).unquote(func_name)(unquote_splicing(args))
          end
        end
      end
    end
  end

  defp make_args(0), do: []
  defp make_args(arity) do
    Enum.map 1..arity, &(Macro.var :"arg#{&1}", __MODULE__)
  end
end

Ресурсы

Спасибо всем!

...