I view it as a reflection of a Don’t Write Macros But Do Learn How They Work culture.
i.e. in your software don’t lean too heavily on the mechanics of use
but expect to use it in combination with the core products (to inject code at compile time).
For example Phoenix.Controller
et al rely heavily on it. First use Phoenix.Controller
appears in the seed rumbl_web.ex
:
defmodule RumblWeb do
@moduledoc """
The entrypoint for defining your web interface, such
as controllers, views, channels and so on.
This can be used in your application as:
use RumblWeb, :controller
use RumblWeb, :view
The definitions below will be executed for every view,
controller, etc, so keep them short and clean, focused
on imports, uses and aliases.
Do NOT define functions inside the quoted expressions
below. Instead, define any helper function in modules
and import those modules here.
"""
def controller do
quote do
use Phoenix.Controller, namespace: RumblWeb
import Plug.Conn
import RumblWeb.Gettext
import RumblWeb.Auth, only: [authenticate_user: 2] # New import
alias RumblWeb.Router.Helpers, as: Routes
end
end
def view do
quote do
use Phoenix.View, root: "lib/rumbl_web/templates",
namespace: RumblWeb
# Import convenience functions from controllers
import Phoenix.Controller, only: [get_flash: 2, view_module: 1]
# Use all HTML functionality (forms, tags, etc)
use Phoenix.HTML
import RumblWeb.ErrorHelpers
import RumblWeb.Gettext
alias RumblWeb.Router.Helpers, as: Routes
end
end
def router do
quote do
use Phoenix.Router
import Plug.Conn
import Phoenix.Controller
import RumblWeb.Auth, only: [authenticate_user: 2] # New import
end
end
def channel do
quote do
use Phoenix.Channel
import RumblWeb.Gettext
end
end
@doc """
When used, dispatch to the appropriate controller/view/etc.
"""
defmacro __using__(which) when is_atom(which) do
apply(__MODULE__, which, [])
end
end
and then RumbWeb
itself is use
d in the controllers.
defmodule RumblWeb.PageController do
use RumblWeb, :controller
def index(conn, _params) do
render conn, "index.html"
end
end
The above referenced render/2
function is actually imported from the Phoenix.Controller
module:
defmodule Phoenix.Controller do
# ...
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
import Phoenix.Controller
# TODO v2: No longer automatically import dependencies
import Plug.Conn
use Phoenix.Controller.Pipeline, opts
plug :put_new_layout, {Phoenix.Controller.__layout__(__MODULE__, opts), :app}
plug :put_new_view, Phoenix.Controller.__view__(__MODULE__)
end
end
# ...
def render(conn, template) when is_binary(template) or is_atom(template) do
render(conn, template, [])
end
# ...
def render(conn, template, assigns)
when is_binary(template) and (is_map(assigns) or is_list(assigns)) do
case Path.extname(template) do
"." <> format ->
do_render(conn, template, format, assigns)
"" ->
raise "cannot render template #{inspect template} without format. Use an atom if the " <>
"template format is meant to be set dynamically based on the request format"
end
end
# ...
end