arcology-phx/arcology_web.org

5.2 KiB
Raw Permalink Blame History

Arcology Web Elixir Application

Setting up the ArcologyWeb module

Much of this is boilerplate which I include here only to understand and take apart how Phoenix operates. There's not a whole lot hiding "under the hood" compared to, say, Ruby on Rails, which I really appreciate, to be honest.

So starting with the moduledoc:

The entrypoint for defining your web interface, such
as controllers, components, channels, and so on.

This can be used in your application as:

    use ArcologyWeb, :controller
    use ArcologyWeb, :html

The definitions below will be executed for every controller,
component, etc, so keep them short and clean, focused
on imports, uses and aliases.

Do NOT define functions inside the quoted expressions
below. Instead, define additional modules and import
those modules here.

This is pretty straightforward, right? Our controllers and views can include a bunch of standard behavior (auth, logging, rate limiting, etc) by invoking those use statements. There are a bunch of functions defined below which will be exposed in that fashion using this "magic" __using__ macro:

@doc """
When used, dispatch to the appropriate controller/view/etc.
"""
defmacro __using__(which) when is_atom(which) do
  apply(__MODULE__, which, [])
end

Some of the helpers include some extra helpers shared among them by "unquoting" a function which returns "quoted" import and alias statements this is the sort of indirection which makes sense in my Lisp brain, but it can be really off-putting to people who aren't exposed to this sort of pattern. The basic idea here is to provide those statements in the function which is "used in" rather than in the private helpers where they would be executed were they not quoted.

These functions import functionality for HTML rendering, compile-time Verified Route generation, and translations:

defp html_helpers do
  quote do
    # HTML escaping functionality
    import Phoenix.HTML
    # Core UI components and translation
    import ArcologyWeb.CoreComponents
    import ArcologyWeb.Gettext

    # Shortcut for generating JS commands
    alias Phoenix.LiveView.JS

    # Routes generation with the ~p sigil
    unquote(verified_routes())
  end
end

def verified_routes do
  quote do
    use Phoenix.VerifiedRoutes,
      endpoint: ArcologyWeb.Endpoint,
      router: ArcologyWeb.Router,
      statics: ArcologyWeb.static_paths()
  end
end

Use'ing :router in roam:ArcologyWeb.Router lets me customize the behavior of the router by introducing Plugs; I'll be adding a Plug to attempt to load Arcology pages rather than using a "greedy" route which I used in (for example) the FastAPI Arcology Domain Router.

def router do
  quote do
    use Phoenix.Router, helpers: false

    # Import common connection and controller functions to use in pipelines
    import Plug.Conn
    import Phoenix.Controller
    import Phoenix.LiveView.Router
  end
end

Routing, controlling, viewing, etc, can all be skipped for static_paths, these are files that can just be served out of the filesystem where the application is built and bundled:

def static_paths, do: ~w(assets fonts images favicon.ico robots.txt)

The :controller helper sets up rendering rules, provides access to translations

def controller do
  quote do
    use Phoenix.Controller,
      formats: [:html, :json],
      layouts: [html: ArcologyWeb.Layouts]

    import Plug.Conn
    import ArcologyWeb.Gettext

    unquote(verified_routes())
  end
end

The :live_view, :live_component, and :html helpers set up HTML rendering and dynamic LiveView components using the quoted helpers described above.

def live_view do
  quote do
    use Phoenix.LiveView,
      layout: {ArcologyWeb.Layouts, :app}

    unquote(html_helpers())
  end
end

def live_component do
  quote do
    use Phoenix.LiveComponent

    unquote(html_helpers())
  end
end

def html do
  quote do
    use Phoenix.Component

    # Import convenience functions from controllers
    import Phoenix.Controller,
      only: [get_csrf_token: 0, view_module: 1, view_template: 1]

    # Include general helpers for rendering HTML
    unquote(html_helpers())
  end
end

The :channel helper will provide Phoenix.Channel helpers to PubSub channels, if I decide to use them.

def channel do
  quote do
    use Phoenix.Channel
  end
end
defmodule ArcologyWeb do
  @moduledoc """
  <<ArcologyWeb.moduledoc>>
  """

  <<helpers>>

  <<quoted_helpers>>
  
  <<ArcologyWeb.__using__>>
end

ArcologyWeb.Endpoint helpers