Skip to content

Instantly share code, notes, and snippets.

@roehst

roehst/todo.ex Secret

Created October 4, 2021 16:40
Show Gist options
  • Save roehst/33a8b3b9c1f300fdc405f5076e168191 to your computer and use it in GitHub Desktop.
Save roehst/33a8b3b9c1f300fdc405f5076e168191 to your computer and use it in GitHub Desktop.
Full Dependency Injection in Elixir
defmodule Todo do
defstruct [:add_todo, :get_todos, :terminate]
defmodule Repo do
def all(_) do
throw(:not_implemented)
end
def get(_, _) do
throw(:not_implemented)
end
end
def unimplemented do
%__MODULE__{
add_todo: fn _todo -> throw(:not_implemented) end,
get_todos: fn -> throw(:not_implemented) end,
terminate: fn -> throw(:not_implemented) end
}
end
def loop(%{todos: %{} = todos, next_id: next_id} = state) do
receive do
{:add_todo, todo} ->
loop(%{state | todos: Map.put(todos, next_id + 1, todo), next_id: next_id + 1})
{:get_todos, from} ->
send(from, {:todos, todos})
loop(state)
end
end
def as_ecto() do
%__MODULE__{
terminate: nil,
add_todo: fn todo ->
Repo.insert(Todo, todo)
end,
get_todos: fn ->
Repo.all(Todo)
end
}
end
def as_process() do
pid = spawn(__MODULE__, :loop, [%{todos: %{}, next_id: 0}])
%__MODULE__{
terminate: fn -> Process.exit(pid, :normal) end,
add_todo: fn todo -> send(pid, {:add_todo, todo}) end,
get_todos: fn ->
send(pid, {:get_todos, self()})
receive do
{:todos, todos} -> todos
end
end
}
end
def demo(app) do
app.add_todo.("Hey")
app.add_todo.("Ho")
todos = app.get_todos.()
IO.inspect(todos)
end
end
@roehst
Copy link
Author

roehst commented Oct 4, 2021

What's nice is demo/1 knows nothing about the underlying implementation.

All behavior is abstracted behind functions in the Todo struct: the whole application instance is a closure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment