Update: Finished persistable todo server
This commit is contained in:
parent
09665c3b88
commit
55589a963f
80 changed files with 809 additions and 0 deletions
4
chapter7/todo/.formatter.exs
Normal file
4
chapter7/todo/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
24
chapter7/todo/.gitignore
vendored
Normal file
24
chapter7/todo/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
todo-*.tar
|
||||
|
||||
21
chapter7/todo/README.md
Normal file
21
chapter7/todo/README.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Todo
|
||||
|
||||
**TODO: Add description**
|
||||
|
||||
## Installation
|
||||
|
||||
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||
by adding `todo` to your list of dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:todo, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||
be found at [https://hexdocs.pm/todo](https://hexdocs.pm/todo).
|
||||
|
||||
47
chapter7/todo/lib/todo/list.ex
Normal file
47
chapter7/todo/lib/todo/list.ex
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
defmodule Todo.List do
|
||||
defstruct auto_id: 1, entries: %{}
|
||||
|
||||
def new(), do: %Todo.List{}
|
||||
|
||||
def add_entry(todo_list, entry) do
|
||||
entry = Map.put(entry, :id, todo_list.auto_id)
|
||||
|
||||
new_entries =
|
||||
Map.put(
|
||||
todo_list.entries,
|
||||
todo_list.auto_id,
|
||||
entry
|
||||
)
|
||||
|
||||
%Todo.List{todo_list | entries: new_entries, auto_id: todo_list.auto_id + 1}
|
||||
end
|
||||
|
||||
def entries(todo_list, date) do
|
||||
todo_list.entries
|
||||
|> Stream.filter(fn {_, entry} -> entry.date == date end)
|
||||
|> Enum.map(fn {_, entry} -> entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, %{} = new_entry) do
|
||||
update_entry(todo_list, new_entry.id, fn _ -> new_entry end)
|
||||
end
|
||||
|
||||
def update_entry(todo_list, entry_id, updater_fun) do
|
||||
case Map.fetch(todo_list.entries, entry_id) do
|
||||
:error ->
|
||||
todo_list
|
||||
|
||||
{:ok, old_entry} ->
|
||||
old_entry_id = old_entry.id
|
||||
# Make sure that the result of the updater is a map and the
|
||||
# id remains unchanged.
|
||||
new_entry = %{id: ^old_entry_id} = updater_fun.(old_entry)
|
||||
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
|
||||
%Todo.List{todo_list | entries: new_entries}
|
||||
end
|
||||
end
|
||||
|
||||
def delete_entry(todo_list, entry_id) do
|
||||
%Todo.List{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
|
||||
end
|
||||
end
|
||||
48
chapter7/todo/lib/todo/server.ex
Normal file
48
chapter7/todo/lib/todo/server.ex
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
defmodule Todo.Server do
|
||||
use GenServer
|
||||
|
||||
def start do
|
||||
GenServer.start(__MODULE__, nil)
|
||||
end
|
||||
|
||||
def add_entry(pid, entry) do
|
||||
GenServer.cast(pid, {:add_entry, entry})
|
||||
end
|
||||
|
||||
def delete_entry(pid, entry_id) do
|
||||
GenServer.cast(pid, {:delete_entry, entry_id})
|
||||
end
|
||||
|
||||
def update_entry(pid, entry_id, updater_fun) do
|
||||
GenServer.cast(pid, {:update_entry, entry_id, updater_fun})
|
||||
end
|
||||
|
||||
def entries(pid, date) do
|
||||
GenServer.call(pid, {:entries, date})
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(_) do
|
||||
{:ok, Todo.List.new()}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_cast({:add_entry, entry}, state) do
|
||||
{:noreply, Todo.List.add_entry(state, entry)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_cast({:delete_entry, entry_id}, state) do
|
||||
{:noreply, Todo.List.delete_entry(state, entry_id)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_cast({:update_entry, entry_id, updater_fun}, state) do
|
||||
{:noreply, Todo.List.update_entry(state, entry_id, updater_fun)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:entries, date}, _, state) do
|
||||
{:reply, Todo.List.entries(state, date), state}
|
||||
end
|
||||
end
|
||||
28
chapter7/todo/mix.exs
Normal file
28
chapter7/todo/mix.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule Todo.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :todo,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
1
chapter7/todo/test/test_helper.exs
Normal file
1
chapter7/todo/test/test_helper.exs
Normal file
|
|
@ -0,0 +1 @@
|
|||
ExUnit.start()
|
||||
Loading…
Add table
Add a link
Reference in a new issue