From eff21ff5ecebf716da3d3f7aebd7751d7934903b Mon Sep 17 00:00:00 2001
From: Sean Callan <seancallan@gmail.com>
Date: Sun, 29 Apr 2018 13:50:10 -0600
Subject: [PATCH 1/2] New Simple Bank exercise

---
 README.md                              |  4 ++
 simple_bank/.formatter.exs             |  4 ++
 simple_bank/.gitignore                 | 24 ++++++++++
 simple_bank/README.md                  | 21 +++++++++
 simple_bank/config/config.exs          | 30 ++++++++++++
 simple_bank/lib/simple_bank.ex         | 32 +++++++++++++
 simple_bank/lib/simple_bank/account.ex |  5 ++
 simple_bank/mix.exs                    | 28 ++++++++++++
 simple_bank/test/simple_bank_test.exs  | 63 ++++++++++++++++++++++++++
 simple_bank/test/test_helper.exs       |  1 +
 10 files changed, 212 insertions(+)
 create mode 100644 simple_bank/.formatter.exs
 create mode 100644 simple_bank/.gitignore
 create mode 100644 simple_bank/README.md
 create mode 100644 simple_bank/config/config.exs
 create mode 100644 simple_bank/lib/simple_bank.ex
 create mode 100644 simple_bank/lib/simple_bank/account.ex
 create mode 100644 simple_bank/mix.exs
 create mode 100644 simple_bank/test/simple_bank_test.exs
 create mode 100644 simple_bank/test/test_helper.exs

diff --git a/README.md b/README.md
index 71d5e49..6f28749 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,10 @@ Each exercise is created as a standalone Mix project requiring a varying degree
 
   > Provided with a string of characters ("aabbc"), print all possible palindrome premutations ("abcba", "bacab") to IO.
 
+  - [Simple Bank](/tree/master/simple_bank) _added 2018-04-30_
+
+  > Implement a GenServer based bank with support for account registration, deposits, withdrawls, and account balance inquiries.
+
 ## Contributions
 
 We'd love to hear your feedback on how these exercises are working for you.
