elixir-todo-list/todo_list.ex

162 lines
3.6 KiB
Elixir

defmodule ServerProcess do
def start(callback_module) do
spawn(fn ->
initial_state = callback_module.init()
loop(callback_module, initial_state)
end)
end
defp loop(callback_module, current_state) do
receive do
{:call, request, caller} ->
{response, new_state} = callback_module.handle_call(request, current_state)
send(caller, {:response, response})
loop(callback_module, new_state)
{:cast, request} ->
new_state = callback_module.handle_cast(request, current_state)
loop(callback_module, new_state)
end
end
def call(pid, message) do
send(pid, {:call, message, self()})
receive do
{:response, response} ->
response
end
end
def cast(pid, message) do
send(pid, {:cast, message})
nil
end
end
defmodule TodoServer do
def init(entries) do
TodoList.new(entries)
end
def handle_call({:entries, date}, todo_list) do
entries = TodoList.entries(todo_list, date)
{entries, todo_list}
end
def handle_call(invalid_message, todo_list) do
IO.puts("Invalid message: #{IO.inspect(invalid_message)}")
todo_list
end
def handle_cast({:add, entry}, todo_list) do
TodoList.add(todo_list, entry)
end
def handle_cast({:update, id, update_fun}, todo_list) do
TodoList.update(todo_list, id, update_fun)
end
def handle_cast({:delete, id}, todo_list) do
TodoList.delete(todo_list, id)
end
def handle_cast(invalid_message, todo_list) do
IO.puts("Invalid message: #{IO.inspect(invalid_message)}")
todo_list
end
def add(pid, entry) do
ServerProcess.cast(pid, {:add, entry})
end
def entries(pid, date) do
ServerProcess.call(pid, {:entries, date})
end
def update(pid, id, update_fun) do
ServerProcess.cast(pid, {:update, id, update_fun})
end
def delete(pid, id) do
ServerProcess.cast(pid, {:delete, id})
end
end
defmodule TodoList do
defstruct entries: %{}, next_id: 1
def new(entries \\ []) do
Enum.reduce(entries, %__MODULE__{}, &add(&2, &1))
end
def add(
%__MODULE__{entries: entries, next_id: next_id} = todo_list,
%{date: _, title: _} = entry
) do
new_entry = Map.put(entry, :id, next_id)
new_entries = Map.put(entries, next_id, new_entry)
%__MODULE__{todo_list | entries: new_entries, next_id: next_id + 1}
end
def entries(%__MODULE__{} = todo_list, date) do
todo_list.entries
|> Map.filter(fn {_, entry} -> entry.date == date end)
|> Map.values()
end
def update(%__MODULE__{entries: entries} = todo_list, id, update_fun)
when is_function(update_fun, 1) do
case Map.fetch(entries, id) do
:error ->
todo_list
{:ok, entry} ->
new_entry = update_fun.(entry)
new_entries = Map.put(entries, id, new_entry)
%__MODULE__{todo_list | entries: new_entries}
end
end
def delete(%__MODULE__{entries: entries} = todo_list, id) when is_number(id) do
new_entries = Map.delete(entries, id)
%__MODULE__{todo_list | entries: new_entries}
end
end
defmodule TodoList.CSVImporter do
def import(path) do
path
|> File.stream!()
|> Stream.map(&parse_line/1)
|> TodoList.new()
end
defp parse_line(line) do
line
|> String.trim()
|> String.split(",")
|> create_entry()
end
defp create_entry([date, title]) do
final_date = parse_date(date)
%{date: final_date, title: title}
end
defp parse_date(string) do
[year, month, day] = string |> String.split("-") |> Enum.map(&String.to_integer/1)
Date.new!(year, month, day)
end
end