Rowland Carlson

Programming Phoenix >= 1.4 by Chris McCord, Bruce Tate, and José Valim

Programming Phoenix >= 1.4 by Chris McCord, Bruce Tate, and José Valim

ISBN: 9781680502268
Date read: 2022-07-22
Recommended for: Developers who want to build resilient web applications.
(See my list of books, for more.)

You can buy it here.

An overview and tutorial on how to use Elixir's Phoenix web framework.

my notes

The opposite of a cache miss is a cache hit.

|> endpoint()
|> router()
|> pipelines()
|> controller()
new_page = route |> controller() |> view() |> template()

Use variables and Elixir code in .eex files with:

<%= :elixir_code and @assigned_variable %>

Web servers are just fancy functions.

Phoenix contexts are modules in the app directory.
With submodules defined in an appropriately named folder.
The outer module should serve as a public API for a context.

Structs can protect against mistyped map keys from developers.

In EEx files:

<%= %>

injects the result into the rendered file

<% %>


What is an I/O list?
How can they be used?

The functionality from use AppWeb, :view can be found in app_web.ex.
That use header invokes the view function in app_web.ex.
Which itself then brings in imports and uses by default.

Nest templates with

<%= render "first_template.html", var: @var %>

Ecto's schema macros let me declare both the struct and database table at the same time.

Create a migration with mix ecto.gen.migration migration_name

The separation of Changesets from schemas allows you to have a single schema,
With multiple update pathways.
Without tying each of those update pathways to each other.

comeonin is a password hashing specification for Elixir.

Fields in an Ecto schema can be set to virtual: true,
In which case that field will exist only in the struct,
Not the database.

Plugs can either be modules or functions.

plug Module
plug :function

Module plugs need two functions:

# Handles compile time options
def init(opts), do: opts

# Handles runtime updates to the connection object
def call(conn, _opts), do: conn

Functions plugs need to adhere to the arity 2 contract:

# A function plug must accept conn and opts, and return a conn
def function(conn, _opts), do: conn |> stuff()

A function plug can use a guard clause to handle specific actions

plug :function when action in [:index, :other]

configure_session(conn, renew: true) sends an updated cookie back to the user.

If a username isn't found during a login attempt,
Use comeonin's no_user_verify() to prevent timing attacks.

Pattern matching on {:error, _} lets me to return a vague failure,
Preserving privacy over whether an account exists on the website.

The html generator mix phx.gen.html takes the following parameters:

mix phx.gen.html \
ContextName \
SchemaName \
schema_plural \
each:string field:int with:types

To define a relationship in an Ecto schema use:

belongs_to :field, App.Other.Schema

Each Phoenix controller uses arguments defined by a default action/2.
I can override these arguments by defining my own:

def action(conn, _) do
  args = [conn, conn.params, my, extra, args]
  apply(__MODULE__, action_name(conn), args)

Then each function in the controller,
such as show would need to use these arguments:

def show(conn, params, my, extra, args), do: stuff()

Prefer creating new contexts to expanding old ones.

mix phx.gen.html is used to define and expose a resource over the web.
mix phx.gen.context is used to define a resource without exposure.
mix phx.gen.schema only creates a schema without convenince functions.
mix ecto.gen.migration creates an empty migration.

In an Ecto Query with pipe syntax,
Each query is independent of each other,
So the binding is specific to that function call.

Ecto offers two layers of escape hatches:
fragment/2 which safely interpolates a SQL string,
And Ecto.Adapaters.SQL.query/3:

  "SELECT fun_not_in_ecto($1, $2)", 
  [2, 17]

Both use escaped interpolation to prevent SQL injection.

What makes an error message "beautiful"?

Elixir has an unless macro.

Ecto uses a sandbox for test isolation.
Each individual test is wrapped in a reversible transaction.

Since each test is in its own transaction,
Tests can run against the database concurrently.
Async database test are only recommended for Postgres.

bypass_through/3 allows you to setup the connection that a plug expects for testing.

What does the phoenix_html javascript do?

Elixir has an URI module.

Ecto.Type behaviors can customize how a field is converted to and from the database.

Phoenix sockets are independent of transport protocol.
They only care that there is a connection,
Not where it came from.
This allows you to use sockets and channels for any connection.

Observer allows you to trigger failures in a supervised system.

You can define supervision values directly with a keyword list:

defmodule Example do
  use GenServer, restart: :permanent

Or you can define a child_spec(arg) behaviour,
That returns a map with the values needed.

Task links its process to its parent.
If any task fails, the parent will also fail.
This makes Task a bad choice for things that can acceptably fail.

URI.encode_query will take a keyword list
And transform them into a set of url query parameters.

Operators can be used in pipelines by calling them with their module:

()> "text "
()> |> Kernel.<>("more text")
"text more text" can measure the execution time of an module, function, args in microseconds.