PhoenixMountable behaviour (PhoenixMountable v0.2.1)

Copy Markdown View Source

Build libraries that mount into a Phoenix app.

A mountable library brings its own routes, LiveViews, controllers, assets and processes, and a host app mounts it with one line in its router:

# in the host router
import MyLib.Router

scope "/" do
  pipe_through :browser
  my_lib "/my_lib", resolver: MyApp.MyLibResolver
end

The library declares everything in a definition module:

defmodule MyLib.Router do
  use PhoenixMountable,
    otp_app: :my_lib,
    macro: :my_lib,
    layout: {MyLib.Layouts, :root},
    assets:
      {PhoenixMountable.Assets.Embedded,
       css: "priv/static/app.css",
       js: "priv/static/app.js",
       prepend: [:phoenix, :phoenix_html, :phoenix_live_view, :phoenix_mountable]}

  options resolver: [type: :atom, default: MyLib.Resolver, verify: {:behaviour, MyLib.Resolver}],
          features: [type: {:list, :atom}, default: []]

  routes do
    get "/export", MyLib.ExportController, :csv

    live_session :public, host_on_mount: false do
      live "/share/:token", MyLib.ShareLive, :show
    end

    live_session :default, on_mount: [MyLib.Auth] do
      live "/", MyLib.HomeLive, :index
      live "/items/:id", MyLib.ItemLive, :show
    end
  end
end

The generated macro (my_lib/2) inserts ordinary Phoenix routes and live sessions into the host router: mix phx.routes lists them, the host's pipelines and scopes apply to them, and LiveView navigation works between them. LiveView is optional: a library of controllers and plugs mounts in a host without it.

See PhoenixMountable.Definition for options/1, routes/1 and pipeline/2, and PhoenixMountable.Routes for the routes.

use options

  • :otp_app (atom/0) - Required. The library's OTP application.

  • :macro (atom/0) - Required. The name of the mount macro defined for hosts.

  • :as (atom/0) - The default mount name, used to name live sessions and route helpers. Defaults to the macro name. Hosts pass :as to mount the library more than once.

  • :title (String.t/0) - The page title of PhoenixMountable.Layout. Defaults to the camelized OTP application.

  • :layout - The root layout of the library's pages: a {module, function} tuple for a document of its own (such as {PhoenixMountable.Layout, :root}), or :host to render inside the host's root layout. The default value is :host.

  • :assets - How the library's CSS and JavaScript reach the browser: {strategy, options}. See PhoenixMountable.Assets. The default value is nil.

  • :on_mount - on_mount hooks of the library, run in every library live session after the host's. The default value is [].

  • :schema (atom/0) - The library's PhoenixMountable.Migrations module. The first use of each mount then checks the host has run the library's migrations; see PhoenixMountable.repo/1.

  • :reload_on_stale (boolean/0) - Reload a page whose library assets belong to a previous build when its LiveView reconnects. The default value is true.

  • :socket_path (String.t/0) - The default of the :socket_path mount option. The default value is "/live".

  • :transport - The default of the :transport mount option. The default value is :websocket.

Mount options

Besides the options a library declares, every mount macro takes:

  • :as (atom/0) - The mount name: names the live sessions and route helpers. Mount a library twice with different names.

  • :on_mount - The host's on_mount hooks, run before the library's. The default value is [].

  • :socket_path (String.t/0) - The path of the host's LiveView socket. Defaults to "/live".

  • :transport - The LiveView transport, :websocket (default) or :longpoll.

  • :csp_nonce_assign_key - The assign holding the request's CSP nonce, or a map of kinds to assigns, such as %{script: :script_nonce, style: :style_nonce}. The default value is nil.

  • :asset_host - A scheme and host to load the library's assets from, such as a CDN. The default value is nil.

  • :pubsub - The PubSub server of PhoenixMountable.PubSub. Defaults to the endpoint's. The default value is nil.

The mount

Library code reads the current PhoenixMountable.Mount as @mount in LiveViews and templates, conn.assigns.mount in controllers and plugs, or with PhoenixMountable.Mount.current/0 in the process handling them.

Summary

Callbacks

Child specs of the processes of one mount.

Adjusts or cross-validates the library's options after their validation.

The Ecto repo holding the library's tables, for the :schema check.

Extra values for the LiveView session of a library live session.

Functions

The mounts a host router declares.

A :via name for a process of a mount, unique to the mount.

Callbacks

children(t)

(optional)
@callback children(PhoenixMountable.Mount.t()) :: [
  Supervisor.child_spec() | {module(), term()} | module()
]

Child specs of the processes of one mount.

They start under a supervisor of their own the first time the mount is used, or when the host starts PhoenixMountable.Supervisor. Each mount gets its own subtree, so two mounts never share state. Name the children with via/2.

normalize_options(keyword)

(optional)
@callback normalize_options(keyword()) :: keyword()

Adjusts or cross-validates the library's options after their validation.

Receives the options with defaults (a keyword list) and returns them, possibly changed. Raise ArgumentError for an invalid combination. Runs when the host router compiles.

repo(t)

(optional)
@callback repo(PhoenixMountable.Mount.t()) :: module() | nil

The Ecto repo holding the library's tables, for the :schema check.

session(conn, t, session)

(optional)
@callback session(conn :: Plug.Conn.t(), PhoenixMountable.Mount.t(), session :: atom()) ::
  map()

Extra values for the LiveView session of a library live session.

Runs on the HTTP request that renders a library LiveView, with the host's conn, so it can resolve the current user from the host's assigns. Returns a map with string keys, merged into the session.

Functions

mounts(router)

@spec mounts(module()) :: [PhoenixMountable.Mount.t()]

The mounts a host router declares.

via(mount, name)

@spec via(PhoenixMountable.Mount.t(), term()) ::
  {:via, Registry, {PhoenixMountable.Registry, term()}}

A :via name for a process of a mount, unique to the mount.

def children(mount) do
  [{MyLib.Cache, name: PhoenixMountable.via(mount, :cache)}]
end