Pipe the response of LiveViews and GenServers
Instead of wrapping your result in a tuple at the end of a callback
def mount(_params, _session, socket) do
socket =
socket
|> assign(:posts, Blog.list_posts())
{:ok, socket}
endjust pipe it all the way down:
def mount(_params, _session, socket) do
socket
|> assign(:posts, Blog.list_posts())
|> ok()
endInstallation
With Igniter
If you use Igniter, run:
mix igniter.install reply
This adds reply to your dependencies and imports it in html_helpers of your web module.
Manually
Add reply to your list of dependencies in your mix.exs:
def deps do
[
{:reply, "~> 1.2"}
]
endand import it in your web module lib/my_app_web.ex inside html_helpers, so it is available in every LiveView and LiveComponent:
defp html_helpers do
quote do
# HTML escaping functionality
import Phoenix.HTML
+ import Reply
...In a GenServer, simply import Reply in the module.
Usage
Phoenix LiveView
Use ok/1 in mount and noreply/1 in handle_* callbacks:
def mount(_params, _session, socket) do
socket
|> assign(:posts, Blog.list_posts())
|> ok()
end
def handle_params(%{"id" => id}, _uri, socket) do
socket
|> assign(:post, Blog.get_post!(id))
|> noreply()
endOptions for mount can be passed to ok/2:
def mount(_params, _session, socket) do
socket
|> assign(:posts, Blog.list_posts())
|> ok(temporary_assigns: [posts: []])
endTo reply to the client in handle_event, use reply/2:
def handle_event("load", %{"id" => id}, socket) do
post = Blog.get_post!(id)
socket
|> assign(:post, post)
|> reply(%{title: post.title})
endGenServer
The same functions work with the state of a GenServer:
defmodule Stack do
use GenServer
import Reply
def init(elements) do
elements
|> ok()
end
def handle_call(:pop, _from, [head | tail]) do
tail
|> reply(head)
end
def handle_cast({:push, element}, state) do
[element | state]
|> noreply()
end
endGenServer actions (a timeout, :hibernate or {:continue, term}) are passed as the last argument:
def init(args) do
args
|> ok({:continue, :load})
end
def handle_cast({:push, element}, state) do
[element | state]
|> noreply({:continue, :persist})
end
def handle_call(:pop, _from, [head | tail]) do
tail
|> reply(head, :hibernate)
endand stop/2 or stop/3 stop the process:
def handle_info(:shutdown, state) do
state
|> stop(:normal)
end
def handle_call(:shutdown, _from, state) do
state
|> stop(:normal, :ok)
endOverview
| Function | Returns | LiveView | GenServer |
|---|---|---|---|
ok(socket_or_state) | {:ok, socket_or_state} | ✓ | ✓ |
ok(socket_or_state, opts_or_action) | {:ok, socket_or_state, opts_or_action} | ✓ | ✓ |
noreply(socket_or_state) | {:noreply, socket_or_state} | ✓ | ✓ |
noreply(state, action) | {:noreply, state, action} | ✓ | |
reply(socket_or_state, payload) | {:reply, payload, socket_or_state} | ✓ | ✓ |
reply(state, payload, action) | {:reply, payload, state, action} | ✓ | |
stop(state, reason) | {:stop, reason, state} | ✓ | |
stop(state, reason, payload) | {:stop, reason, payload, state} | ✓ |
Phoenix Channels
Phoenix.Channel defines its own reply/2. If you import Reply in a channel, exclude it to avoid an ambiguous call:
import Reply, except: [reply: 2]License
Reply is released under the MIT License.