• DSL sobre Rack, ¡en un sólo fichero! • Rutas, filtros y helpers • Plantillas y archivos estáticos • Framework propio de tests • Aplicaciones modulares rubyconf2008.confreaks.com/lightweight-web-services.html sinatrarb.com
Torquebox (basado en JRuby) • Streaming desde el servidor * • Ecosistema algo caótico (25+ págs de resultados en RubyGems) • Sinatra sólo es un DSL • Padrino.rb para construir aplicaciones más complejas • scaffolding • plugins recomendados (persistencia, websockets, etc) github.com/nandosola/trantor-stream-uploader/blob/master/README.md *
is compelling because they are minimal: if you already know the target language and the basics of HTTP, then you can be almost instantly productive with these frameworks.” infoq.com/news/2010/10/scalatra hackage.haskell.org/package/scotty sparkjava.com … github.com/twitter/finatra
/scala/suxxorz = String • get(“/scala/*”){"Scala "+multiParams(“splat").mkString(" ")} GET /scala/is/awesome = Seq[String] • get("""^\/s(.*)/m(.*)""".r){multiParams("captures")} GET /scala/meetup = Seq[String] • CAVEAT: las rutas se evalúan desde abajo
acciones • ActionResult: corresponden a respuestas HTTP • Array[Byte]: escribe a outputStream un application/octet-stream • Unit, Any: conversión a String 3. Las excepciones en el filtro before o en las rutas se pasan a la closure error. Su resultado se devuelve como acción de la ruta 4. Filtros after 5. Renderizado de la respuesta
ayuda de ScalaZ "org.scalatra" %% "scalatra-commands" % ScalatraVersion post("/todos") { (command[CreateTodoCommand] >> (TodoData.create(_))).fold( errors => halt(400, errors), todo => redirect("/") ) } … • CAVEAT: más pensadas para validar formularios que JSON • Los objetos anidados requieren type converters • Hay Alternativas más sencillas si no se conoce ScalaZ o json4s avanzado • require() / error{ … } • github.com/wix/accord http://www.scalatra.org/2.3/guides/formats/commands.html
vía sbt package • Akka puede funcionar con JavaEE 7, pero hay que tener cuidado @Resource private ManagedExecutorService mes ActorSystem("myActorSystem", Some(config), None, Some(mes)) … implicit def executor: ExecutionContext = ExecutionContext.fromExecutorService(mes) • JAR autoejecutable • github.com/sbt/sbt-assembly • JDK 1.8 recomendado para Akka (ForkJoinPool) • Con JDK 1.7, Akka puede ir más lento
Domain classes */ case class Upstream(thingId: String, code:String, date: String) { val Pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" val dtf: DateTimeFormatter = DateTimeFormat.forPattern(Pattern) require(thingId.matches("[A-Fa-f0-9-]{8,}"), "thingId has an invalid format") require(code.matches("[0-9]{4}"), "code has an invalid format") require( try { dtf.parseDateTime(feedbackDate); true } catch { case _: Throwable => false }, "feedbackDate has an invalid format") } Esto es Java en Scala. ¿Y qué? • No hace falta saber ScalaZ para validar • Always refactor!
case e: MappingException => logger.error("Mapping exception. The request format couldn't be recognized as valid JSON", e) haltHelper(ErrorResponse(400, "Couldn't parse request")) case e: Throwable => logger.error("Unknown exception received", e) haltHelper(ErrorResponse(500, "Internal server error")) }
myRequest.things.getOrElse(myRequest.thingValues.get) match { case List() => haltHelper(ErrorResponse(400, "No things provided")) case rawItems: List[RawItem] => rawItems.par.aggregate(SuccessResponse()){ (succRes, rawItem) => rawItem.asItem match { case errItem: ErrorItem => succRes.addThing(errItem) case reqItem: RequestItem => val future = ask(repositoryActor, reqItem).mapTo[ResponseItem] Await.result(future, timeout.duration) match { case resItem => succRes.addThing(resItem) } case _ => haltHelper(ErrorResponse(400, "Unsupported thing object")) }, _ + _ } …