Packages

A server-side backend library for building reactive JavaScript frontends over Phoenix Channels.

Current section

Files

Jump to
hyphen lib hyphen.ex
Raw

lib/hyphen.ex

defmodule Hyphen do
@moduledoc """
Behaviour for Hyphen backends.
A Hyphen backend defines the data shape sent to a JS frontend via
WebSocket (Phoenix Channels) and handles typed actions received back.
"""
@type socket :: Phoenix.Socket.t()
@callback mount_static(Plug.Conn.t(), params :: map()) :: map()
@callback mount(socket(), params :: map()) ::
{:ok, socket()} | {:ok, socket(), keyword()} | {:error, map()}
@callback handle_params(map(), socket()) :: {:noreply, socket()}
@callback handle_event(String.t(), map(), socket()) ::
{:noreply, socket()} | {:reply, {:ok | :error, map()}, socket()}
@callback handle_info(term(), socket()) :: {:noreply, socket()}
@callback handle_task(term(), term(), socket()) :: {:noreply, socket()}
@doc false
@callback __hyphen_opts__ :: keyword()
@optional_callbacks handle_task: 3
defmacro __using__(opts) do
quote do
@behaviour unquote(__MODULE__)
import unquote(__MODULE__)
@doc false
def __hyphen_opts__ do
unquote(opts)
end
def handle_params(_params, socket) do
{:noreply, socket}
end
def handle_info(_msg, socket) do
{:noreply, socket}
end
defoverridable __hyphen_opts__: 0, handle_params: 2, handle_info: 2
end
end
@spec assign(socket(), list() | map()) :: socket()
def assign(socket, kvs) when is_list(kvs) when is_map(kvs) do
Hyphen.Channel.assign_changes(socket, kvs)
end
@spec assign(socket(), atom(), term()) :: socket()
def assign(socket, key, value) do
Hyphen.Channel.assign_changes(socket, [{key, value}])
end
@spec unassign(socket(), atom()) :: socket()
def unassign(socket, key) do
Hyphen.Channel.unassign(socket, key)
end
@spec source_assign(socket(), atom(), term(), keyword()) :: socket()
def source_assign(socket, key, value, params \\ []) do
Hyphen.Channel.assign_source(socket, key, value, Map.new(params))
end
# TODO(doc): Keywords are always transformed into a map when passed to the
# source module
@spec source_update(socket(), atom(), map() | list() | (term() -> {:ok, term()} | {:stop, term()})) :: socket()
def source_update(socket, key, params) when is_map(params) when is_list(params) do
Hyphen.Channel.update_source(socket, key, Map.new(params))
end
def source_update(socket, key, fun) when is_function(fun, 1) do
Hyphen.Channel.update_source(socket, key, fun)
end
@spec source_refresh(socket(), atom()) :: socket()
def source_refresh(socket, key) do
# Force rendering the source by updating with a no-op function
source_update(socket, key, &{:ok, &1})
end
end