Two Scoops of Scala

Two Scoops of Scala

April tech talk at FullContact

085d620aca53f8b5a7630021747d9c9d?s=128

Kārlis Lauva

April 09, 2014
Tweet

Transcript

  1. Two scoops of Scala & Unfiltered April 2014 FullContact techTalk

    @skazhy
  2. Scala? • object oriented and functional programming language • Type

    safe • Runs on JVM
  3. Unfiltered? • A toolkit for HTTP in Scala • Really,

    really modular • Works with Netty and Jetty • Built for meetup.com realtime API • Makes good use of Scala's idioms
  4. Toolchain • SBT - Scala Build Tool • Best conventions

    from Maven world • Iterative development in SBT shell
  5. My setup • IntelliJ + SBT console(s) inside tmux •

    IntelliJ 13 has SBT support baked in
  6. Deployment • Works great with Jenkins & Asgard • Minimal

    configuration tweaks needed (wait for the next slide)
  7. • A tool for fetching templates for Scala projects •

    g8 skazhy/unfiltered-netty-rx • All SBT plugins for deployment and IntelliJ are included Bootstrapping with giter8
  8. Using opinionated RESTful frameworks for microservices?

  9. None
  10. Example time!

  11. object MyPlan extends cycle.Plan with cycle.ThreadPool with ServerErrorResponse { def

    intent = { case GET(Path("/")) => ResponseString("Hello!") case OPTIONS(_) => Created ~> Location("/foo") case _ => NotFound } } unfiltered.netty.Http(1337).handler(MyPlan).run()
  12. An Intent is a partial function that pattern matches HTTP

    requests
  13. A Plan is a binding between intent and the underlying

    interface
  14. Pattern matching • Decomposing data structures to extract certain fields

    or validate them • Really powerful in Scala • “routing” mechanism in Unfiltered
  15. None
  16. Match on request methods • Use builtins: GET(_) POST(_) PATCH(_)

    • Or define your own: object SPACEJUMP extends Method("SPACEJUMP")
  17. What happens under the hood? class Method(method: String) { def

    unapply[T](req: HttpRequest[T]) = if (req.method.equalsIgnoreCase(method)) Some(req) else None } object POST extends Method("POST")
  18. Match on request headers BasicAuth(username, password) UserAgent("Cobook")

  19. ...on URN Path("/foobar") Path(Seg("contactLists" :: "v2" :: listId :: Nil))

    // Will extract /contactLists/v2/1234
  20. ...or query parameters object AccountIdParam extends Params.Extract("numericId", Params.first ~> Params.nonempty

    ~> Params.long) PATCH(Params(AccountIdParam(accountId))) // Will extract 1234 from PATCH /foo?numericId=1234
  21. None
  22. function composition!

  23. Responses are composable case _ => Unauthorized ~> ResponseString("No pasaran")

    ~> ResponseHeader("WWW-Authenticate", "Basic realm=foo")
  24. • HTTP requests are stateless • HTTP responses are side

    effects
  25. Async made easy case req @ GET(Path("/async")) => val futureOp

    = asyncOperation futureOp.onSuccess { case result => req.respond(Ok ~> ResponseString(result)) } futureOp.onFailure { case _ => req.respond(InternalServerError) }
  26. Unfiltered plays well with RxJava too

  27. case req @ GET(Path("/reactive")) => observableOp.subscribe( result => req.respond(ResponseString(result)) _

    => req.respond(InternalServerError) )
  28. What about testing?

  29. • Separate library for testing with Specs2 • Mocking with

    Mockito • Comes bundled with the giter8 template unfiltered-specs2
  30. object OperationPostSpec extends Specification { "PATCH /foo" should { lazy

    val r = Http(url((host \ "foo").PATCH)) lazy val response = r() "return HTTP 200" in { response.getResponseStatus mustEqual 200 } } }
  31. Consider using Unfiltered when you... • need an async &

    lightweight HTTP service • Have separate business logic • Want to leverage the λ-force
  32. Questions?