Creación de plugins
    con Grails
  José Juan Reyes Zuñiga(@neodevelop)
Agenda

talk = new Talk()
talk.agenda{
	 quienSoy()
	 porQueCrearPlugins()
	 arquitecturaDePlugins()
	 estructuraDePlugins()
	 puntosClave()
}
¿Quién soy?

•   12 años desarrollando software

•   Varias plataformas y paradigmas

•   Co-editor y ex-podcaster de SpringHispano.org

•   Co-fundador y ex-podcaster de la comunidad GrailsMX

•   Editor de Artesanos.de/software y Scala-Mug.org

•   Fundador y podcaster de ViveCodigo

•   Apasionado de Groovy / Grails

•   Actualmente trabajo en SynergyJ.com
¿Por qué crear plugins?

•   Clásico:

    •   Modularidad

    •   Proveer funcionalidades muy puntuales

    •   Integrar librerías

•   Pero...

    •   El plugin hace lo que necesito, sin embargo...

    •   ¿Has tenido problemas al integrar algún(os) plugin(s)?

    •   ¿Lo has resuelto?

    •   ¿Que implica ser el autor de un plugin?
Arquitectura de plugins
Estructura de un plugin
Estructura de un plugin
// DOMAIN CLASSES                         // CONTROLLERS
package com.synergyj                      package com.synergyj

// Create tables in ypur app              // Warning: UrlMappings maybe mapped this in your app
class UberClass {                         class UberController {
	
	   String name                               def create = { /*...Magic...*/ }
	   // More properties...                 	   def anotherAction = { /*..More magic..*/ }
	   // A lot of fields and relations      	   // more actions
                                          }
    static constraints = {
	   	  name(blank:false,unique:true)
    }
                                                  //   TAGLIB's
}                                                 //   URL MAPPINGS
// TAGLIB's                                       //   VIEWS
package com.synergyj
                                                  //   LAYOUTS
class UberTagLib {                                //   UTILS
	   static namespace = "uber"
	                                                 //   SCRIPTS
	   def inlineAction = { attrs, body ->           //   SERVICES
	   	   // Inline functionality(abuse?)
	   	   out << body()                             //   src/java
	   }                                             //   src/groovy
}
Cuidado con la flexibilidad...
// In your app
class YourClass extends UberClass{ // <--- mmmm, I don't think so
}



 <g:resource dir="${pluginContextPath}/js" file="uber.js" />



 <g:render template="viewFolder/templateName" plugin="uber"/>



                def dependsOn = [:]
                 // Be careful
                 def pluginExcludes = [
                         "grails-app/views/error.gsp"
                 ]
ActiveMQ


Your App
                                 Mail plugin

      // Relative or absolute
      grails.plugin.location.uber = "../uber"



Your plugin
Trabajando con plugins
def doWithWebDescriptor = { xml ->
    // Modify the web descriptor at your convenience
}

def doWithSpring = {
    // Define and configure beans
}

def doWithDynamicMethods = { ctx ->
    // Add behavior to your artifacts
}

def doWithApplicationContext = { applicationContext ->
    // Spring is up? Yes, then...
}

def onChange = { event ->
    // Learn the magic, because happens some of it in here
}

def onConfigChange = { event ->
    // Magic for configuration
}
Metaprogramación
Seek and ... create...
        application.controllerClasses.each{ cClass ->
	   	   	 // Exploring controllers
	   	   	 application.isControllerClass(cClass.clazz) // True or false
	   	   	 application.getControllerClass(cClass.clazz) // Controller
	   	   	
	   	   }

        application.<artifact>Classes.each{ aClass ->
	   	   	 application.is<Artifact>Class(aClass.clazz) // True or false
	   	   	 application.get<Artifact>Class(aClass.clazz) // Controller
	   	   	
	   	   }



         // Properties of artifacts
         cClass.clazz // The class
         cClass.metaClass // cClass.clazz.metaClass
         cClass.name // name without suffix and without packages
