Skip to content

Instantly share code, notes, and snippets.

@friedbrice
Last active December 31, 2017 17:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save friedbrice/b08d5e5660a824e3f912d4570cdf7f8c to your computer and use it in GitHub Desktop.
Save friedbrice/b08d5e5660a824e3f912d4570cdf7f8c to your computer and use it in GitHub Desktop.
Exception Control Flow - Scala
import Spec._
import Undefined._
object Continuations {
def getUser(req: Request)(cont: User => Response): Response = {
val token = req.header.get("Authorization")
if (token.isEmpty) noToken else
if (`malformed token?`) malformedToken(token.get) else
if (`user not found?`) noUser(token.get) else
cont(`the user`)
}
def getResource(req: Request)(cont: Resource => Response): Response = {
val path: String = req.path
if (`resource not found?`) noResource(path) else
cont(`the resource`)
}
def execute(content: String, usr: User, src: Resource)
(cont: Unit => Response): Response = {
if (!`is permitted?`) notPermitted(src.path) else
if (!`is executed?`) badConnection else
cont(())
}
def handlePost(req: Request): Response = {
if (req.method != "POST") notAllowed(req.method) else
if (req.body.isEmpty) noBody else
getUser(req) { usr =>
getResource(req) { src =>
execute(req.body, usr, src) { _ =>
success(req.path, req.body) } } }
}
}
import Spec._
import Undefined._
object Eithers {
def failIf[A](p: Boolean, a: => A): Either[A, Unit] =
if (p) Left(a) else Right(())
def getUser(req: Request): Either[Response, User] = for {
token <- req.header.get("Authorization").toRight(noToken)
_ <- failIf(`malformed token?`, malformedToken(token))
_ <- failIf(`user not found?`, noUser(token))
} yield `the user`
def getResource(req: Request): Either[Response, Resource] = for {
path <- Right(req.path)
_ <- failIf(`resource not found?`, noResource(path))
} yield `the resource`
def execute(content: String, usr: User, src: Resource):
Either[Response, Unit] = for {
_ <- failIf(! `is permitted?`, notPermitted(src.path))
_ <- failIf(! `is executed?`, badConnection)
} yield {}
def handlePost(req: Request): Response = {
for {
_ <- failIf(req.method != "POST", notAllowed(req.method))
_ <- failIf(req.body.isEmpty, noBody)
usr <- getUser(req)
src <- getResource(req)
_ <- execute(req.body, usr, src)
} yield success(req.path, req.body)
}.fold(identity, identity)
}
import Spec._
import Undefined._
object Exceptions {
case class NoTokenProvided() extends Exception
case class MalformedToken(token: String) extends Exception
case class NoUserFound(token: String) extends Exception
@throws[NoTokenProvided]
@throws[MalformedToken]
@throws[NoUserFound]
def getUser(req: Request): User = {
val token = req.header.get("Authorization")
if (token.isEmpty) throw NoTokenProvided()
if (`malformed token?`) throw MalformedToken(token.get)
if (`user not found?`) throw NoUserFound(token.get)
`the user`
}
case class NoResourceFound(path: String) extends Exception
@throws[NoResourceFound]
def getResource(req: Request): Resource = {
lazy val path: String = req.path
if (`resource not found?`) throw NoResourceFound(path)
`the resource`
}
case class NotPermitted(path: String) extends Exception
case class BadConnection() extends Exception
@throws[NotPermitted]
@throws[BadConnection]
def execute(content: String, usr: User, src: Resource): Unit = {
if (! `is permitted?`) throw NotPermitted(src.path)
if (! `is executed?`) throw BadConnection()
}
case class MethodNotAllowed(method: String) extends Exception
case class NoBodyProvided() extends Exception
@throws[MethodNotAllowed]
@throws[NoBodyProvided]
def checkPreconditions(req: Request): Unit = {
if (req.method != "POST") throw MethodNotAllowed(req.method)
if (req.body.isEmpty) throw NoBodyProvided()
}
def handlePost(req: Request): Response = try {
checkPreconditions(req)
val user = getUser(req)
val resource = getResource(req)
execute(req.body, user, resource)
success(req.path, req.body)
} catch {
case MethodNotAllowed(method) => notAllowed(method)
case NoBodyProvided() => noBody
case NoTokenProvided() => noToken
case MalformedToken(token) => malformedToken(token)
case NoUserFound(token) => noUser(token)
case NoResourceFound(path) => noResource(path)
case NotPermitted(path) => notPermitted(path)
case BadConnection() => badConnection
}
}
import Spec._
import Undefined._
object Prototype {
// crashes computer if no token, token malformed, or no associated user
def getUser(req: Request): User = {
`the user`
}
// crashes computer if resource not found
def getResource(req: Request): Resource = {
`the resource`
}
// crashes computer if user does not have permission or bad connection
def execute(content: String, u: User, r: Resource): Unit = {
`is executed?`
}
// crashes computer if method is not POST, body is empty, or any of the reasons above
def handlePost(req: Request): Response = {
if (req.method != "POST") `halt and catch fire`
if (req.body.isEmpty) `halt and catch fire`
val usr = getUser(req)
val src = getResource(req)
execute(req.body, usr, src)
success(req.path, req.body)
}
}
object Spec {
trait User { def token: String }
trait Resource { def path: String }
case class Request(
path: String,
method: String,
body: String,
header: Map[String, String]
)
case class Response(code: Int, content: String)
def success(path: String, body: String): Response =
Response(200, s"Successfully posted $body to $path")
val noBody: Response =
Response(400, "You must provide a request body")
val noToken: Response =
Response(401, "You must provide an authorization header field")
def malformedToken(token: String): Response =
Response(401, s"Provided token is malformed: $token")
def noUser(token: String): Response =
Response(401, s"No user found for token: $token")
def notPermitted(path: String): Response =
Response(403, s"You do not have permission on $path")
def noResource(path: String): Response =
Response(404, s"No resource found for path: $path")
def notAllowed(method: String): Response =
Response(405, s"Method not allowed: $method")
val badConnection: Response =
Response(503, "Connection error, please try again later")
}
import Spec._
object Test extends App {
val request1 = Request("path", "POST", "", Map("Authorization" -> "hunter2"))
val request2 = Request("path", "POST", "body", Map("Nope" -> "nada"))
val request3 = Request("path", "FOO", "body", Map("Authorization" -> "hunter2"))
println("")
println("Testing Exceptions:")
println(" " + Exceptions.handlePost(request1))
println(" " + Exceptions.handlePost(request2))
println(" " + Exceptions.handlePost(request3))
println("Passed.")
println("")
println("Testing Continuations:")
println(" " + Continuations.handlePost(request1))
println(" " + Continuations.handlePost(request2))
println(" " + Continuations.handlePost(request3))
println("Passed.")
println("")
println("Testing Eithers:")
println(" " + Eithers.handlePost(request1))
println(" " + Eithers.handlePost(request2))
println(" " + Eithers.handlePost(request3))
println("Passed.")
println("")
}
import Spec._
object Undefined {
def `malformed token?`: Boolean = ???
def `user not found?`: Boolean = ???
def `the user`: User = ???
def `resource not found?`: Boolean = ???
def `the resource`: Resource = ???
def `is permitted?`: Boolean = ???
def `is executed?`: Boolean = ???
def `halt and catch fire`: Nothing = ???
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment