Slide 1

Slide 1 text

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

Slide 4

Slide 4 text

1. Accumulate middleware (plug Module) 2. Runtime execution

Slide 5

Slide 5 text

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 :)