Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
defmodule Pricing.Bad do
@moduledoc """
Price resolution — BAD: values initialized to nil then assigned in branches.
"""

def quote(cart, customer) do
base = nil
discount = nil
shipping = nil

base = subtotal(cart)

if customer.tier == :gold do
discount = div(base, 10)
end

if discount == nil do
discount = 0
end

if customer.tier == :gold do
shipping = 0
else
shipping = 499
end

%{base: base, discount: discount, shipping: shipping, total: base - discount + shipping}
end

def discount_for(customer, base) do
discount = nil

if customer.tier == :gold do
discount = div(base, 10)
end

if customer.tier == :silver do
discount = div(base, 20)
end

if discount == nil do
discount = 0
end

discount
end

def coupon_value(coupon, base) do
value = nil

if coupon != nil do
value = div(base * coupon.value, 100)
end

if value == nil do
value = 0
end

value
end

defp subtotal(cart), do: Enum.reduce(cart, 0, &(&1.price * &1.qty + &2))
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
defmodule Router.Bad do
@moduledoc """
Request routing — BAD: handler starts nil and is set in branch after branch.
"""

def dispatch(request) do
handler = nil

if request.method == :get and request.path == "/health" do
handler = &health/1
end

if request.method == :get and request.path == "/users" do
handler = &list_users/1
end

if request.method == :post and request.path == "/users" do
handler = &create_user/1
end

if handler == nil do
handler = &not_found/1
end

handler.(request)
end

def status_for(result) do
status = nil

if match?({:ok, _}, result) do
status = 200
end

if result == {:error, :not_found} do
status = 404
end

if status == nil do
status = 500
end

status
end

defp health(_request), do: {:ok, %{status: "up"}}
defp list_users(_request), do: {:ok, []}
defp create_user(_request), do: {:ok, %{id: 1}}
defp not_found(_request), do: {:error, :not_found}
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule Session.Bad do
@moduledoc """
Session building — BAD: nil placeholders filled in across nested branches.
"""

def build(conn, store) do
user = nil
token = nil
prefs = nil

token = conn.cookies["session"]

if token != nil do
user = store.lookup(token)

if user != nil do
prefs = store.preferences(user.id)
end
end

if user == nil do
{:error, :unauthenticated}
else
{:ok, %{user: user, token: token, preferences: prefs}}
end
end

def current_role(session) do
role = nil

if session != nil do
if session.user != nil do
role = session.user.role
end
end

if role == nil do
role = :guest
end

role
end

def expires_at(session, now) do
ttl = nil

if current_role(session) == :admin do
ttl = 3_600
end

if ttl == nil do
ttl = 1_800
end

now + ttl
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
defmodule Pricing.Good do
@moduledoc """
Price resolution — GOOD: each value comes straight from an expression.
"""

def quote(cart, customer) do
base = subtotal(cart)
discount = discount_for(customer, base)
shipping = shipping_for(cart, customer)

%{base: base, discount: discount, shipping: shipping, total: base - discount + shipping}
end

def discount_for(customer, base) do
case customer.tier do
:gold -> div(base, 10)
:silver -> div(base, 20)
_ -> 0
end
end

def shipping_for(cart, customer) do
cond do
customer.tier == :gold -> 0
subtotal(cart) >= 5_000 -> 0
true -> 499
end
end

def coupon_value(nil, _base), do: 0
def coupon_value(%{type: :percent, value: value}, base), do: div(base * value, 100)
def coupon_value(%{type: :flat, value: value}, _base), do: value

defp subtotal(cart), do: Enum.reduce(cart, 0, &(&1.price * &1.qty + &2))
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
defmodule Router.Good do
@moduledoc """
Request routing — GOOD: the handler is the value of a single case expression.
"""

def dispatch(request) do
handler =
case {request.method, request.path} do
{:get, "/health"} -> &health/1
{:get, "/users"} -> &list_users/1
{:post, "/users"} -> &create_user/1
{_, _} -> &not_found/1
end

handler.(request)
end

def status_for(result) do
case result do
{:ok, _} -> 200
{:error, :not_found} -> 404
{:error, :invalid} -> 422
{:error, _} -> 500
end
end

def content_type(request) do
Map.get(request.headers, "accept", "application/json")
end

defp health(_request), do: {:ok, %{status: "up"}}
defp list_users(_request), do: {:ok, []}
defp create_user(_request), do: {:ok, %{id: 1}}
defp not_found(_request), do: {:error, :not_found}
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule Session.Good do
@moduledoc """
Session building — GOOD: values bound once from with/case results.
"""

def build(conn, store) do
with {:ok, token} <- read_token(conn),
{:ok, user} <- store.lookup(token),
{:ok, prefs} <- store.preferences(user.id) do
{:ok, %{user: user, token: token, preferences: prefs}}
else
:no_token -> {:error, :unauthenticated}
{:error, reason} -> {:error, reason}
end
end

def current_role(session) do
case session do
%{user: %{role: role}} -> role
_ -> :guest
end
end

def expires_at(session, now) do
ttl = ttl_for(current_role(session))
now + ttl
end

defp ttl_for(:admin), do: 3_600
defp ttl_for(:guest), do: 300
defp ttl_for(_), do: 1_800

defp read_token(conn) do
case conn.cookies["session"] do
nil -> :no_token
token -> {:ok, token}
end
end
end
Loading