Last active
August 29, 2015 14:07
-
-
Save shishkin/9ff6be6f424218f10f4b to your computer and use it in GitHub Desktop.
Demo code from the Singapore Scala meet-up: http://www.meetup.com/Singapore-Scala-Programmers/events/207505482/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package demo | |
object ScalaDsl { | |
/* | |
* Defining a simplistic model for the web app DSL | |
*/ | |
case class HttpRequest(path: String, headers: Map[String, String], body: Option[String]) | |
case class HttpResponse(status: Int, headers: Map[String, String], body: Option[String]) | |
type Route = PartialFunction[HttpRequest, HttpResponse] | |
/* | |
* Sidenote on Option[T] | |
*/ | |
val opt: Option[Int] = Some(3) | |
//> opt : Option[Int] = Some(3) | |
val opt2: Option[Int] = None | |
//> opt2 : Option[Int] = None | |
val i = opt match { | |
case Some(x) => x | |
case None => 0 | |
} | |
//> i : Int = 3 | |
opt map (_.toString) | |
//> res0: Option[String] = Some(3) | |
opt2 map (_.toString) | |
//> res1: Option[String] = None | |
/* | |
* Defining and using a route | |
*/ | |
val hello: Route = { | |
case HttpRequest("/hello", _, Some(name)) => | |
HttpResponse(200, Map(), Some(s"Hello $name!")) | |
} | |
//> hello : demo.ScalaDsl.Route = <function1> | |
val notFound: Route = { | |
case _ => HttpResponse(404, Map(), Some("Not Found")) | |
} | |
//> notFound : demo.ScalaDsl.Route = <function1> | |
val server = hello orElse notFound | |
//> server : PartialFunction[demo.ScalaDsl.HttpRequest,demo.ScalaDsl.HttpResponse] = <function1> | |
server(HttpRequest("/hello", Map(), Some("Scala"))) | |
//> res2: demo.ScalaDsl.HttpResponse = HttpResponse(200,Map(),Some(Hello Scala!)) | |
server(HttpRequest("/foo", Map(), None)) | |
//> res3: demo.ScalaDsl.HttpResponse = HttpResponse(404,Map(),Some(Not Found)) | |
/* | |
* Request middleware | |
*/ | |
type Middleware = PartialFunction[HttpRequest, HttpRequest] | |
type Directive = Route => Route | |
val notAuthorized: Route = { | |
case _ => HttpResponse(401, Map(), Some("Not Authorized")) | |
} | |
//> notAuthorized : demo.ScalaDsl.Route = <function1> | |
val authentication: Middleware = { | |
case req @ HttpRequest(_, headers, _) if headers.contains("Authorization") => req | |
} | |
//> authentication : demo.ScalaDsl.Middleware = <function1> | |
val authenticate: Directive = route => (authentication andThen route) orElse notAuthorized | |
//> authenticate : demo.ScalaDsl.Directive = <function1> | |
val secureServer = authenticate(hello orElse notFound) | |
//> secureServer : demo.ScalaDsl.Route = <function1> | |
secureServer(HttpRequest("/hello", Map(), Some("Scala"))) | |
//> res4: demo.ScalaDsl.HttpResponse = HttpResponse(401,Map(),Some(Not Authorized)) | |
secureServer(HttpRequest("/hello", Map("Authorization" -> ""), Some("Scala"))) | |
//> res5: demo.ScalaDsl.HttpResponse = HttpResponse(200,Map(),Some(Hello Scala!)) | |
/* | |
* Method overloading using the Magnet pattern | |
* http://spray.io/blog/2012-12-13-the-magnet-pattern/ | |
* | |
* Here we achieve method overloading on a generic type that otherwise would | |
* not be possible due to JVM's generics type erasure. The problem is too | |
* artificial for this simplistic example though. | |
*/ | |
trait Completer { | |
def apply(): Route | |
} | |
import scala.util.{ Try, Success, Failure } | |
object Completer { | |
implicit def fromTryString(body: Try[String]) = new Completer { | |
def apply(): Route = { | |
case _ => body match { | |
case Success(b) => HttpResponse(200, Map(), Some(b)) | |
case Failure(_) => HttpResponse(500, Map(), Some("Oops!")) | |
} | |
} | |
} | |
implicit def fromTryStatus(status: Try[Int]) = new Completer { | |
def apply(): Route = { | |
case _ => status match { | |
case Success(s @ 401) => HttpResponse(s, Map(), Some("Not Authorized")) | |
case Success(s @ 404) => HttpResponse(s, Map(), Some("Not Found")) | |
case Success(s) => HttpResponse(s, Map(), None) | |
case Failure(_) => HttpResponse(500, Map(), Some("Oops!")) | |
} | |
} | |
} | |
} | |
def complete(completer: Completer): Route = { | |
completer() | |
} | |
//> complete: (completer: demo.ScalaDsl.Completer)demo.ScalaDsl.Route | |
val hello2 = complete(Try("Hello Scala!")) | |
//> hello2 : demo.ScalaDsl.Route = <function1> | |
val notFound2 = complete(Try(404)) | |
//> notFound2 : demo.ScalaDsl.Route = <function1> | |
val server2 = authenticate (hello2 orElse notFound2) | |
//> server2 : demo.ScalaDsl.Route = <function1> | |
/* | |
* Request extraction | |
*/ | |
def path(p: String): Directive = { | |
route => | |
val m: Middleware = { case req @ HttpRequest(`p`, _, _) => req } | |
m andThen route | |
} | |
//> path: (p: String)demo.ScalaDsl.Directive | |
def extractBody(route: String => Route): Route = { | |
case req @ HttpRequest(_, _, Some(body)) => route(body)(req) | |
} | |
//> extractBody: (route: String => demo.ScalaDsl.Route)demo.ScalaDsl.Route | |
val hello3 = path("/hello") { extractBody { name => complete(Try(s"Hello $name!")) } } | |
//> hello3 : demo.ScalaDsl.Route = <function1> | |
val goodbye = path("/goodbye") { extractBody { name => complete(Try(s"Goodbye $name!")) } } | |
//> goodbye : demo.ScalaDsl.Route = <function1> | |
val server3 = authenticate (hello3 orElse goodbye orElse notFound2) | |
//> server3 : demo.ScalaDsl.Route = <function1> | |
server3(HttpRequest("/hello", Map("Authorization" -> ""), Some("Scala"))) | |
//> res6: demo.ScalaDsl.HttpResponse = HttpResponse(200,Map(),Some(Hello Scala!)) | |
server3(HttpRequest("/foo", Map("Authorization" -> ""), Some("Scala"))) | |
//> res7: demo.ScalaDsl.HttpResponse = HttpResponse(404,Map(),Some(Not Found)) | |
server3(HttpRequest("/foo", Map(), Some("Scala"))) | |
//> res8: demo.ScalaDsl.HttpResponse = HttpResponse(401,Map(),Some(Not Authorized)) | |
server3(HttpRequest("/goodbye", Map("Authorization" -> ""), Some("Scala"))) | |
//> res9: demo.ScalaDsl.HttpResponse = HttpResponse(200,Map(),Some(Goodbye Scala!)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The web server DSL is inspired by the Spray Routing DSL. Due to time constraints and the information overflow HLists we not touched in the demo.