http://elixir-lang.org/getting_started/14.htmlhtml
在Elixir中模块属性有三个目的:git
Elixir 模块属性的概念来源于Erlang,例如:github
defmodule MyServer do @vsn 2 end
在上面的例子中,咱们设置了MyServer该模块的版本属性。@vsn
用于Erlang code reload机制中的来检查模块代码
是否更新。若是没有指定版本,该版本被设置为模块函数的MD5校验码。web
Elixir 保留的属性屈指可数。下面是最经常使用的几个:编程
@moduledoc
- 为所在的module的文档属性@doc
- 用于函数和宏的文档@behaviour
- 用于指定一个OTP胡行为模式或用户定义的行为.@before_compile
- 提供了一个hook,在module编译以前调用,这使得在编译这前在module注入函数成为可能。@moduledoc
和@doc
应该是用的最多的。咱们也期待你会常常使用。Elixir把文档看做是第一类(first-class)的对象,而且提供了不少函数访问文档:markdown
iex> defmodule MyModule do ...> @moduledoc "It does **x**" ...> ...> @doc """ ...> Returns the version ...> """ ...> def version, do: 1 ...> end {:module, MyModule, <<70, 79, 82, ...>>, {:version, 0}} iex> h MyModule * MyModule It does **x** iex> h MyModule.version * def version() Returns the version
Elixir提倡使用markdown与heredocs编写可读的文档。heredocs多行字符串,开始和结束都是使用三引号,这种方式能够保持内文本的原有的格式:框架
defmodule Math do @moduledoc """ This module provides mathematical functions as sin, cos and constants like pi. ## Examples Math.pi #=> 3.1415... """ end
咱们也提供了一个叫ExDoc
的工具用来HTML格式的文档。ide
你能够看下 []Module](http://elixir-lang.org/docs/stable/Module.html)的文档查看完整支持的属性。Elixir也使用下面的属性来定义 ypespecs函数
@spec
- 可用于描述函数输入参数的类型和返回值的类型@callback
- 描述行为模式的原型定义@type
- 定义类型,可用于 @spec
@typep
- 定义私有类型,用于 @spec
@opaque
- 定义opaque 类型,用于 @spec这部分主要介绍的是扩展属性,其实,用户是能够本身定义属性或经过扩展库支持自定义的行为模式。工具
Elixir 开发都常常会模块属性做为常量使用:
defmodule MyServer do @initial_state %{host: "147.0.0.1", port: 3456} IO.inspect @initial_state end
注意:和Erlang不同的是,用户定义的属性默认不会存储在module内。这些值只会存在于编译时, 若是想让定义属性特性 相似Erlang的属性特性,能够能过Module.register_attribute/3
试图访问未经定义的属性会打印warning:
defmodule MyServer do @unknown end warning: undefined module attribute @unknown, please remove access to @unknown or explicitly set it to nil before access
最后,属性是能够在函数内访问到的:
defmodule MyServer do @my_data 14 def first_data, do: @my_data @my_data 13 def second_data, do: @my_data end MyServer.first_data #=> 14 MyServer.second_data #=> 13
请注意,函数里面读取属性是取当前值的快照。也就是说,值是在编译时读取的,而不是运行时。正如咱们将要看到的,这使得模块属性能够很是方便的在编译期间被用做存储。
Elixir 组织(github organization)下有个Plug项目,是在ELixir中构建Web库和框架的通用基础。
Plug
容许开发都定义本身的plug,运行在本身的web server:
defmodule MyPlug do use Plug.Builder plug :set_header plug :send_ok def set_header(conn, _opts) do put_resp_header(conn, "x-header", "set") end def send_ok(conn, _opts) do send(conn, 200, "ok") end end IO.puts "Running MyPlug with Cowboy on http://localhost:4000" Plug.Adapters.Cowboy.http MyPlug, []
在上面的例子中,咱们使用plug/1
宏链接处理函数,当有web请求时,这些函数会被调用。在内部实现上,每次你调用 call/1
,Plug会把给定的参数存储到@plugs
属性。该模块被编译以前,Plug运行一个回调,定义方法call/ 2
用于处理HTTP请求。这会根据@plugs中定义顺序运行全部的plug。
为了了解底层代码,咱们须要的宏,因此咱们在元编程指南将再次讨论这个模式。不过这里的重点是使用module的属性如何做为存储, 并可让开发者建立的DSL。
另外一个例子来自ExUnit框架,它使用module属性做为注解和存储:
defmodule MyTest do use ExUnit.Case @tag :external test "contacts external service" do # ... end end
在ExUnit框架里,Tags是用做注解,加了tag,后边就能够过滤到这个测试,由于这个测试运行比较慢且依赖其它的服务,咱们不想在开发机器上运行,只想让他在build系统运行。
但愿学习了这部分你能感觉到Elixir是如何支持元编程,以及module 属性在元编程中发挥的重要做用。