我正在为共享许多相同功能的 webmachine 编写几个资源。所以我想知道是否可以在单独的模块中编写常用函数,然后以某种方式包含它,并使其导出由资源自动导出(无需显式从每个资源中导出它们)。
4 回答
我们有一个包装器模块,它实现所有 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
例如,该函数在 wrapper 模块中使用如下:
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}}.
这在我们的项目中运行良好,所有资源共享一些公共逻辑(在这样的包装模块中实现)。虽然没有对性能进行太多基准测试,但开销应该相当小(一个字典查找和一个额外的模块调用,加上一些记录争论)。
实际上这是可能的,但只能通过未记录的功能。模块继承。
是的,您可以使用混音器库,https://github.com/opscode/mixer,它是一个编译器解析转换,可以完全按照您的意愿进行操作。我到处使用它来进行网络机器回调
不,这是不可能的。我所做的是将我的应用程序所需的所有回调实现到通用函数。另一种方法是修补 webmachine 以从函数中获取 cb,而不是通过查看导出。