1
1
14-模块属性
2
2
===========
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 )
6
3
7
- 在Elixir中,模块属性(attributes)主要服务于三个目的:
8
- 1 . 作为一个模块的注释,通常附加上用户或虚拟机用到的信息
4
+ 在Elixir中,模块属性(module attributes)主要服务于三个目的:
5
+ 1 . 作为一个模块的注解(annotations),通常附加上用户或虚拟机会用到的信息
9
6
2 . 作为常量
10
- 3 . 在编译时作为一个临时的存储机制
7
+ 3 . 在编译时作为一个临时的模块存储机制
11
8
12
- 让我们一个一个讲解。
9
+ 下面让我们来一一讲解。
10
+
11
+ ## 作为注解(annotations)
12
+
13
+ Elixir从Erlang带来了模块属性的概念。如:
13
14
14
- ## 14.1-作为注释
15
- Elixir从Erlang带来了模块属性的概念。例子:
16
15
``` elixir
17
16
defmodule MyServer do
18
17
@vsn 2
19
18
end
20
- ```
19
+ ```
21
20
22
21
这个例子中,我们显式地为该模块设置了 _ 版本(vsn即version)_ 属性。
23
- 属性标识``` @vsn ``` 是预定义的属性名称,会被Erlang虚拟机的代码装载机制使用:
24
- 读取并检查该模块是否在某处被更新了。
25
- 如果不注明版本号,会被自动设置为这个模块函数的md5 checksum。
22
+ ` @vsn ` 是一个系统保留的属性名称,它被Erlang虚拟机的代码装载机制使用,以检查该模块是否被更新过。
23
+ 如果不注明版本号,该属性的值会自动设置为模块函数的md5 checksum。
26
24
27
- Elixir有个好多系统保留的预定义属性。比如一些常用的:
28
- - @moduledoc
29
- 为整个模块提供文档说明
30
- - @doc
31
- 为该属性后面的函数或宏提供文档说明
32
- - @behaviour
33
- (注意这个单词是英式拼法)用来注明一个OTP或用户自定义行为
34
- - @before_compile
35
- 提供一个每当模块被编译之前执行的钩子。这使得我们可以在模块被编译之前往里面注入函数。
25
+ Elixir还有好多系统保留的预定义注解。下面是一些比较常用的:
36
26
37
- @moduledoc 和@doc 是很常用的属性,推荐经常使用(写文档)。
27
+ * ` @moduledoc ` - 为当前模块提供文档说明
28
+ * ` @doc ` - 为该属性标注的函数或宏提供文档说明
29
+ * ` @behaviour ` - (注意这个单词是英式拼法)用来注明一个OTP或用户自定义行为
30
+ * ` @before_compile ` - 提供一个每当模块被编译之前执行的钩子。这使得我们可以在模块被编译之前往里面注入函数
38
31
39
- Elixir视文档为一等公民,提供了很多方法来访问文档。
32
+ ` @moduledoc ` 和` @doc ` 是目前最常用的注解属性,我们也希望你能够多使用它们。
33
+ Elixir视文档为一等公民,而且提供了很多方法来访问这些文档。
34
+ 你可以拓展阅读文章[ 《用我们官方的方式写Elixir程序文档》] ( http://elixir-lang.org/docs/stable/elixir/writing-documentation.html ) 。
35
+
36
+ 让我们回到上几章定义的` Math ` 模块,为它添加文档,然后依然保存在math.ex文件中:
40
37
41
- 让我们回到上几章定义的Math模块,为它添加文档,然后依然保存在math.ex文件中:
42
38
``` elixir
43
39
defmodule Math do
44
40
@moduledoc """
@@ -58,11 +54,16 @@ defmodule Math do
58
54
end
59
55
```
60
56
61
- 上面例子使用了heredocs注释。heredocs是多行的文本,用三个引号包裹,保持里面内容的格式。
62
- 下面例子演示在iex中,用h命令读取模块的注释:
57
+ Elixir推荐使用markdown语法和多行文本(heredocs)书写容易阅读的文档。
58
+ heredocs是多行的字符串,用三个双引号包裹,它会保持里面内容的格式不变。
59
+ 我们可以在IEx中读取任何编译的模块的文档:
60
+
63
61
``` elixir
64
62
$ elixirc math.ex
65
63
$ iex
64
+ ```
65
+
66
+ ```
66
67
iex> h Math # Access the docs for the module Math
67
68
...
68
69
iex> h Math.sum # Access the docs for the sum function
@@ -73,25 +74,14 @@ Elixir还提供了[ExDoc工具](https://github.com/elixir-lang/ex_doc),
73
74
利用注释生成HTML页文档。
74
75
75
76
你可以看看[ 模块] ( 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
+
95
85
``` elixir
96
86
defmodule MyServer do
97
87
@initial_state %{host: " 147.0.0.1" , port: 3456 }
100
90
```
101
91
102
92
> 不同于Erlang,默认情况下用户定义的属性不会被存储在模块里。属性值仅在编译时存在。
103
- 开发者可以通过调用``` Module.register_attribute/3 ``` 来使属性的行为更接近Erlang 。
93
+ 开发者可以通过调用` Module.register_attribute/3 ` 来使这种属性的行为更接近Erlang 。
104
94
105
95
访问一个未定义的属性会报警告:
96
+
106
97
``` elixir
107
98
defmodule MyServer do
108
99
@unknown
@@ -111,6 +102,7 @@ warning: undefined module attribute @unknown, please remove access to @unknown o
111
102
```
112
103
113
104
最后,属性也可以在函数中被读取:
105
+
114
106
``` elixir
115
107
defmodule MyServer do
116
108
@my_data 14
@@ -123,16 +115,20 @@ MyServer.first_data #=> 14
123
115
MyServer .second_data # => 13
124
116
```
125
117
126
- 注意,在函数内读取某属性,读取的是该属性当前值的快照。换句话说,读取的是编译时的值,而非运行时。
127
- 后面我们将看到,这个特点使得属性可以作为模块在编译时的临时存储。
118
+ 注意,在函数内读取某属性,读取的是该属性值的一份快照。换句话说,读取的是编译时的值,而非运行时。
119
+ 后面我们将看到,这个特点使得属性可以作为模块在编译时的临时存储,十分有用。
120
+
121
+ ## 作为临时存储
128
122
129
- ## 14.3-作为临时存储
130
- Elixir组织中有一个项目,叫做[ Plug] ( https://github.com/elixir-lang/plug ) 。
123
+ Elixir组织中有一个项目,叫做[ Plug] ( https://github.com/elixir-lang/plug ) ,
131
124
这个项目的目标是创建一个通用的Web库和框架。
132
125
133
- > 类似于ruby的rack
126
+ > 注:我想功能应该类似于ruby的rack。你可以定义各种plug,这这些plug会像链条一样,
127
+ 按顺序各自对http请求进行加工处理,最后返回。这类似给rails或sinatra定义各种rack中间件,
128
+ 也有些类似Java filter的概念。最终,Plug框架会组织和执行它们。
129
+
130
+ Plug库允许开发者定义它们自己的plug,运行在web服务器上:
134
131
135
- Plug库允许开发者定义它们自己的plug,可以在一个web服务器上运行:
136
132
``` elixir
137
133
defmodule MyPlug do
138
134
use Plug .Builder
@@ -153,15 +149,17 @@ IO.puts "Running MyPlug with Cowboy on http://localhost:4000"
153
149
Plug .Adapters .Cowboy .http MyPlug , []
154
150
```
155
151
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(领域特定语言)。
160
159
161
- 为了理解底层的代码,我们需要宏。因此我们将回顾一下元编程手册里这种模式。
162
- 但是这里的重点是怎样使用属性来存储数据,让开发者得以创建DSL(领域特定语言)。
160
+ 另一个例子来自 [ ExUnit框架 ] ( http://elixir-lang.org/docs/stable/ex_unit/ ) ,
161
+ 它使用模块属性作为注解和存储:
163
162
164
- 另一个例子来自ExUnit框架,它使用模块属性作为注释和存储:
165
163
``` elixir
166
164
defmodule MyTest do
167
165
use ExUnit .Case
@@ -173,8 +171,9 @@ defmodule MyTest do
173
171
end
174
172
```
175
173
176
- ExUnit中,@tag 标签被用来注释该测试用例。之后,这些标签可以作为过滤测试用例之用。
177
- 例如,你可以避免执行那些被标记成``` :external ``` 的测试,因为它们执行起来很慢。
174
+ ExUnit中,标签(Tag)被用来注解该测试用例。在标记之后,这些标签可以用来过滤测试用例。
175
+ 例如,你可以避免执行那些被标记成` :external ` 的测试,因为它们执行起来很慢而且可以依赖外部的东西。
176
+ 但是它们依然在你的工程之内。
178
177
179
- 本章带你一窥Elixir元编程的冰山一角,讲解了模块属性在开发中是如何扮演关键角色的。
180
- 下一章将讲解结构体和协议 。
178
+ 本章带你一窥Elixir元编程的冰山一角,讲解了模块属性在开发中是如何扮演关键角色的。
179
+ 下一章将讲解结构体(structs)和协议(protocols),在前进到其它更远的知识点(诸如异常处理等)之前 。
0 commit comments