2017-02-15 17 views
0

Я изучаю метапрограммирование Elixir, и я играю с созданием macro, что позволяет мне определять ресурсы REST. Интерфейс будет выглядеть так:Eixir metaprogramming - определяющий макрос, доступный во время компиляции изнутри макроса

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.

Как я могу заставить код в Router работать, чтобы вызывающий ресурс «xxx» толкал «xxx» в стек в атрибуте модуля @stack?

ответ

1

Вы должны определить второй Macro отдельно, а не в пределах __using__ макроса. Вы можете использовать первый макрос для import Ресурс и определить начальный @stack, чтобы затем вы могли использовать макрос resource в своем модуле.

Вам также не нужно звонить Module.get_attribute и Module.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 

Calling Domain.run теперь должен дать вам ["foo", 1, 2, 3]. Вы также должны ознакомиться с официальным руководством по созданию собственного Domain Specific Languages в Эликсире.