arrow ← to go left, arrow → to go right

unfiltered

a scala web toolkit

@softprops

Hi, I'm

HTTP.

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:

  • solve many problems around a given domain

def framework:

  • solve many problems around a given domain
  • “remove the plumbing” (sometimes sold with kitchen sinks)

def framework:

  • solve many problems around a given domain
  • “remove the plumbing” (sometimes sold with kitchen sinks)
  • monolithic systems :)

def framework:

  • solve many problems around a given domain
  • “remove the plumbing” (sometimes sold with kitchen sinks)
  • monolithic systems :)
  • simple problems :(

toolkits

def toolkit:

def toolkit:

  • loosly coupled components that can be composed to solve different problems

def toolkit:

  • loosly coupled components that can be composed to solve different problems
  • the micro frameworks

def toolkit:

  • loosly coupled components that can be composed to solve different problems
  • the micro frameworks
  • monolithic systems :|

def toolkit:

  • loosly coupled components that can be composed to solve different problems
  • the micro frameworks
  • monolithic systems :|
  • simple problems \('.')~

Problem:

  • seamingly lots of frameworks, few toolkits (on the jvm)

(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

ƒ() =>

type ResponseFunction =
  HttpServletResponse => HttpServletResponse
PartialFunction[HttpServletRequest, ResponseFunction]

/** as a class */ class App extends unfiltered.Planify({ case _ => ResponseString("hola amigo") })
/** as a trait */ class App extends Stuff with unfiltered.Plan { def filter = { case _ => ResponseString("hola amigo") } }
/** as a fn */ unfiltered.Planify { case _ => ResponseString("hola amigo") }

abstraction thru extraction

request extractors

extractors?

extractors

// the ghost in the compiler def unapply(x: X): Y
// imagine this... subject match { case SomeExtractor(y) => Some(y) case _ => None }
// as this in reverse (returns Some(y) or None) SomeExtractor.unapply(subject)
// the ghost in unfiltered's compiler def unapply(x: X): (Y, HttpServletRequest)
GET(_), POST(_), PUT(_), DELETE(_), HEAD(_)
Path("/hello", _)
Path(Seg("have" :: "a" :: what :: when :: Nil), _) => ResponseString("oh really? having a %s on %s" format(what, when))
Params(params, _) // Map[String, Seq[String]]
InStream(in, _)
Reader(r, _)
BasicAuth(creds, _) => creds match { case ("jane", "j@n3") => // jane stuff case ("jim", "j1m") => // jim stuff case _ => // default stuff }
Accepts(fmt, _) => fmt match { case 'json => // json stuff case 'xml => // xml stuff case _ => // otherwise default stuff }

It's just a convention.

ƒ() in, ƒ() out

response combinators

Hi("doug") ~> Im ~> Combinating ~> With("friends")

import javax.servlet.http.{HttpServletResponse => R} type ResponseFunction = R => R trait Responder extends ResponseFunction { def respond(res: R) def ~> (that: ResponseFunction) = new ChainResponse( this andThen that ) } class ChainResponse(f: ResponseFunction) extends Responder { def respond(res: R) = f(res) }

Don't worry.

They're just functions.

type ResponseFunction = R => R
ResponseString("what evs")
Redirect("/")
Status(200) // or Ok // or Status(404) // or NotFound // we named them all*
Pass*
PassAndThen after { case _ => // do stuff after next filter is done }

why ƒn's

ƒn's compose

compose

ResponseString("what evs") ~> Ok ~> ETag(obj.hash) ~> ResponseHeader( "x-runtime", TimeFrom(start) :: Nil)

(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

  • use what you've got
  • be transparent
  • don't buy a kitchen sink if you only own a few dishes
  • languages have idioms, use them
  • be happy

(un)veiled @ github.com/unfiltered/ Unfiltered

questions? 

heckling? 

coding?