大家好,几天没跟新了,在上一篇,我们简单介绍了mochiweb这个项目,以及下载,编译,创建mochiweb的源码以及示例。今天继续跟大家来分析mochiweb这个项目,跟之前分析cowboy的方法一样,我们找到切入口点,来一一分析每个函数的功能,作用,下面看下上一篇创建的示例都有哪些文件,如下图:

这里除了keepalive.erl 是我从官方example考过来以外,其他为上一篇创建的例子的源码。
我不一一介绍这几个文件了,参考我一直给大家推荐的Erlang OTP 设计原理。好好看几遍,你就对Erlang应用程序有一定的了解了。
我们从运行程序的 mochiweb_example:start/0 开始看吧,代码如下:
%% @spec start() -> ok
%% @doc Start the mochiweb_example server.
start() ->
mochiweb_example_deps:ensure(),
ensure_started(crypto),
application:start(mochiweb_example).
这个函数三行代码,第一行先跳过,一会我们重点看下。
第二行启动crypto,mochiweb_example:ensure_started/1,代码如下:
ensure_started(App) ->
case application:start(App) of
ok ->
ok;
{error, {already_started, App}} ->
ok
end.
很简单,判断下该应用程序是否启动,如果没启动就启动,如果已经启动了,就提示 {error, {already_started, App}} 错误。
第三行,启动mochiweb_example,很简单,其实这里也可以修改成这样:ensure_started(mochiweb_example),因为上面那个函数本来也就是启动应用程序用的。不知道作者为什么生成代码,不用上面的函数,不过关系不大。
现在可以重点看下:mochiweb_example_deps:ensure/0 这个函数了,代码如下:
%% @spec ensure(Module) -> ok
%% @doc Ensure that all ebin and include paths for dependencies
%% of the application for Module are on the code path.
ensure(Module) ->
code:add_paths(new_siblings(Module)),
code:clash(),
ok.
%% @spec ensure() -> ok
%% @doc Ensure that the ebin and include paths for dependencies of
%% this application are on the code path. Equivalent to
%% ensure(?Module).
ensure() ->
ensure(?MODULE).
从上面代码可以知道 mochiweb_example_deps:ensure/0 调用 mochiweb_example_deps:ensure/1 函数,并传递参数 ?MODULE,而mochiweb_example_deps:ensure/1 函数,就三行代码也很简单,我们先看下 mochiweb_example_deps:new_siblings/1 函数:
%% @spec new_siblings(Module) -> [Dir]
%% @doc Find new siblings paths relative to Module that aren't already on the
%% code path.
new_siblings(Module) ->
Existing = deps_on_path(),
SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)),
Siblings = [filename:dirname(X) || X <- SiblingEbin,
ordsets:is_element(
filename:basename(filename:dirname(X)),
Existing) =:= false],
lists:filter(fun filelib:is_dir/1,
lists:append([[filename:join([X, "ebin"]),
filename:join([X, "include"])] ||
X <- Siblings])).
从这个函数的注释,我们能大概知道,查找相对于Module模块不在代码路径下的新的路径。看下具体逻辑,首先是 mochiweb_example_deps:deps_on_path/0 函数,代码如下:
%% @spec deps_on_path() -> [ProjNameAndVers]
%% @doc List of project dependencies on the path.
deps_on_path() ->
F = fun (X, Acc) ->
ProjDir = filename:dirname(X),
case {filename:basename(X),
filename:basename(filename:dirname(ProjDir))} of
{"ebin", "deps"} ->
[filename:basename(ProjDir) | Acc];
_ ->
Acc
end
end,
ordsets:from_list(lists:foldl(F, [], code:get_path())).
这个函数,有不少的系统函数,我们先看下这几个函数:
函数:filename:dirname/1,返回当前文件名所在的目录,大家可以查下erlang doc,地址:http://www.erlang.org/doc/man/filename.html#dirname-1 我截了个图,方便大家看吧,如下图:

函数filename:basename/1,大概意思是:如果它不包含任何目录分隔符,返回最后一部分的文件名或文件名本身。同样查下erlang doc,地址:http://www.erlang.org/doc/man/filename.html#basename-1,如下图:

函数:code:get_path/0,返回当前代码路径。 erlang doc 地址:http://www.erlang.org/doc/man/code.html#get_path-0,如下图:

我在shell上尝试打印出完整的列表(使用rp命令),一共是58项,如下图:


函数:lists:foldl/3,对List中的每一项执行函数Fun,Acc0作为该函数第二个参数。 erlang doc 地址:http://www.erlang.org/doc/man/lists.html#foldl-3,如下图:

我们做个练习如下:

函数: ordsets:from_list/1,对List排序,erlang doc 地址:http://www.erlang.org/doc/man/ordsets.html#from_list-1,如下图:

示例如下:

理解完这几个系统函数,我们来整理下 mochiweb_example_deps:deps_on_path/0 这个函数的作用。首先是定义了匿名函数F,接受2个参数,X为调用 code:get_path/0 返回的列表的每一项项,Acc用来保存对每一项处理的结果。而处理每一项的逻辑也很简单,这里我们用Debugger跟踪下其中一项的处理过程,如下图:
图一:

图二:

图三:

根据上面三个图,我们就能更加清楚这个函数的作用了。
好了,这一篇就到这里,这些系统函数需要多动手练习还会熟悉。希望大家也多多动手,下一篇,我们继续从mochiweb_example_deps:new_siblings/1 函数往下看。
谢谢大家的收看。
来源:https://www.cnblogs.com/yourihua/archive/2012/07/14/2588893.html