View Source Rephex.Selector behaviour (rephex v0.2.0)

Rephex.Selector is a module that functions similarly to a view in a database. It manages display data derived from actual data, keeping the presentation layer in sync with changes to the underlying data.

Selectors automatically update their values whenever the associated real data changes, effectively decoupling the logic for updating display data from the logic for updating real data.

By using args/1 callback, you can specify the dependencies of the selector and avoid unnecessary updates.

Example

defmodule RephexPg.StateWithSelector do
  alias RephexPg.StateWithSelector.SelectDoubleV
  alias Rephex.Selector

  @initial_state %{
    v: 1,
    double_v: Selector.new(SelectDoubleV)
  }

  use Rephex.State, initial_state: @initial_state

  # mutators --------------------------------

  def set_v(socket, v) do
    put_state_in(socket, [:v], v)
  end

  # selectors --------------------------------

  defmodule SelectDoubleV do
    @behaviour Rephex.Selector

    @impl true
    def args(state), do: {state.v}

    @impl true
    def eval({v}), do: v * 2
    # if state.v is not changed, eval/1 will not be called.
  end
end

socket = RephexPg.StateWithSelector.set_v(socket, 2)
# socket.assigns.rpx.v = 2
# socket.assigns.rpx.double_v.value == 4

Selector dependent on other selectors

If a selector depends on other selectors, you can specify the dependencies.

Example

defmodule RephexPg.StateWithSelector do
  alias RephexPg.StateWithSelector.{SelectDoubleV, SelectQuadV}
  alias Rephex.Selector

  @initial_state %{
    v: 1,
    double_v: Selector.new(SelectDoubleV),
    quad_v: Selector.new(SelectQuadV)
  }

  use Rephex.State, initial_state: @initial_state

  def set_v(socket, v) do
    put_state_in(socket, [:v], v)
  end

  defmodule SelectDoubleV do
    # ...
  end

  defmodule SelectQuadV do
    @behaviour Rephex.Selector

    @impl true
    def deps(), do: [SelectDoubleV]

    @impl true
    def args(state), do: {state.double_v.value}

    @impl true
    def eval({dv}), do: dv * 2
  end
end

socket = RephexPg.StateWithSelector.set_v(socket, 2)
# socket.assigns.rpx.v = 2
# socket.assigns.rpx.double_v.value == 4
# socket.assigns.rpx.quad_v.value == 8

Summary

Functions

Create a new selector. Selector should be in root of the state.

Types

@type args() :: tuple()
@type dependencies() :: [module()]
@type state() :: map()
@type t() :: t(any())
@type t(value) :: %Rephex.Selector{args_cache: args(), module: module(), value: value}
@type value() :: any()

Callbacks

@callback args(state()) :: args()
@callback deps() :: [module()]
@callback eval(args()) :: value()

Functions

Create a new selector. Selector should be in root of the state.

Example

defmodule RephexPg.StateWithSelector do
  alias RephexPg.StateWithSelector.SelectDoubleV
  alias Rephex.Selector

  @initial_state %{
    v: 1,
    double_v: Selector.new(SelectDoubleV)
  }

  use Rephex.State, initial_state: @initial_state
end