unfiltered
a scala web toolkit
@softprops
a scala web toolkit
@softprops
Hi, I'm
I'm stateless.
Who are you?
Problem:
HEAD /$#!Kittens*^! HTTP/1.1
User-Agent: nobodyisfoolin/1.0
Content-Type: application/json
solutions
frameworks
def framework:
def framework:
def framework:
def framework:
def framework:
toolkits
def toolkit:
def toolkit:
def toolkit:
def toolkit:
def toolkit:
\('.')~
Problem:
(pause)
what are requests anyway?
requests are patterns
Scala ♥'s
pattern matching
val isDoug = person match {
case Person("doug", _) => true
case _ => false
}
def act() {
loop {
react {
case Ping => ...
case Pong => ...
}
}
}
unfiltered ♥'s
matching
request patterns
Scala ♥'s ƒp
ƒper's (cool kids) say
“everything is a ƒ()
unfiltered plans are partial functions
ƒ() =>
abstraction thru extraction
request extractors
extractors?
It's just a convention.
ƒ() in, ƒ() out
response combinators
Hi("doug") ~> Im ~> Combinating ~> With("friends")
Don't worry.
They're just functions.
type ResponseFunction = R => R
why ƒn's
ƒn's compose
(raises hand)
How do we test?
simple
>sbt ~test
polite libraries complement
trait Served extends Specification {
shareVariables()
import unfiltered.server._
def setup: (Server => Server)
val port = 9090
lazy val server = setup(new Http(port))
val host = :/("localhost", port)
doBeforeSpec { server.start() }
doAfterSpec { server.stop() }
}
object BasicSpec extends Specification
with unfiltered.spec.Served {
import unfiltered.request.{Path => UFPath}
def setup = { _.filter(unfiltered.Planify {
case GET(UFPath("/", _)) => ResponseString("test")
})}
"A Server" should {
"respond to requests" in {
Http(host as_str) must_=="test"
}
}
}
object ToasterSpec extends Specification with
unfiltered.spec.Served {
def setup = { _.filter(unfiltered.Planify {
case _ => JsonContent ~> ResponseString(
"""{"bread":"toasted"}""")
}) }
"Toaster" should {
"toast bread" in {
val (body, headers) = Http(host >+ { r =>
(r as_str, r >:> { h => h }) })
headers must havePair(
("Content-Type" -> Set(
"application/json; charset=utf-8")))
body must_=="""{"bread":"toasted"}"""
}
}
}out to sea
<deployment-configuration>
<param-a>foo</param-a>
<param-b>bleh</parame-b>
<param-list>
<parm-c>
<param-list>
</deployment-configuration>
no thanks...
let's embed!
unfiltered.server.Http
Jetty under the covers
(for now, others later)
object Server {
def main(args: Array[String]) {
server.Http(8080)
.filter(Planify { ... })
.filter(Planify { ... })
.resources(getClass.getResouce("path/to/rsrc"))
.start
}
}
package treasure
class Maps extends unfiltered.Planify({
case GET(Path("/", BasicAuth(creds, _))) =>
creds match {
case ("doug", "d0ug") =>
ResponseString("~treasure map~") ~> Ok
case _ =>
WWWAuthenticate("Basic realm=\"/\"") ~>
Unauthorized
}
})
package treasure
object Server {
def main(args: Array[String]) {
server.Http(8080).filter(new Maps).start
}
}
but, if you really need to...
<?xml version="1.0" encoding="utf8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<filter>
<filter-name>Maps</filter-name>
<filter-class>treasure.Maps</filter-class>
</filter>
<filter-mapping>
<filter-name>Maps</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
the wilderness
before we part
(un)veiled @ github.com/unfiltered/ Unfiltered
questions?
heckling?
coding?