lib/phoenix/live_editable/view_cache.ex

defmodule Phoenix.LiveEditable.ViewCache do

  @moduledoc """
  ViewCache

  What it does:
  - starts an agent, attached to the socket
  - when the socket dies, the ViewCache is wiped out

  Purpose:
  - to cache config data
  - fast and simple config storage

  Rationale:
  - minimize the number of params in a HEEX tag
  - can only use string and numeric values in HEEX assigns - not lists, maps etc.

  Notes:
  - depends on Phoenix.LiveEditable.Registry
  - start the application to run in iex: `iex -S mix`

  Usage:

      import Phoenix.LiveEditable.ViewCache
      vc_new(name)

  """

  defstruct [
    # editable data - can be nil
    ple_data: nil,
    # form type: <text | select | multi-select | radio | ...>
    ple_type: "text",
    # render mode: <anchor | focus>
    ple_mode: "anchor",
    # display style: <inline | popup>
    ple_style: "inline",
    # storage for misc config attributes
    ple_store: %{},
    # submit action: anything
    ple_action: "ple-default-save",
    # appname: for looking up config settings
    ple_appname: nil,
    # target: pid of view
    ple_viewpid: nil,
    # data source: place where state is managed <component | view>
    ple_datasrc: "component",
    # handler module - can reset in view or tag
    ple_handler: Phoenix.LiveEditable.ComponentHandler.Default,
    # interface module: see Phoenix.LiveEditable.Interface.*
    ple_interface: Phoenix.LiveEditable.Interface.Milligram,
  ]

  @doc """
  vc_new

  Generate a new ViewCache populated with a ViewCache struct.

  This cache is meant to be view-specific, started when the view is created,
  and killed when the view process is destroyed.
  """
  def vc_new(name) when is_binary(name) do
    initial_value = %Phoenix.LiveEditable.ViewCache{}
    vianame = vianame(name)
    Agent.start_link(fn() -> initial_value end, name: vianame)
    name
  end

  def vc_new(socket) do
    socket.id |> vc_new()
  end

  def vc_get(name) when is_binary(name) do
    vianame = vianame(name)
    Agent.get(vianame, &(&1))
  end

  def vc_get(socket) do
    socket.id |> vc_get()
  end

  def vc_get(name, key) when is_binary(name) do
    vc_get(name)
    |> Map.get(key)
  end

  def vc_get(socket, key) do
    socket.id |> vc_get(key)
  end

  def vc_put(name, key, data) when is_binary(name) do
    vianame = vianame(name)
    Agent.update(vianame, &(Map.put(&1, key, data)))
  end

  def vc_put(socket, key, data) do
    socket.id |> vc_put(key, data)
  end

  def vc_putval(name, key, data) when is_binary(name) do
    unless data == nil do
      vc_put(name, key, data)
    end
  end

  def vc_putval(socket, key, data) do
    unless data == nil do
      vc_put(socket, key, data)
    end
  end

  def vc_merge(name, map) when is_binary(name) do
    newmap = name |> vc_get() |> Map.merge(map)
    vianame = vianame(name)
    Agent.update(vianame, fn(_) -> newmap end)
  end

  def vc_merge(socket, map) do
    socket.id |> vc_merge(map)
  end

  def vc_mergeval(name, map) when is_binary(name) do
    vc_merge(name, strip_nils(map))
  end

  def vc_mergeval(socket, map) do
    socket.id |> vc_mergeval(map)
  end

  # --------------------------------------------------------

  defp vianame(name) do
    {:via, Registry, {Phoenix.LiveEditable.Registry, name}}
  end

  defp strip_nils(%_{} = struct) do
    struct
    |> Map.from_struct()
    |> strip_nils()
  end

  defp strip_nils(map) when is_map(map) do
    map
    |> Enum.reject(fn({_, v}) -> is_nil(v) end)
    |> Map.new()
  end

end