- Announcing GenStage http://elixir-lang.org/blog/2016/07/14/announcing-genstage/
https://gist.github.com/colinbankier/a3f2eb7642b94c0d610a
Elixir in Action, by Sasa Juric https://www.manning.com/books/elixir-in-action
We'll work through the hands-on excerises from this book, would build a ToDo server application. The concept of the application is simple enough. The basic version of the to-do list will support the following features:
- Creating a new data abstraction
- Adding new entries
- Querying the abstraction We'll continue adding features and by the end we’ll have a fully working distributed web server that can manage a large number of to-do lists.
- Todo list data abstraction
- Server Processes
- Todo generic server process
- GenServer powered Todo server
- Building a concurrent system
- Managing multiple Todo lists
- Persisting data
- Analysing and addressing bottlenecks
- Fault tolerance
- Errors in concurrent systems
- Supervisors and Supervision trees
- Isolating error effects
- Sharing state
- Single process bottlenecks
- ETS Tables
- Production
- Working with components
- Building and running the distributed system
First, we'll create a basic data structure for our todo lists, and a module for functions to manipulate it. Here is an example usage:
iex(1)> todo_list =
TodoList.new |>
TodoList.add_entry({2013, 12, 19}, "Dentist") |>
TodoList.add_entry({2013, 12, 20}, "Shopping") |>
TodoList.add_entry({2013, 12, 19}, "Movies")
iex(2)> TodoList.entries(todo_list, {2013, 12, 19})
["Movies", "Dentist"]
iex(3)> TodoList.entries(todo_list, {2013, 12, 18})
[]
First, create a new project:
mix new todo
Let's do a little bit of TDD as we go, so start a new test in test/todo_list_test.exs
Here is a test that does what our example snippet says, but I'm going to represent an entry as a
Map with date and title attributes:
defmodule TodoListTest do
use ExUnit.Case
test "should add todos to a todo list" do
todo_list =
TodoList.new |>
TodoList.add_entry(%{date: {2013, 12, 19}, title: "Dentist"}) |>
TodoList.add_entry(%{date: {2013, 12, 20}, title: "Shopping"}) |>
TodoList.add_entry(%{date: {2013, 12, 19}, title: "Movies"})
assert TodoList.entries(todo_list, {2013, 12, 19}) == [
%{date: {2013, 12, 19}, title: "Movies"},
%{date: {2013, 12, 19}, title: "Dentist"}
]
assert TodoList.entries(todo_list, {2013, 12, 18}) == []
end
end
Then create TodoList module in lib/todo_list.ex
Looking at our test, we can see a todo list is some of mapping from dates to entries, so we'll use a Map
as our internal represention.
defmodule TodoList do
def new, do: %{}
end
Next, we'll implement the add_entry/3
function. It needs to add the entry to the entry list for the specified date, and
also handle the case where no entries exist yet for that date. Turns out we can do with one call to Map.update/4
.
def add_entry(todo_list, date, title) do
Map.update(
todo_list,
date,
[title],
fn(titles) -> [title | titles] end
)
end
Then, we'll add the entries
function:
def entries(todo_list, date) do
Map.get(todo_list, date, [])
end