Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
Unfiltered Unveiled
The Un-framework
© Wilfred Springer, 2015 | http://www.flotsam.nl/
Whoami
Code/train Scala/Node.js for a living
* Currently @ Gust
* Formerly @ ProQuest Flow
* Formerly @ ProQuest Udini
* F...
What are we trying to solve here?
GET / HTTP/1.0 HTTP/1.1 200 OK
Host: nxt.flotsam.nl Date: Mon, 27 April, 2015
Accept: te...
And therefore, we shall have an
∞/insane number of web
frameworks
© Wilfred Springer, 2015 | http://www.flotsam.nl/
HTTP
Stuff => Stuff
© Wilfred Springer, 2015 | http://www.flotsam.nl/
HTTP
Request => Response
© Wilfred Springer, 2015 | http://www.flotsam.nl/
Web Application
Request1 => Response1
Request2 => Response2
Request3 => Response3
Request4 => Response4
© Wilfred Springer...
Wait, that rings a bell
… match {
case Request1 => Response1
case Request2 => Response2
case Request3 => Response3
case Re...
Pattern matching
def doubleOf(obj: Any) = {
obj match {
case i: Int => i * 2
case s: String => s + s
}
}
© Wilfred Springe...
Partial Function
{
case i: Int => i * 2
case s: String => s + s
}
© Wilfred Springer, 2015 | http://www.flotsam.nl/
Partial Function
val doubleOf = PartialFunction[Any, Any] = {
case i: Int => i * 2
case s: String => s + s
}
doubleOf(3) /...
Unfiltered Anatomy
The Intent (Simplified)
type Intent = PartialFunction[HttpRequest, ResponseFunction]
© Wilfred Springer, ...
Unfiltered Anatomy
The Intent
type Intent[A,B] = PartialFunction[
HttpRequest[A], // A: some request representation
Respons...
Unfiltered Anatomy
The Intent
{
case _ => ResponseString("yay")
}
---------------------------------
GET / => HTTP/1.1 200 O...
Unfiltered Anatomy
Binding
import unfiltered.filter
import unfiltered.netty
// Turn it into a Jetty compatible Filter
val p...
Unfiltered Anatomy
Launching
import unfiltered.jetty
import unfiltered.filter
import unfiltered.netty
// Turn it into a Jet...
ResponseFunction
Simplified, and not really true
trait ResponseFunction[-A]
extends HttpResponse[A] => HttpResponse[A]
© Wi...
ResponseFunction
case … => …
case … => … ~> … ~> …
------------------------------------
Ok Html(…)
NotFound Html5(…)
Respo...
ResponseFunction
Ok ~> PlainTextContent ~> ResponseString("foo")
---------------------------------------------------------...
ResponseFunction
Composition using ~>
Ok ~> PlainTextContent ~> ResponseString("foo")
NotFound ~> HtmlContent ~> Html(<htm...
Or rather…
Scalate
// index.scaml
!!! 5
%html
%head
%body Not found
// .scala
import unfiltered.scalate.Scalate
Ok ~> Html...
Matching the HttpRequest
case … => …
case … & … & … =>
GET(…) Accept(…)
POST(…) UserAgent(…)
DELETE(…) Host(…)
PUT(…) IfMo...
Matching the HttpRequest
Samples
case _ =>
case GET(Path('/index'))
case GET(Path(p)) if p endsWith ".json"
case Path(Seg(...
It's just extractors, dude
object DotJson {
unapply[A](req: HttpRequest[A]) = req.uri endsWith ".json"
}
case DotJson() =>...
Summary so far
— An Intent is just a partial function
— accepting an HttpRequest,
— producing a ResponseFunction
— … which...
Upping our game
What else do we need?
© Wilfred Springer, 2015 | http://www.flotsam.nl/
Reusable Composite ResponseFunctions
Don't Repeat Yourself
… => Ok ~> HtmlContent ~> Html(<html><body>text1</body></html>)...
Cross Cutting Concerns
or, how to use Spring AOP
© Wilfred Springer, 2015 | http://www.flotsam.nl/
Cross Cutting Concerns
or, how to use Spring AOP
© Wilfred Springer, 2015 | http://www.flotsam.nl/
Cross Cutting Concerns
How NOT to use Spring AOP
© Wilfred Springer, 2015 | http://www.flotsam.nl/
Cross Cutting Concerns
Kit
In general, a Kit is something that:
— Takes an Intent (but it doesn't have to)
— Produces an I...
Cross Cutting Concerns
Authentication
import unfiltered.kit.Auth
def verify(username: String, password: String): Boolean =...
Cross Cutting Concerns
// Can you tell the issue with this code?
import unfiltered.request._
import unfiltered.response._
...
Cross Cutting Concerns
Directives for error handling
import unfiltered.directives._, Directives._
val Smart = unfiltered.f...
Cross Cutting Concerns
Directives for routing
val Sweet = unfiltered.filter.Planify { Directive.Intent.Path {
case "/" =>
...
Parameter Based Routing
Primitive (never do this)
case Params(ps) if ps contains "name" =>
val name = ps.get("name").head
...
Parameter Based Routing
No longer preferred
// Building extractor for name parameter
object Name extends Params.Extract("n...
Parameter Based Routing
Getting it right with Directives and Interpreters
Directive.Intent {
case Path("/") =>
for {
name ...
© Wilfred Springer, 2015 | http://www.flotsam.nl/
Prochain SlideShare
Chargement dans…5
×

Unfiltered Unveiled

419 vues

Publié le

Unfiltered introduction, based on a presentation given at the Brabant Scala Meetup in April 2015.

Publié dans : Logiciels
  • Soyez le premier à commenter

Unfiltered Unveiled

  1. 1. Unfiltered Unveiled The Un-framework © Wilfred Springer, 2015 | http://www.flotsam.nl/
  2. 2. Whoami Code/train Scala/Node.js for a living * Currently @ Gust * Formerly @ ProQuest Flow * Formerly @ ProQuest Udini * Formerly @ Bol.com * Formerly @ Xebia * Formerly @ TomTom * Formerly @ Sun Microsystems © Wilfred Springer, 2015 | http://www.flotsam.nl/
  3. 3. What are we trying to solve here? GET / HTTP/1.0 HTTP/1.1 200 OK Host: nxt.flotsam.nl Date: Mon, 27 April, 2015 Accept: text/html … Last-Modified: … ETag: "4ce43…" Content-Type: text/html Content-Length: 9636 Server: AmazonS3 Content: … © Wilfred Springer, 2015 | http://www.flotsam.nl/
  4. 4. And therefore, we shall have an ∞/insane number of web frameworks © Wilfred Springer, 2015 | http://www.flotsam.nl/
  5. 5. HTTP Stuff => Stuff © Wilfred Springer, 2015 | http://www.flotsam.nl/
  6. 6. HTTP Request => Response © Wilfred Springer, 2015 | http://www.flotsam.nl/
  7. 7. Web Application Request1 => Response1 Request2 => Response2 Request3 => Response3 Request4 => Response4 © Wilfred Springer, 2015 | http://www.flotsam.nl/
  8. 8. Wait, that rings a bell … match { case Request1 => Response1 case Request2 => Response2 case Request3 => Response3 case Request4 => Response4 } © Wilfred Springer, 2015 | http://www.flotsam.nl/
  9. 9. Pattern matching def doubleOf(obj: Any) = { obj match { case i: Int => i * 2 case s: String => s + s } } © Wilfred Springer, 2015 | http://www.flotsam.nl/
  10. 10. Partial Function { case i: Int => i * 2 case s: String => s + s } © Wilfred Springer, 2015 | http://www.flotsam.nl/
  11. 11. Partial Function val doubleOf = PartialFunction[Any, Any] = { case i: Int => i * 2 case s: String => s + s } doubleOf(3) // 6 doubleOf("ha") // haha © Wilfred Springer, 2015 | http://www.flotsam.nl/
  12. 12. Unfiltered Anatomy The Intent (Simplified) type Intent = PartialFunction[HttpRequest, ResponseFunction] © Wilfred Springer, 2015 | http://www.flotsam.nl/
  13. 13. Unfiltered Anatomy The Intent type Intent[A,B] = PartialFunction[ HttpRequest[A], // A: some request representation ResponseFunction[B] // B: some response representation ] © Wilfred Springer, 2015 | http://www.flotsam.nl/
  14. 14. Unfiltered Anatomy The Intent { case _ => ResponseString("yay") } --------------------------------- GET / => HTTP/1.1 200 OK Content-Length: 3 Content: yay © Wilfred Springer, 2015 | http://www.flotsam.nl/
  15. 15. Unfiltered Anatomy Binding import unfiltered.filter import unfiltered.netty // Turn it into a Jetty compatible Filter val plan = filter.Planify(intent) // Turn it into something Netty compatible val plan = netty.cycle.Planify(intent) © Wilfred Springer, 2015 | http://www.flotsam.nl/
  16. 16. Unfiltered Anatomy Launching import unfiltered.jetty import unfiltered.filter import unfiltered.netty // Turn it into a Jetty compatible Filter val plan = filter.Planify(intent) jetty.Server.http(8080).plan(plan).run() // Turn it into something Netty compatible val plan = netty.cycle.Planify(intent) netty.Server.http(8080).plan(plan).run() © Wilfred Springer, 2015 | http://www.flotsam.nl/
  17. 17. ResponseFunction Simplified, and not really true trait ResponseFunction[-A] extends HttpResponse[A] => HttpResponse[A] © Wilfred Springer, 2015 | http://www.flotsam.nl/
  18. 18. ResponseFunction case … => … case … => … ~> … ~> … ------------------------------------ Ok Html(…) NotFound Html5(…) ResponseString(…) JsonContent Json(…) TextXmlContent ResponseWriter(…) Redirect(…) © Wilfred Springer, 2015 | http://www.flotsam.nl/
  19. 19. ResponseFunction Ok ~> PlainTextContent ~> ResponseString("foo") ----------------------------------------------------------- Ok resp.setStatus(SC_OK) PlainTextContent resp.setContentType("text/plain") ResponseString("foo") resp.setCharacterEncoding("UTF-8") val writer = resp.getWriter() writer.print("foo") writer.close © Wilfred Springer, 2015 | http://www.flotsam.nl/
  20. 20. ResponseFunction Composition using ~> Ok ~> PlainTextContent ~> ResponseString("foo") NotFound ~> HtmlContent ~> Html(<html> <body>Not found</body> </html>) Redirect("/index.html") © Wilfred Springer, 2015 | http://www.flotsam.nl/
  21. 21. Or rather… Scalate // index.scaml !!! 5 %html %head %body Not found // .scala import unfiltered.scalate.Scalate Ok ~> HtmlContent ~> Scalate(req, "/index.scaml") © Wilfred Springer, 2015 | http://www.flotsam.nl/
  22. 22. Matching the HttpRequest case … => … case … & … & … => GET(…) Accept(…) POST(…) UserAgent(…) DELETE(…) Host(…) PUT(…) IfModifiedSince(…) Path(…) Referer(…) © Wilfred Springer, 2015 | http://www.flotsam.nl/
  23. 23. Matching the HttpRequest Samples case _ => case GET(Path('/index')) case GET(Path(p)) if p endsWith ".json" case Path(Seg("give", "it", "to", "me" :: Nil)) case Path("/") & Params(params) case Accept("application/json") © Wilfred Springer, 2015 | http://www.flotsam.nl/
  24. 24. It's just extractors, dude object DotJson { unapply[A](req: HttpRequest[A]) = req.uri endsWith ".json" } case DotJson() => ResponseString("Json!") © Wilfred Springer, 2015 | http://www.flotsam.nl/
  25. 25. Summary so far — An Intent is just a partial function — accepting an HttpRequest, — producing a ResponseFunction — … which will in turn produce an HttpResponse — Matching based on HttpRequest extractors — Use & to compose HttpRequest extractors — Use ~> to compose ResponseFunctions © Wilfred Springer, 2015 | http://www.flotsam.nl/
  26. 26. Upping our game What else do we need? © Wilfred Springer, 2015 | http://www.flotsam.nl/
  27. 27. Reusable Composite ResponseFunctions Don't Repeat Yourself … => Ok ~> HtmlContent ~> Html(<html><body>text1</body></html>) … => Ok ~> HtmlContent ~> Html(<html><body>text2</body></html>) … => Ok ~> HtmlContent ~> Html(<html><body>text2</body></html>) // vs case class Html5(text: String) extends ComposeResponse( Ok ~> HtmlContent ~> Html(<html><body>{text}</body></html>) ) … => Html5("text1") … => Html5("text2") … => Html5("text3") © Wilfred Springer, 2015 | http://www.flotsam.nl/
  28. 28. Cross Cutting Concerns or, how to use Spring AOP © Wilfred Springer, 2015 | http://www.flotsam.nl/
  29. 29. Cross Cutting Concerns or, how to use Spring AOP © Wilfred Springer, 2015 | http://www.flotsam.nl/
  30. 30. Cross Cutting Concerns How NOT to use Spring AOP © Wilfred Springer, 2015 | http://www.flotsam.nl/
  31. 31. Cross Cutting Concerns Kit In general, a Kit is something that: — Takes an Intent (but it doesn't have to) — Produces an Intent (which it always has to) Anything that produces an Intent should be considered a Kit © Wilfred Springer, 2015 | http://www.flotsam.nl/
  32. 32. Cross Cutting Concerns Authentication import unfiltered.kit.Auth def verify(username: String, password: String): Boolean = … Auth.basic(verify) { | { case … => … | case … => … case … => … | case … => … case … => … | case … => … } | } © Wilfred Springer, 2015 | http://www.flotsam.nl/
  33. 33. Cross Cutting Concerns // Can you tell the issue with this code? import unfiltered.request._ import unfiltered.response._ val Simple = unfiltered.filter.Planify { case Path("/") & Accepts.Json(_) => JsonContent ~> ResponseString("""{ "response": "Ok" }""") } unfiltered.jetty.Server(8080).plan(Simple).run() © Wilfred Springer, 2015 | http://www.flotsam.nl/
  34. 34. Cross Cutting Concerns Directives for error handling import unfiltered.directives._, Directives._ val Smart = unfiltered.filter.Planify { Directive.Intent { case Path("/") => for { _ <- Accepts.Json } yield JsonContent ~> ResponseString("""{ "response": "Ok" }""") } } unfiltered.jetty.Server(8080).plan(Smart).run() © Wilfred Springer, 2015 | http://www.flotsam.nl/
  35. 35. Cross Cutting Concerns Directives for routing val Sweet = unfiltered.filter.Planify { Directive.Intent.Path { case "/" => for { _ <- Accepts.Json } yield JsonContent ~> ResponseString("""{ "response": "Ok" }""") } } unfiltered.jetty.Server(8080).plan(Sweet).run() © Wilfred Springer, 2015 | http://www.flotsam.nl/
  36. 36. Parameter Based Routing Primitive (never do this) case Params(ps) if ps contains "name" => val name = ps.get("name").head © Wilfred Springer, 2015 | http://www.flotsam.nl/
  37. 37. Parameter Based Routing No longer preferred // Building extractor for name parameter object Name extends Params.Extract("name", Params.first) case GET("/") & Name(name) => © Wilfred Springer, 2015 | http://www.flotsam.nl/
  38. 38. Parameter Based Routing Getting it right with Directives and Interpreters Directive.Intent { case Path("/") => for { name <- data.as.String ~> required named "name" } yield ResponseString(name) } © Wilfred Springer, 2015 | http://www.flotsam.nl/
  39. 39. © Wilfred Springer, 2015 | http://www.flotsam.nl/

×