代码之家  ›  专栏  ›  技术社区  ›  toraritte

如何制作包含来自其他模块的函数的包装器模块?

  •  0
  • toraritte  · 技术社区  · 5 年前

    使用CQRS模式开发web应用程序(通过 Commanded ),并希望从中公开函数 Read Write


    Accounts )分为两部分。

    2 回复  |  直到 5 年前
        1
  •  1
  •   Aleksei Matiushkin    5 年前

    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]
    
        2
  •  0
  •   toraritte    5 年前

    主要归功于 @velimir for his 5 year-old-answer ,但最后的资源也是无价的。与@velimir的解决方案的不同之处在于,传递的Elixir模块需要 Macro.expand/2 -ed(因为它们不是简单的原子),并且它可以处理多个参数 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()
    

    这个 包装纸

    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
    

    资源