Introducción a Ratpack

Toolkit para desarrollo web

Miguel de la Cruz

Ingeniero en Kaleidos Open Source

- @mgdelacroix

Antes de nada

¿Qué es Ratpack?

¿Qué es Ratpack?

  • Toolkit para crear aplicaciones web
  • Basado en Netty (Non-blocking I/O framework)
  • Core implementado en Java
  • Intenta hacer sencillo desarrollar aplicaciones simples, y no ser obstrusivo cuando éstas se compliquen
  • La aplicación funciona en ~30 Mb
  • No es una aplicación con Java Servlets
  • Se despliega sin servidor de aplicaciones
  • Live Reloading a través de spring-loaded
  • Optimizado para Groovy y Java8

¿Cuándo usar Ratpack?

  • No-framework
  • Scripting
  • Microservicios

¿Cuándo usar Grails?

  • Persistencia out-of-the-box
  • Modelo muy amplio con muchos endpoints
  • Necesidad de Spring Security

Empezando a usar Ratpack

Grab


  @Grab("io.ratpack:ratpack-groovy:0.9.2")
  import static ratpack.groovy.Groovy.*
            

Cuatro líneas de DSL


  ratpack {
      handlers {

      }
  }
            

Live Coding

DSL Básico

¿Qué es un handler?

  • Unidad básica de procesamiento en ratpack
  • Interfaz a implementar
  • Manipula un contexto, que proporciona acceso a request y response

Handler básico


  ratpack{
      handlers {
          get {
              render 'Hello World!'
          }
      }
  }
            

Query params


  get {
      def name = request.queryParams.name
      render "Hi $name!"
  }
            

Path tokens


  get('name/:name') {
      def name = pathTokens.name
      render "Hi $name, this time from PathTokens!"
  }
            

Métodos HTTP


  post('name/:name?') {
      def name = pathTokens.name
      render "Hi $name, this time from PathTokens and a POST endpoint!"
  }
            

htmlBuilder


  get('something') {
      render htmlBuilder {
          head {
              title 'My website title'
          }
          body {
              h1 'Hi stranger!'
              p {
                  a href: 'http://github.com', "Let's visit GitHub!"
              }
          }
      }
  }
            

groovyTemplate

Handler


  get {
      render groovyTemplate([name: 'John Doe'], 'testTemplate.html')
  }
            

Template


  

String que viene del modelo

${model.name}

Form


  import ratpack.form.Form

  post('myform') {
      def form = parse Form
      form.get('param')
      form.file('fileName')
  }
            

JSON

Podríamos usar un módulo de Guice para Jackson

En su lugar, usaremos JsonOutput y toJson



  import static groovy.json.JsonOutput.toJson

  get {
      response.contentType 'application/json'
      render toJson(['message': 'Long live to Json!'])
  }
  get('simpler') {
      response.send 'application/json', toJson(['even': 'simpler'])
  }
            

byMethod


  handler('myendpoint') {
      byMethod {
          get {
              response.send 'application/json', toJson(['method': 'GET'])
          }
          post {
              response.send 'application/json', toJson(['method': 'POST'])
          }
      }
  }
            

byContent


  get {
      byContent {
          json {
              render 'JSON'
          }
          plainText {
              render 'TEXT'
          }
          html {
              render 'HTML'
          }
      }
  }
            

Prefix


  prefix('api') {
      get('end') {
          render 'end'
      }
      get('point') {
          render 'point'
      }
  }
            

Assets


  ratpack {
      handlers {
          assets("public")
      }
  }
            

Total

Ya tenemos algo funcionando

Ratpack y gradle

Más allá del script

Podemos integrar nuestra aplicación con gradle a través del plugin ratpack-groovy

Documentación

LazyBones al rescate

Herramienta de creación de proyectos a partir de plantillas


  lazybones create [ratpack template] [ratpack version] [app name]
            

Project structure


         src
          |
          +- ratpack
          |     |
          |     +- ratpack.groovy
          |     +- ratpack.properties
          |     +- public          // Static assets in here
          |
          +- main
          |   |
          |   +- groovy
          |        |
          |        +- // App classes in here!
          |
          +- test
              |
              +- groovy
                   |
                   +- // Spock tests in here!
            

build.gradle


  apply plugin: "ratpack-groovy"

  buildscript {
      repositories {
          jcenter()
      }
      dependencies {
          classpath "io.ratpack:ratpack-gradle:0.9.2"
      }
  }

  repositories {
      jcenter()
      maven { url "http://repo.springsource.org/repo" } // for springloaded
  }

  dependencies {
      testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
      // SpringLoaded enables runtime hot reloading.
      springloaded "org.springsource.springloaded:springloaded-core:1.1.1"
  }
            

ratpack.properties

  • Debe existir vacío.
  • Podemos configurar HandlerFactory, el puerto...
  • No existe documentación
  • Podemos basarnos en el JavaDoc de LaunchConfig

