Fun with Macros
Tymon Tobolski
GitHub: @teamon
Twitter: @iteamon
Slide 2
Slide 2 text
defmodule AppRouter do
use Plug.Router
plug :match
plug :dispatch
get "/hello" do
send_resp(conn, 200, "world")
end
match _ do
send_resp(conn, 404, "oops")
end
end
Slide 3
Slide 3 text
defmodule Cabify do
use Tesla
plug Tesla.Middleware.Base, "https://cabify.com"
plug Tesla.Middleware.JSON
def jobs do
request(:get, "/jobs")
end
end
defmodule Tesla do
defmacro __using__(_) do
quote do
Module.register_attribute(__MODULE__, :middleware, accumulate: true)
@before_compile Tesla
import Tesla, only: [plug: 1, plug: 2]
def request(method, path), do: Tesla.request(__MODULE__, {method, path})
end
end
# ...
Slide 6
Slide 6 text
defmacro plug(module, opts \\ []) do
quote do
@middleware {unquote(module), unquote(opts)}
end
end
Slide 7
Slide 7 text
defmodule Cabify do
Module.register_attribute(__MODULE__, :middleware, accumulate: true)
@before_compile Tesla
@middleware {Tesla.Middleware.Base, "https://cabify.com"}
@middleware {Tesla.Middleware.JSON, []}
end
Slide 8
Slide 8 text
defmacro __before_compile__(env) do
middleware = Module.get_attribute(env.module, :middleware)
quote do
def __middleware__, do: unquote(middleware)
end
end
Slide 9
Slide 9 text
defmodule Cabify do
# ...
def __middleware__ do
[
{Tesla.Middleware.Base, "https://cabify.com"},
{Tesla.Middleware.JSON, []}
]
end
end
Slide 10
Slide 10 text
defmodule Tesla do
def request(module, env) do
stack = module.__middleware__
run(env, stack)
end
def run(env, []), do: make_request(env)
def run(env, [{module, opts} | rest]), do:
apply(module, :call, [env, rest, opts])
end
Slide 11
Slide 11 text
defmodule Tesla.Middleware.Base do
def call({method, path}, next, base) do
Tesla.run({method, base <> path}, next)
end
end
defmodule Tesla.Middleware.JSON do
def call({method, path}, next, _opts) do
Tesla.run({method, path <> ".json"}, next)
end
end
Slide 12
Slide 12 text
def jobs do
request(:get, "/jobs")
end
Cabify.jobs()
# => "HTTP get https://cabify.com/jobs.json"
Slide 13
Slide 13 text
- Simple & powerful API - call(env, next, opts)
- Allows hot reloading of middlewares on production system
- Full implementation at github.com/teamon/tesla (be sure to
use 1.0.0-beta.1)
- Don’t be afraid of macros :)