lib/ref.ex

defmodule Reactive.Ref do
  @moduledoc """
  Wrapper to work with reactive state directly

  ## Example

      iex> use Reactive
      iex> ref = Ref.new(0) #PID<0.204.0>
      iex> Ref.get(ref)
      0
      iex> Ref.set(ref, 1)
      :ok
      iex> Ref.get(ref)
      1
  """

  require Reactive

  @doc """
  Create a reactive process

  ## Example

      iex> use Reactive
      iex> ref = Ref.new(0)
      iex> Ref.get(ref)
      0
  """
  def new(value, opts \\ []) do
    Reactive.new(fn _ -> value end, opts)
  end

  @doc """
  Set the state for a reactive process

  ## Example

      iex> use Reactive
      iex> ref = Ref.new(0)
      iex> Ref.set(ref, 1)
      :ok
      iex> Ref.get(ref)
      1
  """
  def set(pid, value) do
    existing = Reactive.get_cached(pid)

    if value !== existing do
      Reactive.set(pid, fn _ -> value end)
    end
  end

  @doc """
  Set the state for a reactive process through a function that receives the old value

  ## Example

      iex> use Reactive
      iex> Reactive.Supervisor.ensure_started()
      iex> ref = Ref.new(0)
      iex> Ref.set_fn(ref, fn state -> state + 1 end)
      :ok
      iex> Ref.get(ref)
      1
  """
  def set_fn(pid, value) when is_function(value) do
    existing = Reactive.get(pid)
    new_value = value.(existing)

    if new_value !== existing do
      Reactive.set(pid, fn _ -> new_value end)
    end
  end

  @doc """
  Retrieve the state of a reactive process

  ## Example

      iex> use Reactive
      iex> ref = Ref.new(0)
      iex> Ref.get(ref)
      0
  """
  def get(pid, opts \\ []) do
    Reactive.get(pid, opts)
  end
end