ratpack.groovy


  ratpack {
      handlers {

      }
  }
            

Let's do this!

Inyección de dependencias con

Google Guice

Google Guice

  • Jar: ratpack-guice
  • Dos usos principales
    • Inyección de dependencias
    • Modularización

Show me the code!

example-ratpack-gradle-groovy-app

ScriptExecutionModule.groovy


  class ScriptExecutionModule extends AbstractModule {

      @Override
      protected void configure() {
          bind(ScriptExecutor).to(GroovyScriptExecutor)
      }

  }
            

GroovyScriptExecutor.groovy


  class GroovyScriptExecutor implements ScriptExecutor {

      ScriptResult execute(String scriptText) {
          ...
      }

  }
            

ratpack.groovy


  ratpack {

      modules {
          register new ScriptExecutionModule()
      }

      post("execute") { ScriptExecutor scriptExecutor ->
          def form = parse form()
          def script = form.script
          render scriptExecutor.execute(script)
      }

  }
            

Ratpack internals

Warning!

Hay poca documentación en "prosa".

Javadoc es tu amigo.

Handler

  • Unidad de procesamiento en Ratpack
  • El punto de entrada de la aplicación es un Handler
  • Interfaz a implementar
  • Reciben un contexto

Ejemplo de Handler


  import ratpack.handling.Handler
  import ratpack.handling.Context

  class TestHandler implements Handler {
      void handle(Context context) {
          context.render "Hello World!"
      }
  }
            

Se pueden encadenar


  get 'chain', new FirstHandler()

  class FirstHandler implements Handler {
      void handle(Context context) {
          println 'Soy el primer handler'
          context.insert new SecondHandler()
      }
  }

  class SecondHandler implements Handler {
      void handle(Context context) {
          println 'Soy el segundo handler'
          context.render 'Response!'
      }
  }

  // == Output GET /chain ==
  // Soy el primer handler
  // Soy el segundo handler
  // [200] Response!
            

Si ningún handler envía un response...

El último handler es siempre uno interno que envía un 404


  context.clientError 404
            

Context

  • Proporciona acceso a request y response
  • Los handlers reciben context y se lo pasan al siguiente
  • Podemos almacenar variables utilizando registry

Ejemplo de uso de registry


  class MyHandler implements Handler {
      void handle(Context context) {
          // get some data
          Map myData = ['message': "hoy es ${new Date()}"]
          // delegates on JSON
          context.insert(registry(myData), new ToJson())
      }
  }

  class ToJson implements Handler {
      void handle(Context context) {
          def myData = context.get(LinkedHashMap.class)

          context.response.contentType 'application/json'
          context.render toJson(myData)
      }
  }
            

Testing en Ratpack

Testing

  • Ratpack no está ligado a ningún framework
  • Se recomienda usar spock
  • El plugin de gradle prepara el proyecto para testing

Tipos de testing


  • Unitario
    • UnitTest.invoke()
    • GroovyUnitTest.invoke()

  • Funcional
    • ApplicationUnderTest
    • TestHttpClient

Ejemplo

Handler


  class MyHandler implements Handler {
      void handle(Context context) {
          context.with {
              def outputHeader = request.headers.get("input-value") + ":bar"

              response.headers.set("output-value", outputHeader)
              render "received: " + request.path
          }
      }
  }
            

Ejemplo

Test


  import static ratpack.groovy.test.GroovyUnitTest.invoke

  def invocation = invoke(new MyHandler()) {
      header "input-value", "foo"
      uri "some/path"
  }

  assert invocation.rendered(String) == "received: some/path"
  assert invocation.headers.get("output-value") == "foo:bar"
            

Desplegando Ratpack

Facts

  • Las aplicaciones de Ratpack son autocontenidas
  • No es necesario utilizar un servidor de apliaciones
  • ... aunque es posible a través de un plugin de gradle
  • Consumo de memoria muy bajo

Tareas de gradle


  # Genera la estructura de la aplicación
  # en build/install/$applicationName
  gradle installApp

  # Comprime la aplicación en un fichero .zip
  gradle distZip

  # Comprime la aplicación en un fichero .tar
  gradle distTar
            

Aplicación

Carpeta app

  • ratpack.groovy & ratpack.properties
  • /templates
  • /public

Aplicación

Carpeta bin

Scripts de shell y batch que settean el classpath y ejecutan la aplicación

Aplicación

Carpeta lib

  • Dependencias
  • "${applicationName}.jar"

Conclusiones

Ligero

Consume pocos recursos

Capaz

  • HTML templates
  • REST
  • Inyección de dependencias
  • Testeable

Toolkit != Framework

No toma decisiones por ti

Divertido

Q&A

Gracias :D

Introducción a Ratpack

By mgdelacroix

Introducción a Ratpack

  • 1,235