使用CQRS模式(通过Commanded)在Web应用程序上工作,并且希望在单个模块中公开来自Read
和Write
模块的功能。例如,从Phoenix控制器中隐藏上下文的实现细节。
我知道,可以将上下文模块(例如Accounts
)简单地分为两部分。
答案 0 :(得分:1)
之前发布的答案过于复杂。我们有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]
答案 1 :(得分:0)
主要功劳归于@velimir for his 5 year-old-answer,但最后的资源也非常宝贵。与@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
谢谢大家!