
本文主要是要来谈谈在 elixir 中运用文件、模块 (module) 来组织代码; 使得 elixir 的代码能更容易的维护, 更容易的进行多人的开发与协作。
elixir; 组织代码
- elixir 的文件中可包含任意数目的模块 (module)。如下面的例子, 我们在先前的文件; handler.ex; 中再创建一个新的模块: Servy.Plugins。新的模块 Servy.Plugins 包含著: function clauses track, rewrite_path。function log。
defmodule Servy.Plugins do
@doc "Logs 404 requests"
def track(%{status: 404, path: path} = conv) do
IO.puts "Warning: #{path} is on the loose!"
conv
end
def track(conv), do: conv
def rewrite_path(%{path: "/wildlife"} = conv) do
%{ conv | path: "/wildthings" }
end
def rewrite_path(conv), do: conv
def log(conv), do: IO.inspect conv
end
- 现在, 在原模块 Servy.Handler 中, 需调用 function clauses track, rewrite_path, function log 时, 便需加上个前缀 (prefix): Servy.Plugins.。
defmodule Servy.Handler do
@moduledoc "Handles HTTP requests. "
@page_path Path.expand("../../pages", __DIR__)
@doc "Transforms the request into a response."
def handle(request) do
request
|> parse
|> Servy.Plugins.rewrite_path
|> Servy.Plugins.log
|> route
|> Servy.Plugins.track
|> format_response
end
....
- 当然, 我们可以使用 import:
defmodule Servy.Handler do
@moduledoc "Handles HTTP requests. "
@page_path Path.expand("../../pages", __DIR__)
import Servy.Plugins
@doc "Transforms the request into a response."
def handle(request) do
request
|> parse
|> rewrite_path
|> log
|> route
|> track
|> format_response
end
.......
- import…only; 让我们可以指定只 import 文件中的哪些函数? 需指定 arity (参数的数目)。
defmodule Servy.Handler do
@moduledoc "Handles HTTP requests. "
@page_path Path.expand("../../pages", __DIR__)
import Servy.Plugins, only: [rewrite_path: 1, log: 1, track: 1]
@doc "Transforms the request into a response."
def handle(request) do
request
|> parse
|> rewrite_path
|> log
|> route
|> track
|> format_response
end
.....
- 当然, 我们可以将不同的模块放到不同的文件中; 在目录 ..\servy 下新创建个文件: pligins.ex。然后将模块: Servy.Plugins 放入文件: pligins.ex。

不幸的是, 当执行 elixir…时, 却出现了错误 ?! elixir 找不到新产生的文件; pligins.ex, 所以, 也就找不到文件 pligins.ex 内的模块 Servy.Plugins。
$ elixir ./lib/servy/handler.ex
** (CompileError) lib/servy/handler.ex:7: module Servy.Plugins is not loaded and could not be found
为了使 elixir 能够找得到新产生的文件; pligins.ex, 我们需使用 elixir 的工具: mix。在项目servy 的根目录下; /Users/kenchfang/ElixirProjects/servy; 执行: iex -S mix
pwd
/Users/kenchfang/ElixirProjects/servy
KENCHFANG-MB1:servy kenchfang$ iex -S mix
Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]
这时 elixir 便会找到所有相关的文件、模块; 先找到文件 handler.ex, 模块 Servy.Handler, 再找到模块 Servy.Handler 所需的文件 plugin.ex 与模块 Servy.Plugins。
执行的结果就如我们所预期的:
Compiling 2 files (.ex)
%{method: "GET", path: "/wildthings", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Bears, Lions, Tigers
%{method: "GET", path: "/bears", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 25
Teddy, Smokey, Paddington
%{method: "GET", path: "/bears/1", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 6
Bear 1
%{method: "GET", path: "/bigfoot", resp_body: "", status: nil}
Warning: /bigfoot is on the loose!
HTTP/1.1 404 Not Found
Content-Type: text/html
Content-Length: 17
No /bigfoot here!
%{method: "GET", path: "/wildthings", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Bears, Lions, Tigers
%{method: "GET", path: "/about", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 340
<h1>Clark's Wildthings Refuge</h1>
<blockquote>
When we contemplate the whole globe as one great dewdrop,
striped and dotted with continents and islands, flying through
space with other stars all singing and shining together as one,
the whole universe appears as an infinite storm of beauty.
-- John Muir
</blockquote>
假如, 我们修改了模块 Servy.Handler 或模块 Servy.Plugins 时, 我们只需执行命令 “r“, 重新的载入模块 Servy.Handler 与模块 Servy.Plugins 。
r Servy.Handler
我们继续的创建个新的文件; parser.ex, 新的模块; Servy.Parser, 并且将原来在模块 Servy.Handler 中的函数 parse, 移到新的模块; Servy.Parser。

当我们再度的执行 r Servy.Handler 时, elixir 却报错了。
iex(5)> r Servy.Handler
warning: redefining module Servy.Handler (current version defined in memory)
lib/servy/handler.ex:2
** (CompileError) lib/servy/handler.ex:12: undefined function parse/1
(stdlib) lists.erl:1338: :lists.foreach/2
lib/servy/handler.ex:2: (file)
为何 ?
因为, 当我们执行 r Servy.Handler 时, elixir 还是如先前一样, 只是重新载入了模块 Servy.Handler 与模块 Servy.Plugins 。elixir 并没有将新的文件; parser.ex 中的模块; Servy.Parser 载入。
要解决这个问题, 我们可以离开 iex, 然后再重新的执行 iex -S mix。
但这种的作法真是太折腾了…
事实上, 我们只需在 iex 中, 执行 recompile() 就可以搞定了…
iex(5)> recompile()
Compiling 2 files (.ex)
warning: redefining module Servy.Parser (current version loaded from _build/dev/lib/servy/ebin/Elixir.Servy.Parser.beam)
lib/servy/parser.ex:1
%{method: "GET", path: "/wildthings", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Bears, Lions, Tigers
%{method: "GET", path: "/bears", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 25
Teddy, Smokey, Paddington
%{method: "GET", path: "/bears/1", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 6
Bear 1
%{method: "GET", path: "/bigfoot", resp_body: "", status: nil}
Warning: /bigfoot is on the loose!
HTTP/1.1 404 Not Found
Content-Type: text/html
Content-Length: 17
No /bigfoot here!
%{method: "GET", path: "/wildthings", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Bears, Lions, Tigers
%{method: "GET", path: "/about", resp_body: "", status: nil}
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 340
<h1>Clark's Wildthings Refuge</h1>
<blockquote>
When we contemplate the whole globe as one great dewdrop,
striped and dotted with continents and islands, flying through
space with other stars all singing and shining together as one,
the whole universe appears as an infinite storm of beauty.
-- John Muir
</blockquote>
Generated servy app
:ok
附注:
在 elixir 中创建新的项目 servy:
- 只需在根目录下: /Users/kenchfang/ElixirProjects/servy 执行 mix new。
mix new servy