Building DSLs
Real-world DSLs: Ecto schemas/queries, Phoenix router, Plug.Builder, ExUnit, Absinthe, Ash.
39.1 __using__
defmodule MyApp.Schema do
defmacro __using__(opts) do
quote do
import MyApp.Schema, only: [field: 2]
Module.register_attribute(__MODULE__, :fields, accumulate: true)
@table unquote(Keyword.fetch!(opts, :table))
@before_compile MyApp.Schema
end
end
defmacro field(name, type) do
quote do: @fields {unquote(name), unquote(type)}
end
defmacro __before_compile__(_env) do
quote do
def __table__, do: @table
def __fields__, do: Enum.reverse(@fields)
end
end
end
defmodule User do
use MyApp.Schema, table: "users"
field :name, :string
field :age, :integer
end
User.__fields__() # [name: :string, age: :integer]39.2 A tiny test framework DSL
Real-world DSLs: Ecto schemas/queries, Phoenix router, Plug.Builder, ExUnit, Absinthe, Ash.
defmodule MiniTest do
defmacro __using__(_) do
quote do
import MiniTest
Module.register_attribute(__MODULE__, :tests, accumulate: true)
@before_compile MiniTest
end
end
defmacro test(desc, do: body) do
fun = String.to_atom("test " <> desc)
quote do
@tests {unquote(fun), unquote(desc)}
def unquote(fun)(), do: unquote(body)
end
end
defmacro __before_compile__(_) do
quote do
def run do
for {fun, desc} <- @tests do
result = if apply(__MODULE__, fun, []), do: "PASS", else: "FAIL"
IO.puts("#{result}: #{desc}")
end
end
end
end
end