Current section
Files
Jump to
Current section
Files
README.md
# Reply
**Pipe the response of LiveViews and GenServers**
[](https://github.com/sorax/reply/actions/workflows/test.yml)
[](https://github.com/sorax/reply/actions/workflows/quality.yml)
[](https://hex.pm/packages/reply)
[](https://hexdocs.pm/reply/1.2.1/Reply.html)
[](https://hex.pm/packages/reply)
[](https://github.com/sorax/reply/blob/main/LICENSE.md)
Instead of wrapping your result in a tuple at the end of a callback
```elixir
def mount(_params, _session, socket) do
socket =
socket
|> assign(:posts, Blog.list_posts())
{:ok, socket}
end
```
just pipe it all the way down:
```elixir
def mount(_params, _session, socket) do
socket
|> assign(:posts, Blog.list_posts())
|> ok()
end
```
## Installation
### With Igniter
If you use [Igniter](https://hexdocs.pm/igniter), run:
```sh
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`:
```elixir
def deps do
[
{:reply, "~> 1.2"}
]
end
```
and import it in your web module `lib/my_app_web.ex` inside `html_helpers`, so it is available in every LiveView and LiveComponent:
```diff
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:
```elixir
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()
end
```
Options for `mount` can be passed to `ok/2`:
```elixir
def mount(_params, _session, socket) do
socket
|> assign(:posts, Blog.list_posts())
|> ok(temporary_assigns: [posts: []])
end
```
To reply to the client in `handle_event`, use `reply/2`:
```elixir
def handle_event("load", %{"id" => id}, socket) do
post = Blog.get_post!(id)
socket
|> assign(:post, post)
|> reply(%{title: post.title})
end
```
### GenServer
The same functions work with the state of a GenServer:
```elixir
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
end
```
GenServer actions (a timeout, `:hibernate` or `{:continue, term}`) are passed as the last argument:
```elixir
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)
end
```
and `stop/2` or `stop/3` stop the process:
```elixir
def handle_info(:shutdown, state) do
state
|> stop(:normal)
end
def handle_call(:shutdown, _from, state) do
state
|> stop(:normal, :ok)
end
```
## Overview
| 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:
```elixir
import Reply, except: [reply: 2]
```
## License
Reply is released under the [MIT License](LICENSE.md).