Current section
Files
Jump to
Current section
Files
lib/channel_manager.ex
defmodule PhxInPlace.ChannelManager do
@moduledoc false
import Number.{Currency, Percentage, Delimit}
defmacro __using__(params) do
quote do
@repo Application.get_env(:phx_in_place, :repo)
import PhxInPlace.ChannelManager
@doc """
Handles the pip_update event when called from client side JS
and updates the database for the applicable record. See the Phoenix Channels documents for details.
Called from client-side JS via channel.push:
channel.push('pip_update', update_values)
## Example
def handle_in("pip_update", %{hash: "HashKeyHere", changes: {name: "New Name"}}, socket)
replies with {:ok, %{msg, value}} when successful or {:error, msg} when the update errors out so that any additional client side callbacks can be processed.
"""
def handle_in("pip_update", payload, socket) do
case verify_and_update(@repo, payload) do
{:ok, result} ->
{:reply, {:ok, %{msg: "update successful", value: result}}, socket}
{:error, msg} ->
{:reply, {:error, %{msg: msg}}, socket}
end
end
end
end
@doc """
Called from handle_in('pip_update', payload, socket). Verifies the hashed values in the phx_in_place field element and processes the
database update if successful.
"""
def verify_and_update(repo, payload) do
with\
{:ok, token_values} <- verify_token(payload["hash"]),
{:ok, changes} <- cleanChangeValues(payload["changes"], payload["formatting"]),
{:ok, resp} <- do_update(repo, token_values, changes)
do
process_result(resp, changes, payload["formatting"])
else
{:error, msg} -> {:error, msg}
end
end
# checks that a valid signed token has been passed in with the update request.
defp verify_token(token) do
decoded_token = Application.get_env(:phx_in_place, :tokenHandler).verify(Application.get_env(:phx_in_place, :endpoint), "user salt", token, max_age: 886400)
case decoded_token do
{:error, msg} -> {:error, msg}
_ -> {:ok, Tuple.to_list(decoded_token) |> Enum.at(1) |> Enum.at(0)}
end
end
defp cleanChangeValues(attrs, format) when is_map(attrs) == false, do: {:error, "invalid attrs tuple received in cleanChangeValues"}
defp cleanChangeValues(attrs, format) when is_nil(format) do
attrs = convert_keys_to_atoms(%{}, attrs) |> Enum.at(0)
{:ok, attrs}
end
# TODO: Add handlers for other types display_as options
defp cleanChangeValues(attrs, format) do
attrs = case format do
"number_to_currency" -> strip_formatting(attrs)
"number_to_percentage" -> strip_formatting(attrs)
_ -> {:ok, convert_keys_to_atoms(%{}, attrs) |> Enum.at(0)}
end
#attrs are returned as list containing a key value pair ie - [{"input_quote", "72.34"}]
attrs = convert_keys_to_atoms(%{}, attrs) |> Enum.at(0)
{:ok, attrs}
end
# TODO: Improve regex to filter out all non-numeric values
defp strip_formatting(attrs) do
result = for {k,v} <- attrs do
{k, String.replace(v, ~r/[$#W%\s]/, "")}
end
end
# updates the repo defined in the pip config
defp do_update(repo, hash_values, attrs) do
{struct, id} = hash_values
response = Application.get_env(:phx_in_place, :updateHandler).update_repo(struct, id, attrs)
end
# inserts the updated value into the changes pair so that it can be passed back to the client for updating
defp process_result(resp, attrs, format) do
{field, value} = attrs |> Enum.at(0)
resp
|> Map.get(field)
|> format_value(format) #get back {:ok, foramtted_value} pair
end
# TODO: This is copied from phx_in_place - move to helper and share code?
defp format_value(value, format) do
#handles case where nil value passed through for options
# options = if is_nil(options), do: [], else: options
options = []
case format do
"number_to_currency" -> {:ok, number_to_currency(value, options)}
"number_to_percentage" -> {:ok, number_to_percentage(value, options)}
"number_to_delimited" -> {:ok, number_to_delimited(value, options)}
_ -> {:ok, value}
end
end
#converts keys passed in from Javascript from strings to atoms if keys are strings
# assumes only one pair is provided....
defp convert_keys_to_atoms(map, kvpairs) do
for {k,v} <- kvpairs do
case is_binary(k) do
true -> Map.put(map, String.to_atom(k), v)
false -> Map.put(map, k, v)
end
end
end
end