diff --git a/simple_bank/.formatter.exs b/simple_bank/.formatter.exs
new file mode 100644
index 0000000..525446d
--- /dev/null
+++ b/simple_bank/.formatter.exs
@@ -0,0 +1,4 @@
+# Used by "mix format"
+[
+  inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
+]
diff --git a/simple_bank/.gitignore b/simple_bank/.gitignore
new file mode 100644
index 0000000..2490feb
--- /dev/null
+++ b/simple_bank/.gitignore
@@ -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 3rd-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").
+simple_bank-*.tar
+
diff --git a/simple_bank/README.md b/simple_bank/README.md
new file mode 100644
index 0000000..6cba9f9
--- /dev/null
+++ b/simple_bank/README.md
@@ -0,0 +1,21 @@
+# SimpleBank
+
+**TODO: Add description**
+
+## Installation
+
+If [available in Hex](https://hex.pm/docs/publish), the package can be installed
+by adding `simple_bank` to your list of dependencies in `mix.exs`:
+
+```elixir
+def deps do
+  [
+    {:simple_bank, "~> 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/simple_bank](https://hexdocs.pm/simple_bank).
+
diff --git a/simple_bank/config/config.exs b/simple_bank/config/config.exs
new file mode 100644
index 0000000..21632ba
--- /dev/null
+++ b/simple_bank/config/config.exs
@@ -0,0 +1,30 @@
+# This file is responsible for configuring your application
+# and its dependencies with the aid of the Mix.Config module.
+use Mix.Config
+
+# This configuration is loaded before any dependency and is restricted
+# to this project. If another project depends on this project, this
+# file won't be loaded nor affect the parent project. For this reason,
+# if you want to provide default values for your application for
+# 3rd-party users, it should be done in your "mix.exs" file.
+
+# You can configure your application as:
+#
+#     config :simple_bank, key: :value
+#
+# and access this configuration in your application as:
+#
+#     Application.get_env(:simple_bank, :key)
+#
+# You can also configure a 3rd-party app:
+#
+#     config :logger, level: :info
+#
+
+# It is also possible to import configuration files, relative to this
+# directory. For example, you can emulate configuration per environment
+# by uncommenting the line below and defining dev.exs, test.exs and such.
+# Configuration from the imported file will override the ones defined
+# here (which is why it is important to import them last).
+#
+#     import_config "#{Mix.env}.exs"
diff --git a/simple_bank/lib/simple_bank.ex b/simple_bank/lib/simple_bank.ex
new file mode 100644
index 0000000..0a689d8
--- /dev/null
+++ b/simple_bank/lib/simple_bank.ex
@@ -0,0 +1,32 @@
+defmodule SimpleBank do
+  @moduledoc """
+  SimpleBank is a GenServer charading as a bank.
+  """
+  use GenServer
+
+  def start_link(initial_state \\ %{}) do
+    GenServer.start_link(__MODULE__, initial_state)
+  end
+
+  def init(initial_state) do
+    {:ok, initial_state}
+  end
+
+  @spec register(pid(), String.t()) :: {:ok, String.t()} | {:error, reason}
+  def register(bank_pid, name) do
+  end
+
+  @spec deposit(pid(), String.t(), pos_integer} :: {:ok, pos_integer} | {:error, reason}
+  def deposit(bank_pid, account_id, amount) do
+  end
+
+  @spec deposit(pid(), String.t()} :: {:ok, pos_integer} | {:error, reason}
+  def balance(bank_pid, account_id) do
+  end
+
+  @spec withdrawl(pid(), String.t(), pos_integer} :: {:ok, {pos_integer, pos_integer}} | {:error, reason}
+  def withdrawl(bank_pid, account_id, amount) do
+  end
+
+  def init(initial_state), do: initial_state
+end
diff --git a/simple_bank/lib/simple_bank/account.ex b/simple_bank/lib/simple_bank/account.ex
new file mode 100644
index 0000000..bc91847
--- /dev/null
+++ b/simple_bank/lib/simple_bank/account.ex
@@ -0,0 +1,5 @@
+defmodule SimpleBank.Account do
+  @type t :: %__MODULE__{balance: integer(), id: String.t(), name: String.t()}
+
+  defstruct [:balance, :id, :name]
+end
diff --git a/simple_bank/mix.exs b/simple_bank/mix.exs
new file mode 100644
index 0000000..4bd902a
--- /dev/null
+++ b/simple_bank/mix.exs
@@ -0,0 +1,28 @@
+defmodule SimpleBank.MixProject do
+  use Mix.Project
+
+  def project do
+    [
+      app: :simple_bank,
+      version: "0.1.0",
+      elixir: "~> 1.6",
+      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
diff --git a/simple_bank/test/simple_bank_test.exs b/simple_bank/test/simple_bank_test.exs
new file mode 100644
index 0000000..55675ae
--- /dev/null
+++ b/simple_bank/test/simple_bank_test.exs
@@ -0,0 +1,63 @@
+defmodule SimpleBankTest do
+  use ExUnit.Case, async: false
+
+  alias SimpleBank.Account
+
+  setup do
+    {:ok, bank_pid} = SimpleBank.start_link([%Account{balance: 100, id: "test_id", name: "Test"}])
+    {:ok, bank: bank_pid}
+  end
+
+  describe "register/2" do
+    test "creates a new account and generates an account id", %{bank: bank_pid} do
+      {:ok, account_id} = SimpleBank.register(bank_pid, "Another Test Account")
+      assert is_binary(account_id)
+    end
+
+    test "raises an error for existing account names", %{bank: bank_pid}  do
+      {:error, :existing_account} = SimpleBank.register(bank_pid, "Test")
+    end
+  end
+
+  describe "deposit/3" do
+    test "increases the account balance by the deposited amount", %{bank: bank_pid}  do
+      assert {:ok, 110} == SimpleBank.deposit(bank_pid, "test_id", 10)
+    end
+
+    test "does not allow deposits of negative ammounts", %{bank: bank_pid}  do
+      assert {:error, :pos_integer_only} == SimpleBank.deposit(bank_pid, "test_id", -1)
+    end
+
+    test "raises an error if the account does not exist", %{bank: bank_pid}  do
+      assert {:error, :missing_account} == SimpleBank.deposit(bank_pid, "doesnotexist", 10)
+    end
+  end
+
+  describe "balance/2" do
+    test "returns the current account balance", %{bank: bank_pid}  do
+      assert {:ok, 110} == SimpleBank.deposit(bank_pid, "test_id", 10)
+    end
+
+    test "raises an error if the account does not exist", %{bank: bank_pid}  do
+      assert {:error, :missing_account} == SimpleBank.balance(bank_pid, "doesnotexist")
+    end
+  end
+
+  describe "withdrawl/3" do
+    test "decreases the account balance by the withdrawn amount", %{bank: bank_pid}  do
+      assert {:ok, 100} == SimpleBank.withdrawl(bank_pid, "test_id", 10)
+    end
+
+    test "does not negative ammount balances", %{bank: bank_pid}  do
+      assert {:error, :insufficient_funds} == SimpleBank.withdrawl(bank_pid, "test_id", -1)
+    end
+
+    test "does not allow withdrawls of negative ammounts", %{bank: bank_pid}  do
+      assert {:error, :pos_integer_only} == SimpleBank.withdrawl(bank_pid, "test_id", 1000)
+    end
+
+    test "raises an error if the account does not exist", %{bank: bank_pid}  do
+      assert {:error, :missing_account} == SimpleBank.deposit(bank_pid, "doesnotexist", 10)
+    end
+  end
+end
diff --git a/simple_bank/test/test_helper.exs b/simple_bank/test/test_helper.exs
new file mode 100644
index 0000000..869559e
--- /dev/null
+++ b/simple_bank/test/test_helper.exs
@@ -0,0 +1 @@
+ExUnit.start()

From 4bd3da9544f98e8dc706c83efc9a0ca61bb21871 Mon Sep 17 00:00:00 2001
From: Sean Callan <seancallan@gmail.com>
Date: Tue, 1 May 2018 19:09:25 -0600
Subject: [PATCH 2/2] Add SimpleBankOnline exercise

---
 README.md                                     |  4 ++
 simple_bank_online/.formatter.exs             |  4 ++
 simple_bank_online/.gitignore                 | 24 ++++++++++++
 simple_bank_online/README.md                  | 21 ++++++++++
 simple_bank_online/config/config.exs          | 30 ++++++++++++++
 simple_bank_online/lib/simple_bank_online.ex  | 18 +++++++++
 .../lib/simple_bank_online/application.ex     | 17 ++++++++
 .../lib/simple_bank_online/router.ex          | 39 +++++++++++++++++++
 simple_bank_online/mix.exs                    | 31 +++++++++++++++
 .../test/simple_bank_online_test.exs          |  8 ++++
 simple_bank_online/test/test_helper.exs       |  1 +
 11 files changed, 197 insertions(+)
 create mode 100644 simple_bank_online/.formatter.exs
 create mode 100644 simple_bank_online/.gitignore
 create mode 100644 simple_bank_online/README.md
 create mode 100644 simple_bank_online/config/config.exs
 create mode 100644 simple_bank_online/lib/simple_bank_online.ex
 create mode 100644 simple_bank_online/lib/simple_bank_online/application.ex
 create mode 100644 simple_bank_online/lib/simple_bank_online/router.ex
 create mode 100644 simple_bank_online/mix.exs
 create mode 100644 simple_bank_online/test/simple_bank_online_test.exs
 create mode 100644 simple_bank_online/test/test_helper.exs

diff --git a/README.md b/README.md
index 6f28749..de7c4f1 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,10 @@ Each exercise is created as a standalone Mix project requiring a varying degree
 
   > Implement a GenServer based bank with support for account registration, deposits, withdrawls, and account balance inquiries.
 
+  - [Simple Bank Online](/tree/master/simple_bank_online) _added 2018-05-02_
+
+  > Using the implementation from [Simple Bank](/tree/master/simple_bank), complete the JSON API to bring your bank online!
+
 ## Contributions
 
 We'd love to hear your feedback on how these exercises are working for you.
diff --git a/simple_bank_online/.formatter.exs b/simple_bank_online/.formatter.exs
new file mode 100644
index 0000000..525446d
--- /dev/null
+++ b/simple_bank_online/.formatter.exs
@@ -0,0 +1,4 @@
+# Used by "mix format"
+[
+  inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
+]
diff --git a/simple_bank_online/.gitignore b/simple_bank_online/.gitignore
new file mode 100644
index 0000000..f6463ad
--- /dev/null
+++ b/simple_bank_online/.gitignore
@@ -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 3rd-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").
+simple_bank_online-*.tar
+
diff --git a/simple_bank_online/README.md b/simple_bank_online/README.md
new file mode 100644
index 0000000..74b3563
--- /dev/null
+++ b/simple_bank_online/README.md
@@ -0,0 +1,21 @@
+# SimpleBankOnline
+
+**TODO: Add description**
+
+## Installation
+
+If [available in Hex](https://hex.pm/docs/publish), the package can be installed
+by adding `simple_bank_online` to your list of dependencies in `mix.exs`:
+
+```elixir
+def deps do
+  [
+    {:simple_bank_online, "~> 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/simple_bank_online](https://hexdocs.pm/simple_bank_online).
+
diff --git a/simple_bank_online/config/config.exs b/simple_bank_online/config/config.exs
new file mode 100644
index 0000000..564ea14
--- /dev/null
+++ b/simple_bank_online/config/config.exs
@@ -0,0 +1,30 @@
+# This file is responsible for configuring your application
+# and its dependencies with the aid of the Mix.Config module.
+use Mix.Config
+
+# This configuration is loaded before any dependency and is restricted
+# to this project. If another project depends on this project, this
+# file won't be loaded nor affect the parent project. For this reason,
+# if you want to provide default values for your application for
+# 3rd-party users, it should be done in your "mix.exs" file.
+
+# You can configure your application as:
+#
+#     config :simple_bank_online, key: :value
+#
+# and access this configuration in your application as:
+#
+#     Application.get_env(:simple_bank_online, :key)
+#
+# You can also configure a 3rd-party app:
+#
+#     config :logger, level: :info
+#
+
+# It is also possible to import configuration files, relative to this
+# directory. For example, you can emulate configuration per environment
+# by uncommenting the line below and defining dev.exs, test.exs and such.
+# Configuration from the imported file will override the ones defined
+# here (which is why it is important to import them last).
+#
+#     import_config "#{Mix.env}.exs"
diff --git a/simple_bank_online/lib/simple_bank_online.ex b/simple_bank_online/lib/simple_bank_online.ex
new file mode 100644
index 0000000..b16a0ac
--- /dev/null
+++ b/simple_bank_online/lib/simple_bank_online.ex
@@ -0,0 +1,18 @@
+defmodule SimpleBankOnline do
+  @moduledoc """
+  Documentation for SimpleBankOnline.
+  """
+
+  @doc """
+  Hello world.
+
+  ## Examples
+
+      iex> SimpleBankOnline.hello
+      :world
+
+  """
+  def hello do
+    :world
+  end
+end
diff --git a/simple_bank_online/lib/simple_bank_online/application.ex b/simple_bank_online/lib/simple_bank_online/application.ex
new file mode 100644
index 0000000..4493d19
--- /dev/null
+++ b/simple_bank_online/lib/simple_bank_online/application.ex
@@ -0,0 +1,17 @@
+defmodule SimpleBankOnline.Application do
+  @moduledoc false
+
+  use Application
+
+  import Supervisor.Spec
+
+  def start(_type, _args) do
+    children = [
+      Plug.Adapters.Cowboy.child_spec(:http, SimpleBankOnline.Router, [], port: 8000),
+      worker(SimpleBank, [])
+    ]
+
+    opts = [strategy: :one_for_one, name: Web.Supervisor]
+    Supervisor.start_link(children, opts)
+  end
+end
diff --git a/simple_bank_online/lib/simple_bank_online/router.ex b/simple_bank_online/lib/simple_bank_online/router.ex
new file mode 100644
index 0000000..134df8b
--- /dev/null
+++ b/simple_bank_online/lib/simple_bank_online/router.ex
@@ -0,0 +1,39 @@
+defmodule SimpleBankOnline.Router do
+  use Plug.Router
+
+  plug :match
+  plug Plug.RequestId
+  plug Plug.Logger, log: :info
+  plug Plug.Parsers, parsers: [:json], pass: ["text/*"], json_decoder: Poison
+  plug :dispatch
+
+  get "/", do: send_resp(conn, 200, Poison.encode!(%{msg: "Welcome to Simple Bank Online!"}))
+
+  post "/accounts" do
+  end
+
+  get "/accounts/:id" do
+  end
+
+  post "/accounts/:id/deposits" do
+  end
+
+  post "/accounts/:id/withdrawals" do
+  end
+
+  match _ do
+    render({:error, :not_found}, conn)
+  end
+
+  defp render({:ok, result}, conn) do
+    send_resp(conn, 200, Poison.encode!(result))
+  end
+
+  defp render({:error, reason}, conn) when reason in [:not_found, :missing_account] do
+    send_resp(conn, 404, Poison.encode!(%{errors: ["We're sorry.  What you're looking for cannot be found."]}))
+  end
+
+  defp render({:error, reason}, conn) do
+    send_resp(conn, 400, Poison.encode!(%{errors: [reason]}))
+  end
+end
diff --git a/simple_bank_online/mix.exs b/simple_bank_online/mix.exs
new file mode 100644
index 0000000..58192a1
--- /dev/null
+++ b/simple_bank_online/mix.exs
@@ -0,0 +1,31 @@
+defmodule SimpleBankOnline.MixProject do
+  use Mix.Project
+
+  def project do
+    [
+      app: :simple_bank_online,
+      version: "0.1.0",
+      elixir: "~> 1.6",
+      start_permanent: Mix.env() == :prod,
+      deps: deps()
+    ]
+  end
+
+  # Run "mix help compile.app" to learn about applications.
+  def application do
+    [
+      extra_applications: [:logger],
+      mod: {SimpleBankOnline.Application, []}
+    ]
+  end
+
+  # Run "mix help deps" to learn about dependencies.
+  defp deps do
+    [
+      {:cowboy, "~> 1.0"},
+      {:plug, "~> 1.4"},
+      {:poison, "~> 3.1"},
+      {:simple_bank, path: "../simple_bank"},
+    ]
+  end
+end
diff --git a/simple_bank_online/test/simple_bank_online_test.exs b/simple_bank_online/test/simple_bank_online_test.exs
new file mode 100644
index 0000000..5e26973
--- /dev/null
+++ b/simple_bank_online/test/simple_bank_online_test.exs
@@ -0,0 +1,8 @@
+defmodule SimpleBankOnlineTest do
+  use ExUnit.Case
+  doctest SimpleBankOnline
+
+  test "greets the world" do
+    assert SimpleBankOnline.hello() == :world
+  end
+end
diff --git a/simple_bank_online/test/test_helper.exs b/simple_bank_online/test/test_helper.exs
new file mode 100644
index 0000000..869559e
--- /dev/null
+++ b/simple_bank_online/test/test_helper.exs
@@ -0,0 +1 @@
+ExUnit.start()
