Current section
Files
Jump to
Current section
Files
lib/signal/signal_source.ex
defmodule Reactivity.Signal.Source do
@doc """
A source signal is used to tie the outside world on the reactive world. A
source signals has no dependencies.
"""
use GenServer
require Logger
alias Reactivity.Signal.Source
alias Reactivity.Registry
# last_value: The last generated value.
# generator : The lambda that will generate the next value.
# sleep : The time between each new signal.
# children : The children's pid's.
defstruct last_value: nil, generator: nil, sleep: 10, children: []
#############
# GenServer #
#############
def start_link(args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end
def init(%{val: v, gen_func: f, sleep: t}) do
# Send first tick already.
Process.send_after(self(), :tick, t)
{:ok, %Source{last_value: v, generator: f, sleep: t, children: []}}
end
#############
# Callbacks #
#############
@doc """
Adds a child to the node. Whenever this node receives a new value it
will "tick" all its children with the new value.
"""
def handle_call({:add_child, pid}, _from, state) do
new_state = %{state | children: [pid | state.children]}
{:reply, :ok, new_state}
end
@doc """
Is called when the parent has sent us a new value. If this happens we need to
contact all our children as well withour newly computed value.
"""
def handle_info(:tick, state) do
# Compute the new value
new_value = state.generator.(state.last_value)
# Tick all my children.
Enum.map(state.children, fn(c) -> send(c, {:tick, new_value, {:signal, :source, self()}}) end)
# Compute the new state.
new_state = %{state | last_value: new_value}
Process.send_after(self(), :tick, state.sleep)
{:noreply, new_state}
end
def handle_call({:last_value}, _from, state) do
{:reply, {:ok, state.last_value}, state}
end
#############
# Interface #
#############
@doc """
Creates a new source signal based on a function.
It's not the point to be used in programs, merely as a simulation.
"""
def new(gen_func, default, time, name) do
{:ok, pid} = GenServer.start_link(__MODULE__, %{val: default, gen_func: gen_func, sleep: time}, name: name)
Registry.add_source({:signal, :source, pid}, name)
{:ok, {:signal, :source, pid}}
end
end