2017-02-15 119 views
0

我正在学习Elixir元编程,并且正在制作一个macro,它允许我定义REST资源。接口会是这样:Eixir元编程 - 在宏内编译时定义宏

defmodule Router do 
    use Resources 

    resource "cars" 
    resource "animals" 
end 

我得到尽可能使用Module模块定义模块属性,但我不能让下面的工作:

defmodule Resource do 
    defmacro __using__(_opts) do 
    quote do 
     Module.put_attribute __MODULE__, :stack, [1, 2, 3] 
     defmacro resource(name) do 
     stack = Module.get_attribute __MODULE__, :stack 
     Module.put_attribute __MODULE__, :stack, [name|stack] 
     end 
    end 
    end 
end 

下列不编译:

defmodule Domain do 
    use Resource 

    resource "foo" 

    def run do 
    IO.inspect @stack 
    end 
end 

如果我删除资源行,它会正确打印[1, 2, 3]

resource/1宏从run/0可见。

如何让路由器上的代码工作,以便调用资源“xxx”将“xxx”推入模块属性@stack上的堆栈?

回答

1

您需要单独定义第二个Macro,而不是在__using__宏中。您可以使用第一个宏到import资源并定义最初的@stack,以便您可以在模块中使用resource宏。

您也不需要拨打Module.get_attributeModule.put_attribute,只需要使用@stack无处不在。

试试这个:

defmodule Resource do 
    defmacro __using__(_opts) do 
    quote do 
     import Resource 
     @stack [1,2,3] 
    end 
    end 

    defmacro resource(name) do 
    quote do 
     @stack [unquote(name) | @stack] 
    end 
    end 
end 

中调用Domain.run现在应该给你["foo", 1, 2, 3]。您还应该浏览关于在Elixir建立自己的Domain Specific Languages的官方指南。