Seek and ... create...
    application.domainClasses.each{ dClass ->
	   	   	  if(UberInterface.class.isAssignableFrom(dClass.clazz)){
	   	   	  	   println "The class " + dClass.clazz + " implements UberInterface"
	   	   	  	   dClass.clazz.metaClass {
	   	   	  	   	   'static'{ sayHiStatic{ String nombre -> "Hi static $nombre !!!" } }
	   	   	  	   	   sayHi { String nombre -> "Hi $nombre !!!" }
	   	   	  	   }
	   	   	  }
	   	   }



    import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU


//...
def isConfigurable = GCU.getStaticPropertyValue(cClass.clazz, "isConfigurable")
if(isConfigurable){
	 println "Hell Yeah!!!"
	 // Stuff with values from properties in artifacts
}
//...
Class reloading...


def observe = ["controllers"]
def observe = ["controllers","services","filters","urlMappings","hibernate","i18n"]



def onChange = { event ->
	 // event.source // class com.synergyj.MyAppController
	 // event.application // org.codehaus.groovy.grails.commons.DefaultGrailsApplication
	 // event.ctx // org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext
	 // event.manager // org.codehaus.groovy.grails.plugins.DefaultGrailsPluginManager
	 // event.plugin // org.codehaus.groovy.grails.plugins.web.ControllersGrailsPlugin
}
Testing...

class UberControllerTests extends ControllerUnitTestCase {
	
	   def uberService
	
	   protected void setUp() {
	   	   super.setUp()
	   	   mockConfig('''
      grails{
         plugins{
           yourPlugin{
             apiKey = 'YOUR_API_KEY'
           }
         }
      }
    ''')
	   }
                                 protected void setUp() {
	   void testSomething() {
                             	   	   super.setUp()
	   }
                             	   	   SpringSecurityUtils.metaClass.'static'.getSecurityConfig = {
}
                             	   	   	   [successHandler: [defaultTargetUrl: "http://localhost:8080/app"]]
                             	   	   }
                             	   }
Resumen...
• Ubica en un mismo directorio tus plugins y haz
  referencias relativas
• No hagas dependencias fuertes entre tus plugins y
  tus apps
• Trabaja con la metaprogramación, es divertido...(Usa
  la GroovyConsole)
• Explora tus clases(Introspección)
• No siempre tienes que manejar los cambios en los
  artefactos de tu aplicación
Gracias!

Q &A

