2024-08-03 08:51:48 +02:00
|
|
|
defmodule TodoServer do
|
2024-08-03 09:44:00 +02:00
|
|
|
def start(entries \\ [], options \\ []) do
|
|
|
|
pid = spawn(fn -> loop(TodoList.new(entries)) end)
|
|
|
|
|
|
|
|
if Keyword.get(options, :local) do
|
|
|
|
Process.register(pid, :todo_list)
|
|
|
|
end
|
|
|
|
|
|
|
|
pid
|
2024-08-03 08:51:48 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def from_list(todo_list) do
|
|
|
|
spawn(fn -> loop(todo_list) end)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp loop(todo_list) do
|
|
|
|
new_todo_list =
|
|
|
|
receive do
|
|
|
|
message ->
|
|
|
|
process_message(todo_list, message)
|
|
|
|
after
|
|
|
|
1000 -> todo_list
|
|
|
|
end
|
|
|
|
|
|
|
|
loop(new_todo_list)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp process_message(todo_list, {:add, entry}) do
|
|
|
|
TodoList.add(todo_list, entry)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp process_message(todo_list, {:entries, sender, date}) do
|
|
|
|
entries = TodoList.entries(todo_list, date)
|
|
|
|
|
|
|
|
send(sender, {:entries, entries})
|
|
|
|
|
|
|
|
todo_list
|
|
|
|
end
|
|
|
|
|
|
|
|
defp process_message(todo_list, {:update, id, update_fun}) do
|
|
|
|
TodoList.update(todo_list, id, update_fun)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp process_message(todo_list, {:delete, id}) do
|
|
|
|
TodoList.delete(todo_list, id)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp process_message(todo_list, invalid_message) do
|
|
|
|
IO.puts("Invalid message: #{IO.inspect(invalid_message)}")
|
|
|
|
|
|
|
|
todo_list
|
|
|
|
end
|
|
|
|
|
|
|
|
def add(pid, entry) do
|
|
|
|
send(pid, {:add, entry})
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2024-08-03 09:44:00 +02:00
|
|
|
def add(entry) do
|
|
|
|
add(:todo_list, entry)
|
|
|
|
end
|
|
|
|
|
2024-08-03 08:51:48 +02:00
|
|
|
def entries(pid, date) do
|
|
|
|
send(pid, {:entries, self(), date})
|
|
|
|
|
|
|
|
receive do
|
|
|
|
{:entries, value} ->
|
|
|
|
{:ok, value}
|
|
|
|
after
|
|
|
|
1000 -> {:error}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-08-03 09:44:00 +02:00
|
|
|
def entries(date) do
|
|
|
|
entries(:todo_list, date)
|
|
|
|
end
|
|
|
|
|
2024-08-03 08:51:48 +02:00
|
|
|
def update(pid, id, update_fun) do
|
|
|
|
send(pid, {:update, id, update_fun})
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2024-08-03 09:44:00 +02:00
|
|
|
def update(id, update_fun) do
|
|
|
|
update(:todo_list, id, update_fun)
|
|
|
|
end
|
|
|
|
|
2024-08-03 08:51:48 +02:00
|
|
|
def delete(pid, id) do
|
|
|
|
send(pid, {:delete, id})
|
|
|
|
nil
|
|
|
|
end
|
2024-08-03 09:44:00 +02:00
|
|
|
|
|
|
|
def delete(id) do
|
|
|
|
delete(:todo_list, id)
|
|
|
|
end
|
2024-08-03 08:51:48 +02:00
|
|
|
end
|
|
|
|
|
2024-08-01 20:25:13 +02:00
|
|
|
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
|
|
|
|
|
2024-08-03 08:51:48 +02:00
|
|
|
def entries(%__MODULE__{} = todo_list, date) do
|
|
|
|
todo_list.entries
|
2024-08-01 20:25:13 +02:00
|
|
|
|> 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
|