Skip to content

Commit

Permalink
Account balance using patterns (#1)
Browse files Browse the repository at this point in the history
Hiding the fact that we are dealing with a process, and accessing its services through a functional interface.
  • Loading branch information
simonewebdesign committed Feb 18, 2017
1 parent 7a71ab2 commit f109ff0
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 37 deletions.
68 changes: 62 additions & 6 deletions lib/live_odds/account.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 22 additions & 31 deletions test/live_odds/account_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit f109ff0

Please sign in to comment.