Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to Bandit instead of Cowboy #217

Merged
merged 3 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ npm-debug.log
/priv/static/cache_manifest.json

# Ignore assets that are produced by build tools.
/priv/static/*
#/priv/static/*

# Ignore this noise:
.DS_Store
61 changes: 46 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,16 @@ When you run the command:
elixir -v
```

You should expect to see output similar to the following:
At the time of writing, you should expect to see output similar to the following:

```elixir
Elixir 1.15.4 (compiled with Erlang/OTP 26)
Elixir 1.17.3 (compiled with Erlang/OTP 26)
```

This informs us we are using `Elixir version 1.15.4`
This informs us we are using `Elixir version 1.17.3`
which is the _latest_ version at the time of writing.
Some of the more advanced features of Phoenix 1.7 during compilation time require elixir
`1.14` although the code will work in previous versions.
`1.17` although the code will work in previous versions.

<br />

Expand All @@ -171,7 +171,7 @@ mix phx.new -v
You should see something similar to the following:

```sh
Phoenix installer v1.7.7
Phoenix installer v1.7.14
```

If you have an earlier version,
Expand Down Expand Up @@ -676,7 +676,9 @@ defmodule CounterWeb.Counter do
@topic "live"

def mount(_session, _params, socket) do
CounterWeb.Endpoint.subscribe(@topic) # subscribe to the channel
if connected?(socket) do
CounterWeb.Endpoint.subscribe(@topic) # subscribe to the channel
end
{:ok, assign(socket, :val, 0)}
end

Expand Down Expand Up @@ -721,12 +723,28 @@ The second change is on
[Line 7](https://github.com/dwyl/phoenix-liveview-counter-tutorial/blob/664228ac564a79a0dd92d06857622c1ba22cda71/lib/counter_web/live/counter.exL7)
where the
[`mount/3`](https://github.com/dwyl/phoenix-liveview-counter-tutorial/blob/d3cddb14dff911a377d0e41b916cfe57b0557606/lib/counter_web/live/counter.ex#L6)
function now creates a subscription to the `@topic`:
function now creates a subscription to the `@topic` when the socket is connected:

```elixir
CounterWeb.Endpoint.subscribe(@topic) # subscribe to the channel topic
```

When the client (the browser) connects to the Phoenix server,
a websocket connection is established.
The interface is the `socket` and
we know that the socket is connected
when the [connected?/1](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#connected?/1) function returns `true`.
This is why we only subscribe to the channel
when the socket is connected.
Why do we do this?
Because a websocket connection starts with an HTTP request
and HTTP is a stateless protocol.
So when the client connects to the server,
the server does not know if the client is already connected to the server.
Once the websocket connection is established,
the server knows that the client is connected,
thus `connected?(ocket) == true`.

Each client connected to the App
subscribes to the `@topic`
so when the count is updated on any of the clients,
Expand Down Expand Up @@ -1371,8 +1389,9 @@ defmodule CounterWeb.Counter do
@topic Count.topic

def mount(_params, _session, socket) do
PubSub.subscribe(Counter.PubSub, @topic)

if connected?(socket) do
PubSub.subscribe(Counter.PubSub, @topic)
end
{:ok, assign(socket, val: Count.current()) }
end

Expand Down Expand Up @@ -1567,6 +1586,12 @@ but not here) so the rest of the code goes into
2. We handle Presence updates and use the current count, adding joiners and
subtracting leavers to calculate the current numbers 'present'. We do that
in a pattern matched `handle_info`.
Notice that since we populate the socket's state in the `mount/3` callback,
and compute the Presence there, we need to remove the connected client
from the joins in the `handle_info` callback.
We use `Map.delete` to remove the client from the joins.
This works because the client is identified by the socket's `id` and Presence
process returns a map whose key value is the `socket.id`.
3. We publish the additional data to the client in `render`

```diff
Expand All @@ -1580,14 +1605,19 @@ defmodule CounterWeb.Counter do
+ @presence_topic "presence"

def mount(_params, _session, socket) do
PubSub.subscribe(Counter.PubSub, @topic)

+ Presence.track(self(), @presence_topic, socket.id, %{})
+ CounterWeb.Endpoint.subscribe(@presence_topic)
+
+ initial_present =
+ Presence.list(@presence_topic)
+ |> map_size
if connected?(socket) do
PubSub.subscribe(Counter.PubSub, @topic)

+ Presence.track(self(), @presence_topic, socket.id, %{})
+ CounterWeb.Endpoint.subscribe(@presence_topic)
+
+ Presence.list(@presence_topic)
+ |> map_size
+ else
+ 0
+ end

+ {:ok, assign(socket, val: Count.current(), present: initial_present) }
- {:ok, assign(socket, val: Count.current()) }
Expand All @@ -1609,6 +1639,7 @@ defmodule CounterWeb.Counter do
+ %{event: "presence_diff", payload: %{joins: joins, leaves: leaves}},
+ %{assigns: %{present: present}} = socket
+ ) do
+ {_, joins} = Map.pop!(joins, socket.id, %{})
+ new_present = present + map_size(joins) - map_size(leaves)
+
+ {:noreply, assign(socket, :present, new_present)}
Expand Down
1 change: 1 addition & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Config
# Configures the endpoint
config :counter, CounterWeb.Endpoint,
url: [host: "localhost"],
adapter: Bandit.PhoenixAdapter,
render_errors: [
formats: [html: CounterWeb.ErrorHTML, json: CounterWeb.ErrorJSON],
layout: false
Expand Down
7 changes: 5 additions & 2 deletions lib/counter_web/components/layouts/app.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
</p>
</div>
<div class="flex items-center gap-4 font-semibold leading-6 text-zinc-900">
<a href="https://github.com/dwyl/phoenix-liveview-counter-tutorial" class="hover:text-zinc-700">
<a
href="https://github.com/dwyl/phoenix-liveview-counter-tutorial"
class="hover:text-zinc-700"
>
GitHub
</a>
<a
Expand All @@ -26,4 +29,4 @@
<.flash_group flash={@flash} />
<%= @inner_content %>
</div>
</main>
</main>
29 changes: 17 additions & 12 deletions lib/counter_web/live/counter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@ defmodule CounterWeb.Counter do
alias Phoenix.PubSub
alias Counter.Presence

@topic Count.topic
@topic Count.topic()

@presence_topic "presence"

def mount(_params, _session, socket) do
PubSub.subscribe(Counter.PubSub, @topic)
Presence.track(self(), @presence_topic, socket.id, %{})

initial_present =
Presence.list(@presence_topic)
|> map_size
if connected?(socket) do
PubSub.subscribe(Counter.PubSub, @topic)
CounterWeb.Endpoint.subscribe(@presence_topic)
Presence.track(self(), @presence_topic, socket.id, %{})

CounterWeb.Endpoint.subscribe(@presence_topic)
Presence.list(@presence_topic) |> map_size()
else
0
end

{:ok, assign(socket, val: Count.current(), present: initial_present) }
{:ok, assign(socket, val: Count.current(), present: initial_present)}
end

def handle_event("inc", _, socket) do
Expand All @@ -33,11 +36,13 @@ defmodule CounterWeb.Counter do
end

def handle_info(
%{event: "presence_diff", payload: %{joins: joins, leaves: leaves}},
%{assigns: %{present: present}} = socket
) do
new_present = present + map_size(joins) - map_size(leaves)
%{event: "presence_diff", payload: %{joins: joins, leaves: leaves}},
%{assigns: %{present: present}} = socket
) do
{_, joins} = Map.pop(joins, socket.id, %{})

changes = map_size(joins) - map_size(leaves)
new_present = present + changes

{:noreply, assign(socket, :present, new_present)}
end
Expand Down
2 changes: 1 addition & 1 deletion lib/counter_web/live/counter_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule CounterComponent do
def render(assigns) do
~H"""
<div class="text-center">
<h1 class="text-4xl font-bold text-center"> Counter: <%= @val %> </h1>
<h1 class="text-4xl font-bold text-center">Counter: <%= @val %></h1>
<button phx-click="dec" class={btn("red")}>
-
</button>
Expand Down
8 changes: 4 additions & 4 deletions lib/counter_web/live/counter_state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ defmodule Counter.Count do
end

def incr() do
GenServer.call @name, :incr
GenServer.call(@name, :incr)
end

def decr() do
GenServer.call @name, :decr
GenServer.call(@name, :decr)
end

def current() do
GenServer.call @name, :current
GenServer.call(@name, :current)
end

def init(start_count) do
Expand All @@ -34,7 +34,7 @@ defmodule Counter.Count do
# Implementation (Runs in GenServer process)

def handle_call(:current, _from, count) do
{:reply, count, count}
{:reply, count, count}
end

def handle_call(:incr, _from, count) do
Expand Down
27 changes: 18 additions & 9 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ defmodule Counter.MixProject do
def project do
[
app: :counter,
version: "1.7.7",
elixir: "~> 1.14",
version: "1.8.0",
elixir: "~> 1.17",
elixirc_paths: elixirc_paths(Mix.env()),
start_permanent: Mix.env() == :prod,
aliases: aliases(),
Expand Down Expand Up @@ -42,20 +42,29 @@ defmodule Counter.MixProject do
# Type `mix help deps` for examples and options.
defp deps do
[
{:phoenix, "~> 1.7.7"},
{:phoenix_html, "~> 4.0"},
{:phoenix, "~> 1.7.14"},
{:phoenix_html, "~> 4.1"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.20.0"},
# TODO bump on release to {:phoenix_live_view, "~> 1.0.0"},
{:phoenix_live_view, "~> 1.0.0-rc.1", override: true},
{:floki, ">= 0.30.0", only: :test},
{:esbuild, "~> 0.7", runtime: Mix.env() == :dev},
{:tailwind, "~> 0.2.1", runtime: Mix.env() == :dev},
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
{:tailwind, "~> 0.2", runtime: Mix.env() == :dev},
{:heroicons,
github: "tailwindlabs/heroicons",
tag: "v2.1.1",
sparse: "optimized",
app: false,
compile: false,
depth: 1},
{:telemetry_metrics, "~> 1.0"},
{:telemetry_poller, "~> 1.0"},
{:jason, "~> 1.2"},
{:plug_cowboy, "~> 2.5"},
{:dns_cluster, "~> 0.1.1"},
{:bandit, "~> 1.5"},

# Track test coverage: github.com/parroty/excoveralls
{:excoveralls, "~> 0.18.0", only: [:test, :dev]},
{:excoveralls, "~> 0.16.0", only: [:test, :dev]}
]
end

Expand Down
Loading
Loading