Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fun with Elixir Macros

Fun with Elixir Macros

Lightning talk from ElixirConfEU 2018


Tymon Tobolski

April 16, 2018

More Decks by Tymon Tobolski

Other Decks in Programming


  1. Fun with Macros Tymon Tobolski GitHub: @teamon Twitter: @iteamon

  2. 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
  3. defmodule Cabify do use Tesla plug Tesla.Middleware.Base, "https://cabify.com" plug Tesla.Middleware.JSON

    def jobs do request(:get, "/jobs") end end
  4. 1. Accumulate middleware (plug Module) 2. Runtime execution

  5. 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 # ...
  6. defmacro plug(module, opts \\ []) do quote do @middleware {unquote(module),

    unquote(opts)} end end
  7. 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
  8. defmacro __before_compile__(env) do middleware = Module.get_attribute(env.module, :middleware) quote do def

    __middleware__, do: unquote(middleware) end end
  9. defmodule Cabify do # ... def __middleware__ do [ {Tesla.Middleware.Base,

    "https://cabify.com"}, {Tesla.Middleware.JSON, []} ] end end
  10. 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
  11. 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
  12. def jobs do request(:get, "/jobs") end Cabify.jobs() # => "HTTP

    get https://cabify.com/jobs.json"
  13. - 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 :)