我正在为webmachine编写几个共享许多相同功能的资源。所以我想知道是否可以在一个单独的模块中编写公共函数,然后以某种方式包含它,并让它的导出由资源自动导出(无需显式地从每个资源中导出它们)。
答案 0 :(得分:3)
我们有一个包装器模块,它实现所有Webgear回调并将它们传输到真实的实现模块(如果它们在那里实现)。该模块可以对某些部分进行特殊实现,甚至可以在使用它的模块中启用新的回调。基本上该模块是所有其他资源的包装资源。
首先,您的调度图如下所示:
[{"/some/path", webgear_wrapper, {actual_resource, ["Some", extra, "Args"]}}].
为此,您需要了解实际实现模块实现的回调:
-record(context, {module, context, exports}).
init({Mod, Args}) ->
{ok, Context} = Mod:init(Args),
{ok, #context{module = Mod, context = Context, exports = exports(Mod)}}.
exports(Mod) ->
dict:from_list(Mod:module_info(exports)).
这将初始化一个基本的Webgear资源,该资源包含有关其状态中真实回调模块的信息。
然后,对于包装器资源的每次回调(如果你希望你的实现模块能够使用,那么你必须实现它),你会看到该函数是否已经实现并在那里处理它,使用此功能:
call(#context{module = Mod, context = Cxt, exports = Exports},
Func, Req, Default) ->
case dict:is_key(Func, Exports) of
true -> Mod:Func(Req, Cxt);
false -> {Default, Req, Cxt}
end.
例如,call/4
函数在包装器模块中使用如下:
malformed_request(Req, Cxt) ->
% false here is the default value to return if the callback is missing
{Res, NewReq, NewCxt} = call(Cxt, malformed_request, Req, false),
% Now we must update the state accordingly
{Res, NewReq, Cxt#context{context = NewCxt}}.
这在我们的项目中运行良好,其中所有资源共享一些共同的逻辑(在这样的包装器模块中实现)。虽然没有对性能进行基准测试,但是开销应该相当小(一个dict查找和一个额外的模块调用,加上一些记录争论)。
答案 1 :(得分:2)
实际上它是可能的,但只能通过未记录的功能。模块继承。
答案 2 :(得分:1)
是的,您可以使用混音器库,https://github.com/opscode/mixer它是一个编译器解析变换,可以完全按照您的需要进行。我在整个地方使用它来进行webmachine回调
答案 3 :(得分:0)
不,这是不可能的。我所做的是将我的应用程序所需的所有回调实现到泛型函数。另一种方法是修补webmachine以从函数中获取cb而不是通过查看导出。