我发现Kernel.apply/3它允许通过将方法指定为原子来动态调用模块中的公共方法,例如result = apply(__MODULE__, :my_method, [arg])转换为result = my_method(arg)
困扰我的是一种称私人方法的方法; 给出这样的代码:
defmodule MyModule do
def do_priv(atom, args) when is_list(args) do
apply(__MODULE__, atom, args)
end
# (change defp => def, and this all works)
defp something_private(arg), do: arg #or whatever
end
Run Code Online (Sandbox Code Playgroud)
我希望这MyModule.do_priv(:something_private, [1])是允许的,因为它是从模块内部调用私有方法.我可以理解,引擎盖Elixir正在使用Erlang的apply/3,所以这种方法可能不会让我们在那里.
我也试过使用这个Code.eval_quoted/3方法,但它似乎甚至没有能够调用硬编码的私有方法(因此没有时间花在手上构建AST,而不是quote do像下面那样使用 - 尽管如果有人看到这是一个选项如何使这项工作):
defmodule MyModule do
def do_priv_static do
something_private(1) #this works just fine
end
def do_priv_dynamic do
code = quote do
something_private(1)
end
Code.eval_quoted(code, [], __ENV__) #nope. …Run Code Online (Sandbox Code Playgroud) 我之前曾使用 AOP 风格的代码将逻辑与日志记录分开,并且对结果非常满意。我认识到对 AOP 的看法各不相同,但我想在 Elixir 中找到一个解决方案,即使我最终没有在产品中使用它。
我见过的最接近的例子是 ExUnit 内部的设置回调,它允许在每个测试运行之前执行代码;我想做类似的事情,但无法通过 ExUnit 源代码来掌握那里的直觉。
以代码形式:
defmodule Project.Logic do
LoggingInjection.inject Project.Logging
def work_do_stuff(arg) do
#...
#returns some_result
end
end
Run Code Online (Sandbox Code Playgroud)
在单独的代码文件中:
defmodule Project.Logging do
#called just before Project.Logic.work_do_stuff with the same args
def before_work_do_stuff(arg) do
Log.write("about to work_do_stuff with #{inspect arg}")
end
# def after_work_do_stuff(some_result) implicitly defined as no-op,
# but could be overridden.
end
Run Code Online (Sandbox Code Playgroud)
最后,真正的问题是:启用这个魔法的代码是什么?
defmodule LoggingInjection do
defmacro inject(logging_module) do
#What goes here?
end
end
Run Code Online (Sandbox Code Playgroud)