1114-模块属性
22===========
3- [ 作为注释] ( #141-%E4%BD%9C%E4%B8%BA%E6%B3%A8%E9%87%8A )
4- [ 作为常量] ( #142-%E4%BD%9C%E4%B8%BA%E5%B8%B8%E9%87%8F )
5- [ 作为临时存储] ( #143-%E4%BD%9C%E4%B8%BA%E4%B8%B4%E6%97%B6%E5%AD%98%E5%82%A8 )
63
7- 在Elixir中,模块属性(attributes)主要服务于三个目的:
8- 1 . 作为一个模块的注释,通常附加上用户或虚拟机用到的信息
4+ 在Elixir中,模块属性(module attributes)主要服务于三个目的:
5+ 1 . 作为一个模块的注解(annotations),通常附加上用户或虚拟机会用到的信息
96 2 . 作为常量
10- 3 . 在编译时作为一个临时的存储机制
7+ 3 . 在编译时作为一个临时的模块存储机制
118
12- 让我们一个一个讲解。
9+ 下面让我们来一一讲解。
10+
11+ ## 作为注解(annotations)
12+
13+ Elixir从Erlang带来了模块属性的概念。如:
1314
14- ## 14.1-作为注释
15- Elixir从Erlang带来了模块属性的概念。例子:
1615``` elixir
1716defmodule MyServer do
1817 @vsn 2
1918end
20- ```
19+ ```
2120
2221这个例子中,我们显式地为该模块设置了 _ 版本(vsn即version)_ 属性。
23- 属性标识``` @vsn ``` 是预定义的属性名称,会被Erlang虚拟机的代码装载机制使用:
24- 读取并检查该模块是否在某处被更新了。
25- 如果不注明版本号,会被自动设置为这个模块函数的md5 checksum。
22+ ` @vsn ` 是一个系统保留的属性名称,它被Erlang虚拟机的代码装载机制使用,以检查该模块是否被更新过。
23+ 如果不注明版本号,该属性的值会自动设置为模块函数的md5 checksum。
2624
27- Elixir有个好多系统保留的预定义属性。比如一些常用的:
28- - @moduledoc
29- 为整个模块提供文档说明
30- - @doc
31- 为该属性后面的函数或宏提供文档说明
32- - @behaviour
33- (注意这个单词是英式拼法)用来注明一个OTP或用户自定义行为
34- - @before_compile
35- 提供一个每当模块被编译之前执行的钩子。这使得我们可以在模块被编译之前往里面注入函数。
25+ Elixir还有好多系统保留的预定义注解。下面是一些比较常用的:
3626
37- @moduledoc 和@doc 是很常用的属性,推荐经常使用(写文档)。
27+ * ` @moduledoc ` - 为当前模块提供文档说明
28+ * ` @doc ` - 为该属性标注的函数或宏提供文档说明
29+ * ` @behaviour ` - (注意这个单词是英式拼法)用来注明一个OTP或用户自定义行为
30+ * ` @before_compile ` - 提供一个每当模块被编译之前执行的钩子。这使得我们可以在模块被编译之前往里面注入函数
3831
39- Elixir视文档为一等公民,提供了很多方法来访问文档。
32+ ` @moduledoc ` 和` @doc ` 是目前最常用的注解属性,我们也希望你能够多使用它们。
33+ Elixir视文档为一等公民,而且提供了很多方法来访问这些文档。
34+ 你可以拓展阅读文章[ 《用我们官方的方式写Elixir程序文档》] ( http://elixir-lang.org/docs/stable/elixir/writing-documentation.html ) 。
35+
36+ 让我们回到上几章定义的` Math ` 模块,为它添加文档,然后依然保存在math.ex文件中:
4037
41- 让我们回到上几章定义的Math模块,为它添加文档,然后依然保存在math.ex文件中:
4238``` elixir
4339defmodule Math do
4440 @moduledoc """
@@ -58,11 +54,16 @@ defmodule Math do
5854end
5955```
6056
61- 上面例子使用了heredocs注释。heredocs是多行的文本,用三个引号包裹,保持里面内容的格式。
62- 下面例子演示在iex中,用h命令读取模块的注释:
57+ Elixir推荐使用markdown语法和多行文本(heredocs)书写容易阅读的文档。
58+ heredocs是多行的字符串,用三个双引号包裹,它会保持里面内容的格式不变。
59+ 我们可以在IEx中读取任何编译的模块的文档:
60+
6361``` elixir
6462$ elixirc math.ex
6563$ iex
64+ ```
65+
66+ ```
6667iex> h Math # Access the docs for the module Math
6768...
6869iex> h Math.sum # Access the docs for the sum function
@@ -73,25 +74,14 @@ Elixir还提供了[ExDoc工具](https://github.com/elixir-lang/ex_doc),
7374利用注释生成HTML页文档。
7475
7576你可以看看[ 模块] ( http://elixir-lang.org/docs/stable/elixir/Module.html )
76- 里面列出的模块属性列表,看看Elixir还支持那些模块属性。
77-
78- Elixir还是用这些属性来定义
79- [ typespecs] ( http://elixir-lang.org/docs/stable/elixir/Kernel.Typespec.html ) :
80- - @spec
81- 为一个函数提供specification
82- - @callback
83- 为行为回调函数提供spec
84- - @type
85- 定义一个@spec 中用到的类型
86- - @typep
87- 定义一个私有类型,用于@spec
88- - @opaque
89- 定义一个opaque类型用于@spec
90-
91- 本节讲了一些内置的属性。当然,属性可以被开发者、被一些类库扩展用来支持自定义的行为。
92-
93- ## 14.2-作为常量
94- Elixir开发者经常会将模块属性当作常量定义使用:
77+ 里面列出的完整的模块注解列表,Elixir还利用注解来定义[ typespecs] ( ../20-typespecs-behaviors.md ) 。
78+
79+ 本节讲了一些内置的注解。当然,注解可以被开发者和类库扩展使用,来支持自定义的行为。
80+
81+ ## 作为常量
82+
83+ Elixir开发者经常会将模块属性当作常量使用:
84+
9585``` elixir
9686defmodule MyServer do
9787 @initial_state %{host: " 147.0.0.1" , port: 3456 }
10090```
10191
10292> 不同于Erlang,默认情况下用户定义的属性不会被存储在模块里。属性值仅在编译时存在。
103- 开发者可以通过调用``` Module.register_attribute/3 ``` 来使属性的行为更接近Erlang 。
93+ 开发者可以通过调用` Module.register_attribute/3 ` 来使这种属性的行为更接近Erlang 。
10494
10595访问一个未定义的属性会报警告:
96+
10697``` elixir
10798defmodule MyServer do
10899 @unknown
@@ -111,6 +102,7 @@ warning: undefined module attribute @unknown, please remove access to @unknown o
111102```
112103
113104最后,属性也可以在函数中被读取:
105+
114106``` elixir
115107defmodule MyServer do
116108 @my_data 14
@@ -123,16 +115,20 @@ MyServer.first_data #=> 14
123115MyServer .second_data # => 13
124116```
125117
126- 注意,在函数内读取某属性,读取的是该属性当前值的快照。换句话说,读取的是编译时的值,而非运行时。
127- 后面我们将看到,这个特点使得属性可以作为模块在编译时的临时存储。
118+ 注意,在函数内读取某属性,读取的是该属性值的一份快照。换句话说,读取的是编译时的值,而非运行时。
119+ 后面我们将看到,这个特点使得属性可以作为模块在编译时的临时存储,十分有用。
120+
121+ ## 作为临时存储
128122
129- ## 14.3-作为临时存储
130- Elixir组织中有一个项目,叫做[ Plug] ( https://github.com/elixir-lang/plug ) 。
123+ Elixir组织中有一个项目,叫做[ Plug] ( https://github.com/elixir-lang/plug ) ,
131124这个项目的目标是创建一个通用的Web库和框架。
132125
133- > 类似于ruby的rack
126+ > 注:我想功能应该类似于ruby的rack。你可以定义各种plug,这这些plug会像链条一样,
127+ 按顺序各自对http请求进行加工处理,最后返回。这类似给rails或sinatra定义各种rack中间件,
128+ 也有些类似Java filter的概念。最终,Plug框架会组织和执行它们。
129+
130+ Plug库允许开发者定义它们自己的plug,运行在web服务器上:
134131
135- Plug库允许开发者定义它们自己的plug,可以在一个web服务器上运行:
136132``` elixir
137133defmodule MyPlug do
138134 use Plug .Builder
@@ -153,15 +149,17 @@ IO.puts "Running MyPlug with Cowboy on http://localhost:4000"
153149Plug .Adapters .Cowboy .http MyPlug , []
154150```
155151
156- 上面例子我们用了``` plug/1 ``` 宏来连接各个在处理请求时会被调用的函数。
157- 在内部,每当你调用``` plug/1 ``` 时,Plug把参数存储在@plug 属性里。
158- 在模块被编译之前,Plug执行一个回调函数,这个函数定义了处理http请求的方法。
159- 这个方法将顺序执行所有保存在@plug 属性里的plugs。
152+ 上面例子中,我们用了` plug/1 ` 宏来连接处理请求时会被调用的函数。
153+ 在代码背后,每次调用宏` plug/1 ` 时,Plug库把提供的参数(即plug的名字)存储在` @plugs ` 属性里。
154+ 就在模块被编译之前,Plug会执行一个回调函数,该回调函数定义一个函数(` call/2 ` )来处理http请求。
155+ 这个函数将按顺序执行所有保存在` @plug ` 属性里的plugs。
156+
157+ 要理解底层的代码,我们还需要了解宏,因此我们将在后期《元编程》章节中回顾这个模式。
158+ 这里的重点是怎样使用属性来存储数据,让开发者可以创建DSL(领域特定语言)。
160159
161- 为了理解底层的代码,我们需要宏。因此我们将回顾一下元编程手册里这种模式。
162- 但是这里的重点是怎样使用属性来存储数据,让开发者得以创建DSL(领域特定语言)。
160+ 另一个例子来自 [ ExUnit框架 ] ( http://elixir-lang.org/docs/stable/ex_unit/ ) ,
161+ 它使用模块属性作为注解和存储:
163162
164- 另一个例子来自ExUnit框架,它使用模块属性作为注释和存储:
165163``` elixir
166164defmodule MyTest do
167165 use ExUnit .Case
@@ -173,8 +171,9 @@ defmodule MyTest do
173171end
174172```
175173
176- ExUnit中,@tag 标签被用来注释该测试用例。之后,这些标签可以作为过滤测试用例之用。
177- 例如,你可以避免执行那些被标记成``` :external ``` 的测试,因为它们执行起来很慢。
174+ ExUnit中,标签(Tag)被用来注解该测试用例。在标记之后,这些标签可以用来过滤测试用例。
175+ 例如,你可以避免执行那些被标记成` :external ` 的测试,因为它们执行起来很慢而且可以依赖外部的东西。
176+ 但是它们依然在你的工程之内。
178177
179- 本章带你一窥Elixir元编程的冰山一角,讲解了模块属性在开发中是如何扮演关键角色的。
180- 下一章将讲解结构体和协议 。
178+ 本章带你一窥Elixir元编程的冰山一角,讲解了模块属性在开发中是如何扮演关键角色的。
179+ 下一章将讲解结构体(structs)和协议(protocols),在前进到其它更远的知识点(诸如异常处理等)之前 。
0 commit comments