Geoffrey Lessel’s 2019 book, Phoenix in Action, was written for Phoenix 1.4.
I found that the book’s code examples did not match the current Phoenix 1.7 web application structure.
Reverting to Phoenix 1.6, I was able to build, run, and test the example Auction Web application.
As I worked through the book, I made the following changes to accomodate Phoenix 1.6:
6.1 Installing Phoenix on your system
Install the latest version of Phoenix 1.6.
mix archive.install hex phx_new 1.6.16
6.2 Creating a new Phoenix application
Phoenix 1.6 applications include a mailer by default.
The auction project does not need it.
When generating the auction_web
application, omit the mailer with this command.
mix phx.new.web auction_web --no-ecto --no-mailer
Listing 6.3 shows adding this line to configure Phoenix to use Jason.
config :phoenix, :json_library, Jason
Phoenix 1.6 generates that line automatically; there is no need to add it.
6.2.1 Running your server for the first time
The “good news” also includes this advice:
Your web app requires a PubSub server to be running.
The PubSub server is typically defined in a `mix phx.new.ecto` app.
If you don't plan to define an Ecto app, you must explicitly start
the PubSub in your supervision tree as:
{Phoenix.PubSub, name: AuctionWeb.PubSub}
That line should be added to the list of children in
auction_umbrella/apps/auction_web/lib/auction_web/application.ex
:
@impl true
def start(_type, _args) do
children = [
{Phoenix.PubSub, name: AuctionWeb.PubSub},
# Start the Telemetry supervisor
AuctionWeb.Telemetry,
# Start the Endpoint (http/https)
AuctionWeb.Endpoint
# Start a worker by calling: AuctionWeb.Worker.start_link(arg)
# {AuctionWeb.Worker, arg}
]
Running the web server without this line results in a barrage of log messages such as:
08:55:41.210 [error] GenServer #PID<0.629.0> terminating
** (ArgumentError) unknown registry: AuctionWeb.PubSub
...
When first run, phx.server
complained:
>mix phx.server
warning: the :gettext compiler is no longer required in your mix.exs.
Please find the following line in your mix.exs and remove the :gettext entry:
compilers: [..., :gettext, ...] ++ Mix.compilers(),
I deleted :gettext
from this line in auction_umbrella\apps\auction_web\mix.exs
:
compilers: [:gettext] ++ Mix.compilers(),
10.4 Adding site navigation
In Phoenix 1.6, root.html.hexx
contains the HTML boilerplate, including the <header>
.
That file has a placeholder, @inner_content
, for the contents of app.html.heex
,
which provides two alerts and references additional @inner_content
.
The site navigation can be added before those alerts.
11.3.3 Adding the list of bids to the view template
In Listing 11.21, item_bid_path
should be Routes.item_bid_path
in this line:
<%= form_for @bid, item_bid_path(@conn, :create, @item), fn f -> %>
12.2 Connecting a user to a channel and a topic
The Phoenix app was missing user_socket.ex
. Create it with these commands:
>cd auction_umbrella\apps\auction_web
>mix phx.gen.socket User
Follow the resulting instructions to add the socket handler to your lib/auction_web/endpoint.ex
, for example:
socket "/socket", AuctionWeb.UserSocket,
websocket: true,
longpoll: false
12.2.2 Getting the user’s browser to join a topic
With Phoenix 1.6, the socket.js
file is named user_socket.js
.
Listing 12.4 shows the automatically generated channel topic:subtopic
.
Phoenix 1.6 instead generates the channel room:42
.
In Listing 12.6, also uncomment this line near the beginning of the file:
// import "./user_socket.js"
12.4.1 Refactoring the rendering of an item’s bids
The code in Listing 12.12 must be saved in bid.html.eex
, not bid.html.heex
.
Phoenix 16 introduced a new HTML engine that uses HTML-aware template files
(HEEx: HTML Embedded Elixir).
I did not initially notice that many automatically generated templates had the
.heex
extension.
See Phoenix 1.6.0-rc.0 released!
for a description of HEEx templates.
See Phoenix.View.render_many can't render .leex templates · Issue #3952 · phoenixframework/phoenix · GitHub
for a discussion of render_many
and HEEx files.
14.4.3 Viewing documentation with ExDoc
Add a project name to auction_umbrella/mix.exs
:
def project do
[
name: "Auction Umbrella",
apps_path: "apps",
version: "0.1.0",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
14.4.4 Adding examples and doctests
In the first example in Listing 14.22, start the second line with ...>
and correct the double quotes
around the password confirmation value.
iex> insert_user(%{username: "geo", password: "example",
...> password_confirmation: "example", email_address: "test@example.com"})
...> result = get_user_by_username_and_password("geo", "example")
...> match?(%Auction.User{username: "geo"}, result)
true
14.5 Writing tests For Phoenix
The test runner displayed this error instead of the error shown in Listing 14.25:
1) test GET / (AuctionWeb.PageControllerTest)
apps/auction_web/test/auction_web/controllers/page_controller_test.exs:4
** (ArgumentError) cookie store expects conn.secret_key_base to be set
code: conn = get(conn, "/")
stacktrace: ...
Fix by adding a secret_key_base
to the test.exs
auction web endpoint:
config :auction_web, AuctionWeb.Endpoint,
secret_key_base: "Dzlf6LT5j14IS16DQHviw2OuMiNjHBGGkrCDe8fMbR41Us/RMCPQfWzkigWa7Ags"
Next, the test runner displayed this error instead of that shown in Listing 14.25:
1) test GET / (AuctionWeb.PageControllerTest)
apps/auction_web/test/auction_web/controllers/page_controller_test.exs:4
** (DBConnection.OwnershipError) cannot find ownership process for #PID<0.627.0>.
When using ownership, you must manage connections in one
of the four ways:
* By explicitly checking out a connection
* By explicitly allowing a spawned process
* By running the pool in shared mode
* By using :caller option with allowed process
...
Fix by modifying auction_web/test/support/conn_case.ex
to
checkout Auction.Repo
in a sandbox and set its mode to shared.
Replace the setup _tags do
section with:
setup tags do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Auction.Repo)
unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(Auction.Repo, {:shared, self()})
end
{:ok, conn: Phoenix.ConnTest.build_conn()}
end