arcology-phx/arcology_web.org

178 lines
5.2 KiB
Org Mode

:PROPERTIES:
:ID: 20230303T095957.835101
:ROAM_ALIASES: ArcologyWeb
:END:
#+TITLE: Arcology Web Elixir Application
* Setting up the =ArcologyWeb= module
:PROPERTIES:
:ID: 20230303T114356.700426
:ROAM_ALIASES: ArcologyWeb
:END:
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:
#+begin_src text :noweb-ref ArcologyWeb.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.
#+end_src
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:
#+begin_src elixir :noweb-ref ArcologyWeb.__using__
@doc """
When used, dispatch to the appropriate controller/view/etc.
"""
defmacro __using__(which) when is_atom(which) do
apply(__MODULE__, which, [])
end
#+end_src
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, [[https://hexdocs.pm/phoenix/1.7.0-rc.0/Phoenix.VerifiedRoutes.html][compile-time Verified Route]] generation, and translations:
#+begin_src elixir :noweb-ref quoted-helpers
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
#+end_src
Use'ing =:router= in [[roam:ArcologyWeb.Router]] lets me customize the behavior of the router by introducing [[https://hexdocs.pm/plug/readme.html][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 [[id:20220225T175638.482695][Arcology Domain Router]].
#+begin_src elixir :noweb-ref helpers
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
#+end_src
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:
#+begin_src elixir :noweb-ref helpers
def static_paths, do: ~w(assets fonts images favicon.ico robots.txt)
#+end_src
The =:controller= helper sets up rendering rules, provides access to translations
#+begin_src elixir :noweb-ref helpers
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
#+end_src
The =:live_view=, =:live_component=, and =:html= helpers set up HTML rendering and dynamic LiveView components using the quoted helpers described above.
#+begin_src elixir :noweb-ref helpers
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
#+end_src
The =:channel= helper will provide =Phoenix.Channel= helpers to PubSub channels, if I decide to use them.
#+begin_src elixir :noweb-ref helpers
def channel do
quote do
use Phoenix.Channel
end
end
#+end_src
#+begin_src elixir :noweb yes :tangle lib/arcology_web.ex
defmodule ArcologyWeb do
@moduledoc """
<<ArcologyWeb.moduledoc>>
"""
<<helpers>>
<<quoted_helpers>>
<<ArcologyWeb.__using__>>
end
#+end_src
* =ArcologyWeb.Endpoint= helpers
:PROPERTIES:
:ID: 20230303T104626.041201
:END: