Two scoops of Scala &
Unfiltered
April 2014 FullContact techTalk
@skazhy
Slide 2
Slide 2 text
Scala?
● object oriented and functional programming
language
● Type safe
● Runs on JVM
Slide 3
Slide 3 text
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
Slide 4
Slide 4 text
Toolchain
● SBT - Scala Build Tool
● Best conventions from Maven world
● Iterative development in SBT shell
Slide 5
Slide 5 text
My setup
● IntelliJ + SBT console(s) inside tmux
● IntelliJ 13 has SBT support baked in
Slide 6
Slide 6 text
Deployment
● Works great with Jenkins & Asgard
● Minimal configuration tweaks needed (wait
for the next slide)
Slide 7
Slide 7 text
● 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
Slide 8
Slide 8 text
Using opinionated
RESTful frameworks for
microservices?
Slide 9
Slide 9 text
No content
Slide 10
Slide 10 text
Example time!
Slide 11
Slide 11 text
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()
Slide 12
Slide 12 text
An Intent is a partial function that pattern
matches HTTP requests
Slide 13
Slide 13 text
A Plan is a binding between intent and the
underlying interface
Slide 14
Slide 14 text
Pattern matching
● Decomposing data structures to extract
certain fields or validate them
● Really powerful in Scala
● “routing” mechanism in Unfiltered
Slide 15
Slide 15 text
No content
Slide 16
Slide 16 text
Match on request methods
● Use builtins:
GET(_)
POST(_)
PATCH(_)
● Or define your own:
object SPACEJUMP extends Method("SPACEJUMP")
Slide 17
Slide 17 text
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")
Slide 18
Slide 18 text
Match on request headers
BasicAuth(username, password)
UserAgent("Cobook")
...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
Slide 21
Slide 21 text
No content
Slide 22
Slide 22 text
function composition!
Slide 23
Slide 23 text
Responses are composable
case _ =>
Unauthorized ~>
ResponseString("No pasaran") ~>
ResponseHeader("WWW-Authenticate", "Basic realm=foo")
Slide 24
Slide 24 text
● HTTP requests are stateless
● HTTP responses are side effects
Slide 25
Slide 25 text
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)
}
Slide 26
Slide 26 text
Unfiltered plays well with
RxJava too
Slide 27
Slide 27 text
case req @ GET(Path("/reactive")) =>
observableOp.subscribe(
result => req.respond(ResponseString(result))
_ => req.respond(InternalServerError)
)
Slide 28
Slide 28 text
What about testing?
Slide 29
Slide 29 text
● Separate library for testing with Specs2
● Mocking with Mockito
● Comes bundled with the giter8 template
unfiltered-specs2
Slide 30
Slide 30 text
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
}
}
}
Slide 31
Slide 31 text
Consider using Unfiltered when you...
● need an async & lightweight HTTP service
● Have separate business logic
● Want to leverage the λ-force