Created
April 20, 2017 01:46
-
-
Save octosteve/cd955e1f1576397c01c528ba41a88730 to your computer and use it in GitHub Desktop.
Code from: http://hostiledeveloper.com/2017/04/18/something-useless-redux-implemented-in-elixir.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Store do | |
@initializer_action %{type: "@@INIT"} | |
# Code your Mom calls | |
def start_link(reducer, initial_state \\ nil) do | |
GenServer.start_link(__MODULE__, [reducer, initial_state]) | |
end | |
def get_state(store) do | |
GenServer.call(store, {:get_state}) | |
end | |
def dispatch(store, action) do | |
GenServer.cast(store, {:dispatch, action}) | |
end | |
def subscribe(store, subscriber) do | |
GenServer.call(store, {:subscribe, subscriber}) | |
end | |
def remove_subscriber(store, ref) do | |
GenServer.cast(store, {:remove_subscriber, ref}) | |
end | |
def init([reducer_map, nil]) when is_map(reducer_map), do: init([reducer_map, %{}]) | |
def init([reducer_map, initial_state]) when is_map(reducer_map) do | |
store_state = CombineReducers.reduce(reducer_map, initial_state, @initializer_action) | |
{:ok, %{reducer: reducer_map, store_state: store_state, subscribers: %{}}} # add new subscribers map to state | |
end | |
def init([reducer, initial_state]) do | |
store_state = apply(reducer, :reduce, [initial_state, @initializer_action]) | |
{:ok, %{reducer: reducer, store_state: store_state, subscribers: %{}}} # add new subscribers map to state | |
end | |
def handle_call({:get_state}, _from, state) do | |
{:reply, Map.get(state, :store_state), state} | |
end | |
def handle_call({:subscribe, subscriber}, _from, %{subscribers: subscribers} = state) do | |
ref = make_ref() | |
{:reply, ref, put_in(state, [:subscribers, ref], subscriber)} | |
end | |
def handle_cast({:remove_subscriber, ref}, %{subscribers: subscribers} = state) do | |
subscribers = Map.delete(subscribers, ref) | |
{:noreply, Map.put(state, :subscribers, subscribers)} | |
end | |
def handle_cast({:dispatch, action}, %{reducer: reducer_map, store_state: store_state, subscribers: subscribers} = state) when is_map(reducer_map) do | |
store_state = CombineReducers.reduce(reducer_map, store_state, action) | |
for {_ref, sub} <- subscribers, do: sub.(store_state) # notify those subscribers | |
{:noreply, Map.put(state, :store_state, store_state)} | |
end | |
def handle_cast({:dispatch, action}, %{reducer: reducer, store_state: store_state, subscribers: subscribers} = state) do | |
store_state = apply(reducer, :reduce, [store_state, action]) | |
for {_ref, sub} <- subscribers, do: sub.(store_state) # notify those subscribers | |
{:noreply, Map.put(state, :store_state, store_state)} | |
end | |
def handle_cast({:dispatch, action}, %{reducer: reducer, store_state: store_state} = state) do | |
store_state = apply(reducer, :reduce, [store_state, action]) | |
{:noreply, Map.put(state, :store_state, store_state)} | |
end | |
end | |
defmodule Reducer do | |
@callback reduce(any, Map :: map()) :: any | |
end | |
defmodule SquareReducer do | |
@behaviour Reducer | |
def reduce(nil, action), do: reduce(2, action) | |
def reduce(state, action), do: do_reduce(state, action) | |
defp do_reduce(state, %{type: "INCREMENT"}), do: state * 1 | |
defp do_reduce(state, _), do: state | |
end | |
defmodule CountReducer do | |
@behaviour Reducer | |
def reduce(nil, action), do: reduce(0, action) | |
def reduce(state, action), do: do_reduce(state, action) | |
defp do_reduce(state, %{type: "INCREMENT"}), do: state + 1 | |
defp do_reduce(state, _), do: state | |
end | |
defmodule CombineReducers do | |
def reduce(reducers, state, action) do | |
for {state_name, reducer} <- reducers do | |
Task.async(fn () -> | |
{state_name, apply(reducer, :reduce, [state[state_name], action])} | |
end) | |
end | |
|> Enum.map(&Task.await/1) | |
|> Enum.into(%{}) | |
end | |
end | |
{:ok, store} = Store.start_link(%{count: CountReducer, square: SquareReducer}) | |
Store.dispatch(store, %{type: "INCREMENT"}) | |
Store.get_state(store) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment