Skip to content

Commit a0750d5

Browse files
committed
update module attr
1 parent 4ff2a13 commit a0750d5

File tree

1 file changed

+63
-64
lines changed

1 file changed

+63
-64
lines changed

14-mod-attr.md

+63-64
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,40 @@
11
14-模块属性
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
1716
defmodule MyServer do
1817
@vsn 2
1918
end
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
4339
defmodule Math do
4440
@moduledoc """
@@ -58,11 +54,16 @@ defmodule Math do
5854
end
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+
```
6667
iex> h Math # Access the docs for the module Math
6768
...
6869
iex> 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
9686
defmodule MyServer do
9787
@initial_state %{host: "147.0.0.1", port: 3456}
@@ -100,9 +90,10 @@ end
10090
```
10191

10292
>不同于Erlang,默认情况下用户定义的属性不会被存储在模块里。属性值仅在编译时存在。
103-
开发者可以通过调用```Module.register_attribute/3```来使属性的行为更接近Erlang
93+
开发者可以通过调用`Module.register_attribute/3`来使这种属性的行为更接近Erlang
10494

10595
访问一个未定义的属性会报警告:
96+
10697
```elixir
10798
defmodule 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
115107
defmodule MyServer do
116108
@my_data 14
@@ -123,16 +115,20 @@ MyServer.first_data #=> 14
123115
MyServer.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
137133
defmodule MyPlug do
138134
use Plug.Builder
@@ -153,15 +149,17 @@ IO.puts "Running MyPlug with Cowboy on http://localhost:4000"
153149
Plug.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
166164
defmodule MyTest do
167165
use ExUnit.Case
@@ -173,8 +171,9 @@ defmodule MyTest do
173171
end
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

Comments
 (0)