Creación de plugins con Grails

  • 1.
    Creación de plugins con Grails José Juan Reyes Zuñiga(@neodevelop)
  • 2.
    Agenda talk = newTalk() talk.agenda{ quienSoy() porQueCrearPlugins() arquitecturaDePlugins() estructuraDePlugins() puntosClave() }
  • 3.
    ¿Quién soy? • 12 años desarrollando software • Varias plataformas y paradigmas • Co-editor y ex-podcaster de SpringHispano.org • Co-fundador y ex-podcaster de la comunidad GrailsMX • Editor de Artesanos.de/software y Scala-Mug.org • Fundador y podcaster de ViveCodigo • Apasionado de Groovy / Grails • Actualmente trabajo en SynergyJ.com
  • 4.
    ¿Por qué crearplugins? • Clásico: • Modularidad • Proveer funcionalidades muy puntuales • Integrar librerías • Pero... • El plugin hace lo que necesito, sin embargo... • ¿Has tenido problemas al integrar algún(os) plugin(s)? • ¿Lo has resuelto? • ¿Que implica ser el autor de un plugin?
  • 5.
  • 6.
  • 7.
    Estructura de unplugin // DOMAIN CLASSES // CONTROLLERS package com.synergyj package com.synergyj // Create tables in ypur app // Warning: UrlMappings maybe mapped this in your app class UberClass { class UberController { String name def create = { /*...Magic...*/ } // More properties... def anotherAction = { /*..More magic..*/ } // A lot of fields and relations // more actions } static constraints = { name(blank:false,unique:true) } // TAGLIB's } // URL MAPPINGS // TAGLIB's // VIEWS package com.synergyj // LAYOUTS class UberTagLib { // UTILS static namespace = "uber" // SCRIPTS def inlineAction = { attrs, body -> // SERVICES // Inline functionality(abuse?) out << body() // src/java } // src/groovy }
  • 8.
    Cuidado con laflexibilidad... // In your app class YourClass extends UberClass{ // <--- mmmm, I don't think so } <g:resource dir="${pluginContextPath}/js" file="uber.js" /> <g:render template="viewFolder/templateName" plugin="uber"/> def dependsOn = [:] // Be careful def pluginExcludes = [ "grails-app/views/error.gsp" ]
  • 9.
    ActiveMQ Your App Mail plugin // Relative or absolute grails.plugin.location.uber = "../uber" Your plugin
  • 10.
    Trabajando con plugins defdoWithWebDescriptor = { xml -> // Modify the web descriptor at your convenience } def doWithSpring = { // Define and configure beans } def doWithDynamicMethods = { ctx -> // Add behavior to your artifacts } def doWithApplicationContext = { applicationContext -> // Spring is up? Yes, then... } def onChange = { event -> // Learn the magic, because happens some of it in here } def onConfigChange = { event -> // Magic for configuration }
  • 11.
  • 12.
    Seek and ...create... application.controllerClasses.each{ cClass -> // Exploring controllers application.isControllerClass(cClass.clazz) // True or false application.getControllerClass(cClass.clazz) // Controller } application.<artifact>Classes.each{ aClass -> application.is<Artifact>Class(aClass.clazz) // True or false application.get<Artifact>Class(aClass.clazz) // Controller } // Properties of artifacts cClass.clazz // The class cClass.metaClass // cClass.clazz.metaClass cClass.name // name without suffix and without packages
  • 13.
    Seek and ...create... application.domainClasses.each{ dClass -> if(UberInterface.class.isAssignableFrom(dClass.clazz)){ println "The class " + dClass.clazz + " implements UberInterface" dClass.clazz.metaClass { 'static'{ sayHiStatic{ String nombre -> "Hi static $nombre !!!" } } sayHi { String nombre -> "Hi $nombre !!!" } } } } import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU //... def isConfigurable = GCU.getStaticPropertyValue(cClass.clazz, "isConfigurable") if(isConfigurable){ println "Hell Yeah!!!" // Stuff with values from properties in artifacts } //...
  • 14.
    Class reloading... def observe= ["controllers"] def observe = ["controllers","services","filters","urlMappings","hibernate","i18n"] def onChange = { event -> // event.source // class com.synergyj.MyAppController // event.application // org.codehaus.groovy.grails.commons.DefaultGrailsApplication // event.ctx // org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext // event.manager // org.codehaus.groovy.grails.plugins.DefaultGrailsPluginManager // event.plugin // org.codehaus.groovy.grails.plugins.web.ControllersGrailsPlugin }
  • 15.
    Testing... class UberControllerTests extendsControllerUnitTestCase { def uberService protected void setUp() { super.setUp() mockConfig(''' grails{ plugins{ yourPlugin{ apiKey = 'YOUR_API_KEY' } } } ''') } protected void setUp() { void testSomething() { super.setUp() } SpringSecurityUtils.metaClass.'static'.getSecurityConfig = { } [successHandler: [defaultTargetUrl: "http://localhost:8080/app"]] } }
  • 16.
    Resumen... • Ubica enun mismo directorio tus plugins y haz referencias relativas • No hagas dependencias fuertes entre tus plugins y tus apps • Trabaja con la metaprogramación, es divertido...(Usa la GroovyConsole) • Explora tus clases(Introspección) • No siempre tienes que manejar los cambios en los artefactos de tu aplicación
  • 17.