Skip to content

Instantly share code, notes, and snippets.

@steven-solomon
Last active July 20, 2019 23:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steven-solomon/d7a06e591d8e6c6926cc89ae7da3ecd0 to your computer and use it in GitHub Desktop.
Save steven-solomon/d7a06e591d8e6c6926cc89ae7da3ecd0 to your computer and use it in GitHub Desktop.
What Is A GenServer?
# test/account_test.exs
defmodule AccountTest do
use ExUnit.Case
test "fails" do
assert true == false
end
end
defmodule Account do
# ...
def get_balance(pid) do
end
end
defmodule Account do
# ...
def get_balance(pid) do
0
end
end
defmodule Account do
# ...
def init(:ok) do
{:ok, %{balance: 0}}
end
# ...
end
defmodule Account do
# ...
def get_balance(pid) do
GenServer.call(pid, :get_balance)
end
end
defmodule Account do
# ...
def handle_call(:get_balance, _from, state) do
{:reply, Map.get(state, :balance), state}
end
end
defmodule AccountTest do
use ExUnit.Case
# ...
test 'depositing money changes the balance' do
{:ok, pid} = Account.start_link
Account.deposit(pid, 10.0)
end
end
defmodule Account
def deposit(pid, amount) do
end
end
defmodule AccountTest do
# ...
test 'depositing money changes the balance' do
{:ok, pid} = Account.start_link
Account.deposit(pid, 10.0)
assert 10.0 == Account.get_balance(pid)
end
end
defmodule Account do
# ...
def deposit(pid, amount) do
GenServer.cast(pid, {:deposit, amount})
end
end
defmodule Account do
# ...
def handle_cast({:deposit, amount}, state) do
end
end
defmodule Account do
# ...
def handle_cast({:deposit, amount}, state) do
{:noreply, state}
end
end
defmodule Account do
# ...
def handle_cast({:deposit, amount}, state) do
balance = Map.get(state, :balance)
{:noreply, Map.put(state, :balance, balance + amount)}
end
end
defmodule Account do
# ...
def handle_cast({:deposit, amount}, state) do
{_value, balance_with_deposit} =
Map.get_and_update(state, :balance, fn balance ->
{balance, balance + amount}
end)
{:noreply, balance_with_deposit}
end
end
defmodule AccountTest do
# ...
test 'withdraw money reduces the balance' do
{:ok, pid} = Account.start_link()
Account.withdraw(pid, 52.34)
end
end
defmodule Account do
# ...
def withdraw(pid, amount) do
end
# ...
end
defmodule AccountTest do
# ...
test 'withdraw money reduces the balance' do
{:ok, pid} = Account.start_link()
Account.withdraw(pid, 52.34)
assert -52.34 == Account.get_balance(pid)
end
end
defmodule Account do
# ...
def withdraw(pid, amount) do
GenServer.cast(pid, {:withdraw, amount})
end
# ...
end
defmodule Account do
# ...
def handle_cast({:deposit, amount}, state) do
# ...
end
def handle_cast({:withdraw, amount}, state) do
{:noreply, state}
end
end
defmodule Account do
# ...
def handle_cast({:deposit, amount}, state) do
{_value, balance_with_deposit} =
Map.get_and_update(state, :balance, fn balance ->
{balance, balance + amount}
end)
{:noreply, balance_with_deposit}
end
def handle_cast({:withdraw, amount}, state) do
{_value, balance_with_deposit} =
Map.get_and_update(state, :balance, fn balance ->
{balance, balance - amount}
end)
{:noreply, balance_with_deposit}
end
end
defmodule Account do
# ...
def handle_cast({:deposit, amount}, state) do
# Extracted anonymous function
update_balance = fn balance, amount -> balance + amount end
{_value, balance_with_deposit} =
Map.get_and_update(state, :balance, fn balance ->
# we now call the function instead of doing a calculation
{balance, update_balance.(balance, amount)}
end)
{:noreply, balance_with_deposit}
end
# similar changes were made to this function
def handle_cast({:withdraw, amount}, state) do
update_balance = fn balance, amount -> balance - amount end
{_value, balance_with_deposit} =
Map.get_and_update(state, :balance, fn balance ->
{balance, update_balance.(balance, amount)}
end)
{:noreply, balance_with_deposit}
end
end
defmodule AccountTest do
use ExUnit.Case
test "initial balance is zero" do
{:ok, pid} = Account.start_link
end
end
defmodule AccountTest do
use ExUnit.Case
test "initial balance is zero" do
{:ok, pid} = Account.start_link()
assert 0 == Account.get_balance(pid)
end
test 'depositing money changes the balance' do
{:ok, pid} = Account.start_link()
Account.deposit(pid, 10.0)
assert 10.0 == Account.get_balance(pid)
end
test 'withdraw money reduces the balance' do
{:ok, pid} = Account.start_link()
Account.withdraw(pid, 52.34)
assert -52.34 == Account.get_balance(pid)
end
end
defmodule Account do
# ...
def handle_cast({:deposit, amount}, state) do
update_balance = fn balance, amount -> balance + amount end
# variable 'update_balance' changed to 'updated_balance'
{_value, updated_balance} =
Map.get_and_update(state, :balance, fn balance ->
{balance, update_balance.(balance, amount)}
end)
# variable name changed
{:noreply, updated_balance}
end
def handle_cast({:withdraw, amount}, state) do
update_balance = fn balance, amount -> balance - amount end
# variable name changed
{_value, updated_balance} =
Map.get_and_update(state, :balance, fn balance ->
{balance, update_balance.(balance, amount)}
end)
# variable name changed
{:noreply, updated_balance}
end
end
defmodule Account do
# ...
def handle_cast({:withdraw, amount}, state) do
update_balance = fn balance, amount -> balance - amount end
{:noreply, change_balance(state, amount, update_balance)}
end
# extracted function
defp change_balance(state, amount, update_balance) do
{_value, updated_balance} =
Map.get_and_update(state, :balance, fn balance ->
{balance, update_balance.(balance, amount)}
end)
updated_balance
end
end
defmodule Account do
# ...
def handle_cast({:withdraw, amount}, state) do
{:noreply, change_balance(state, amount, &(&1 - &2))}
end
end
defmodule Account do
# ...
defp change_balance(state, amount, calculate_balance) do
{_value, updated_balance} =
Map.get_and_update(state, :balance, fn balance ->
{balance, calculate_balance.(balance, amount)}
end)
updated_balance
end
end
defmodule Account do
# ...
def handle_cast({:withdraw, amount}, state) do
{:noreply, get_updated_balance(state, amount, &(&1 - &2))}
end
defp get_updated_balance(state, amount, calculate_balance) do
{_value, updated_balance} =
Map.get_and_update(state, :balance, fn balance ->
{balance, calculate_balance.(balance, amount)}
end)
updated_balance
end
end
defmodule Account do
# ...
# notice the only different is in the anonymous function
def handle_cast({:deposit, amount}, state) do
{:noreply, get_updated_balance(state, amount, &(&1 + &2))}
end
def handle_cast({:withdraw, amount}, state) do
{:noreply, get_updated_balance(state, amount, &(&1 - &2))}
end
#...
end
defmodule Account do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, :ok)
end
def init(:ok) do
{:ok, %{balance: 0}}
end
# API
def get_balance(pid) do
GenServer.call(pid, :get_balance)
end
def deposit(pid, amount) do
GenServer.cast(pid, {:deposit, amount})
end
def withdraw(pid, amount) do
GenServer.cast(pid, {:withdraw, amount})
end
# Callbacks
def handle_call(:get_balance, _from, state) do
{:reply, Map.get(state, :balance), state}
end
def handle_cast({:deposit, amount}, state) do
{:noreply, get_updated_balance(state, amount, &(&1 + &2))}
end
def handle_cast({:withdraw, amount}, state) do
{:noreply, get_updated_balance(state, amount, &(&1 - &2))}
end
# private
defp get_updated_balance(state, amount, calculate_balance) do
{_value, updated_balance} =
Map.get_and_update(state, :balance, fn balance ->
{balance, calculate_balance.(balance, amount)}
end)
updated_balance
end
end
# bank/lib/account.ex
defmodule Account
end
# lib\account.ex
defmodule Account do
def start_link do
end
end
defmodule Account do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, :ok)
end
end
defmodule Account do
# ...
def init(:ok) do
end
end
defmodule Account do
# ...
def init(:ok) do
{:ok, %{}}
end
end
# test\account_test.exs
defmodule AccountTest do
use ExUnit.Case
test "initial balance is zero" do
{:ok, pid} = Account.start_link
assert 0 == Account.get_balance(pid)
end
end
This is a list of code samples for my post about writing GenServers with TDD.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment