大家好,几天没跟新了,在上一篇,我们简单介绍了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 函数往下看。
谢谢大家的收看。