我有一个 Elixir 模块,可以导入一些函数。我想用我自己的函数来猴子修补其中一个函数。那可能吗?我该怎么做?
更新示例
我的具体示例是,对于任务 Mix.Tasks.Deps.Compile,我想向“编译”功能添加一些功能。
https://github.com/elixir-lang/elixir/blob/master/lib/mix/lib/mix/tasks/deps.compile.ex
Mix.Tasks.Deps.Compile 模块相当深入地融入到 Mix 框架中。我想做最小的改变,同时添加我想要的额外功能。我想要添加的功能是 cond do 块的另一个条件。
简单的答案是:你不能。 BEAM 上没有猴子修补的概念。
更长的答案是 BEAM 中的模块是延迟加载的,因此您可以用您自己的实现替换模块(但是整个模块,而不仅仅是单个函数)。但我真的不确定这是否是正确的方法。
您可以通过反编译要进行猴子修补的模块,更改 Erlang 抽象代码,然后重新加载该模块来完成此操作。找到要更新的特定条件分支会有点困难,但并非不可能。我使用这种方法将私有函数与另一个函数包装起来,以便操纵私有函数的返回值。
{_, beam_, } = :code.get_object_code(Mix.Tasks.Deps.Compile)
{:ok, {_, [{:abstract_code, {:raw_abstract_v1, code}}]}} = :beam_lib.chunks(beam), ~w/abstract_code/a)
# Do the manipulation to the abstract code here
# Then recompile and load the binary code
{:ok, modname, binary} = :compile.forms(code)
:code.load_binary(modname, [], binary)
我不会对这是对还是错做出任何判断,这可能只是达到目的的一种手段。以下库使用此方法: Patch、mecks 以及可能更多的模拟库。