From f109ff0cb210d8bcc318284fe9ffaa48a000e134 Mon Sep 17 00:00:00 2001 From: Simone Vittori Date: Sat, 18 Feb 2017 19:16:51 +0000 Subject: [PATCH] Account balance using patterns (#1) Hiding the fact that we are dealing with a process, and accessing its services through a functional interface. --- lib/live_odds/account.ex | 68 ++++++++++++++++++++++++++++++--- test/live_odds/account_test.exs | 53 +++++++++++-------------- 2 files changed, 84 insertions(+), 37 deletions(-) diff --git a/lib/live_odds/account.ex b/lib/live_odds/account.ex index 967245f..a218061 100644 --- a/lib/live_odds/account.ex +++ b/lib/live_odds/account.ex @@ -3,35 +3,91 @@ defmodule LiveOdds.Account do A bank account. """ + @doc """ Open a bank account. """ + @spec start() :: :ok def start do - {:ok, spawn(fn -> loop(0) end)} + if Enum.member?(Process.registered, :account) do + :ok + else + pid = spawn(fn -> loop(0) end) + Process.register(pid, :account) + :ok + end + end + + + @doc """ + Close a bank account. + """ + @spec stop() :: :ok + def stop do + Process.unregister(:account) + :ok + end + + + @doc """ + Returns the current account balance. + """ + @spec balance() :: integer + def balance do + send(:account, {:balance, self()}) + receive do + {:balance, balance} -> balance + end + end + + + @doc """ + Deposit money. + """ + @spec credit(pos_integer) :: :ok | :error + def credit(amount) do + send(:account, {{:credit, amount}, self()}) + receive do + {:credit, :ok} -> :ok + {:credit, error} -> error + end + end + + + @doc """ + Withdraw money. + """ + @spec debit(pos_integer) :: :ok | :error + def debit(amount) do + send(:account, {{:debit, amount}, self()}) + receive do + {:debit, :ok} -> :ok + {:debit, error} -> error + end end defp loop(state) do receive do {:balance, from} -> - send(from, state) + send(from, {:balance, state}) loop(state) {{:credit, amount}, from} -> if amount > 0 do - send(from, :ok) + send(from, {:credit, :ok}) loop(state + amount) else - send(from, {:error, "not a positive credit"}) + send(from, {:credit, {:error, "not a positive credit"}}) loop(state) end {{:debit, amount}, from} -> if amount > state do - send(from, {:error, "not enough money"}) + send(from, {:debit, {:error, "not enough money"}}) loop(state) else - send(from, :ok) + send(from, {:debit, :ok}) loop(state - amount) end end diff --git a/test/live_odds/account_test.exs b/test/live_odds/account_test.exs index 494ddbf..a6d907d 100644 --- a/test/live_odds/account_test.exs +++ b/test/live_odds/account_test.exs @@ -4,54 +4,45 @@ defmodule AccountTest do alias LiveOdds.Account - test "ask for current balance" do - {:ok, pid} = Account.start() - msg = :balance - send(pid, {msg, self()}) + setup do + :ok = Account.start() - assert_receive(0) + on_exit(fn() -> + :ok = Account.stop() + end) end - test "credit" do - {:ok, pid} = Account.start() - msg = {:credit, 50} - send(pid, {msg, self()}) - - assert_receive(:ok) + test "ask for current balance" do + assert 0 == Account.balance() end - test "deposit and then withdraw" do - {:ok, pid} = Account.start() - msg1 = {:credit, 50} - msg2 = {:debit, 30} - send(pid, {msg1, self()}) - send(pid, {msg2, self()}) + test "deposit" do + assert :ok == Account.credit(50) + assert 50 == Account.balance() + end + - assert_receive(:ok) - assert_receive(:ok) + test "deposit and withdraw" do + assert :ok == Account.credit(100) + assert :ok == Account.debit(30) + assert 70 == Account.balance() end test "depositing a negative amount should error" do - {:ok, pid} = Account.start() - msg = {:credit, -1} - send(pid, {msg, self()}) + {:error, msg} = Account.credit(-1) - assert_receive({:error, "not a positive credit"}) + assert msg == "not a positive credit" end test "withdrawing more than the availability should error" do - {:ok, pid} = Account.start() - msg1 = {:credit, 50} - msg2 = {:debit, 60} - send(pid, {msg1, self()}) - send(pid, {msg2, self()}) - - assert_receive(:ok) - assert_receive({:error, "not enough money"}) + :ok = Account.credit(50) + {:error, msg} = Account.debit(60) + + assert msg == "not enough money" end end