Reputation: 425
I am studying Elixir metaprogramming and I am toying around with making a macro
that allows me to define REST resources. The interface would be like this:
defmodule Router do
use Resources
resource "cars"
resource "animals"
end
I got as far as defining a module attribute using the Module
module, but I can't get the following to work:
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
The following does not compile:
defmodule Domain do
use Resource
resource "foo"
def run do
IO.inspect @stack
end
end
If I remove the resource line, it prints [1, 2, 3]
correctly.
The resource/1
macro is visible from run/0
.
How could I get the code in Router to work, so that calling resource "xxx" pushes "xxx" to the stack on the @stack
module attribute?
Upvotes: 0
Views: 73
Reputation: 75740
You need to define the second Macro
separately, not within the __using__
macro. You can use the first macro to import
Resource and define the initial @stack
, so you can then use the resource
macro in your Module.
You also don't need to call Module.get_attribute
and Module.put_attribute
, and just use @stack
everywhere.
Try this:
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
should now give you ["foo", 1, 2, 3]
. You should also go over the Official guide on building your own Domain Specific Languages in Elixir.
Upvotes: 1