Packages

A reactive programming library for Elixir. The purpose of this library is to experiment in an academic context.

Current section

Files

Jump to
reactivity lib signal_source.ex
Raw

lib/signal_source.ex

defmodule Reactivity.Signal.Source do
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.
defstruct name: nil, 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, name: n}) do
# Send first tick already.
Process.send_after(self(), :tick, t)
{:ok, %Source{last_value: v, generator: f, sleep: t, children: [], name: n}}
end
#############
# Callbacks #
#############
def handle_cast(m, state) do
Logger.debug "Cast: #{inspect m}"
{:noreply, state}
end
def handle_call({:add_child, pid}, _from, state) do
Logger.debug "Adding child to #{state.name}"
new_state = %{state | children: [pid | state.children]}
{:reply, :ok, new_state}
end
def handle_call(m, from, state) do
{:reply, :ok, state}
end
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}) 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_info(m, state) do
Logger.debug "Info: #{inspect m}"
{:noreply, state}
end
###########
# Private #
###########
#############
# 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}, name: name)
Registry.add_source({:signal, :source, pid}, name)
{:ok, {:signal, :source, pid}}
end
def add_child(parent, {:signal, _, child}) do
GenServer.call(parent, {:add_child, child})
end
end