Skip to content

Instantly share code, notes, and snippets.

@gogogarrett
Last active June 12, 2016 05:39
Show Gist options
  • Save gogogarrett/fa578e808d47bec79e3aaa2f64428dbd to your computer and use it in GitHub Desktop.
Save gogogarrett/fa578e808d47bec79e3aaa2f64428dbd to your computer and use it in GitHub Desktop.
# phoenix controller
defmodule ExWeb.Api.V3.ArcadeController do
use ExWeb.Web, :controller
alias ExWeb.{Repo, ArcadeGame, Student}
# delegating entire lobic out into a sub app/package (otp or not..)
# Here I am trying to make the `PlayArcadeGame` super generic and _not_ coupled to phoenix.
# I am trying to pass in all the required info needed up front.. but idk how this will scale
# with more complex business things.
def play_game(conn, %{"game" => %{"id" => id}}) do
case PlayArcadeGame.call(Repo, {Student, current_user}, fetch_game(id)) do
{:ok, _msg} ->
conn |> send_resp(201, "")
{:error, reason} ->
conn |> send_resp(422, reason)
end
end
# stubs below
defp current_user do
Repo.get(Student, 1)
end
defp fetch_game(id) do
Repo.one(from(ac in ArcadeGame, where: ac.name == ^id))
end
end
# main business logic
# trying to be decoupled from reaching outside of it's context
defmodule PlayArcadeGame do
def call(repo, {student_struct, student_model}, game)
steps(repo, {student_struct, student_model}, game)
|> Workflow.call(repo)
end
defp steps(repo, {student_struct, student_model} = student_info, game) do
fn ->
with {:ok, _} <- Service.EnsureEnoughAcorns.call(student.acorns, game.cost),
{:ok, _} <- Service.AwardAcorns.call(repo, student_info, -game.cost),
{:ok, _} <- Service.CreateStudentEvent.call(%{id: 1}),
do: {:ok, "workflow successful"}
end
end
end
# single steps / services of the logic
defmodule Service.EnsureEnoughAcorns do
def call(acorns, cost) when acorns >= cost do
{:ok, ""}
end
def call(_acorns, _cost) do
{:error, "not enough acorns"}
end
end
defmodule Service.CreateStudentEvent do
def call(map) do
case create_student_event(map) do
_event ->
{:ok, "event created."}
_ ->
{:error, "event could not be created."}
end
end
defp create_student_event(map) do
# ie.
# %{
# student_id: student.id,
# precinct: 'arcade',
# event_type: 'ArcadeGame'
# }
# BlakeProgress.Service.create_student_event(map)
%{id: 1}
end
end
defmodule Service.AwardAcorns do
def call(repo, {student_struct, student_model}, amount \\ 3) do
student_model
|> student_struct.changeset(%{acorns: student_model.acorns + amount})
|> repo.update()
end
end
# another common package possibly
# simply to wrap things in a transaction and rollback if all fails
defmodule Workflow do
def call(function, repo) do
repo.transaction fn() ->
case function.() do
{:ok, result} -> result
{:error, error} -> repo.rollback(error)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment