SlideShare uma empresa Scribd logo
1 de 32
Creación de builders y
  DSL's con Groovy
    @neodevelop - @synergyj
Agenda
Groovy
Metaprogramación
Builders
DSL
{}
{}
Metaprogramación

•   Programas que escriben
    programas...

•   Soporte para agregar
    métodos y propiedades
    a un objeto en tiempo
    de ejecución...

•   Soporte de intercepción
    de llamadas, como AOP
Meta-object protocol
“Interpreta la semantica de un programa
abierto y extensible. Determina que
significa un programa y que
comportamiento tiene, así también, maneja
objetos que manipulan, crean, describen o
implementan otros objetos”
En Groovy podemos usar el MOP para
invocar métodos dinámicamente y
sintetizar clases y métodos al vuelo
MOP de Groovy
Todos los accesos a los métodos,
propiedades, constructores, operadores,
etc., pueden ser interceptados
El comportamiento en Java esta
fuertemente atado al tiempo de
compilación, en Groovy, el comportamiento
es adaptable en tiempo de ejecución
Meta-object Protocol
GroovyObject
 En Groovy se trabaja con 3 tipos de
 objetos:
    POJOs
    POGOs
    Groovy Interceptors
GroovyObject
package groovy.lang;

public interface GroovyObject {
    Object invokeMethod(String name, Object args);
    Object getProperty(String propertyName);
    void setProperty(String propertyName, Object newValue);
    MetaClass getMetaClass();
    void setMetaClass(MetaClass metaClass);
}
MetaClass
public interface MetaClass extends MetaObjectProtocol {
     Object invokeMethod(...);
     Object getProperty(...);
     void setProperty(...);
     Object invokeMissingMethod(...);
     Object invokeMissingProperty(...);
     Object getAttribute(...);
     void setAttribute(...);
     void initialize();
     List<MetaProperty> getProperties();
     List<MetaMethod> getMethods();
     ClassNode getClassNode();
     List<MetaMethod> getMetaMethods();
     int selectConstructorAndTransformArguments(...);
}
Intercepción de métodos - GroovyInterceptable
   // Definamos una clase que implemente GroovyInterceptable
   class ClaseInterceptada implements GroovyInterceptable{
   	
   	   def doMetodo(param){
   	   	   "Hola $param"
   	   }
   	
   	   //Tenemos que implementar este metodo de la interfaz
   	   def invokeMethod(String nombre, args){
   	   	   System.out.println "Ejecutando el metodo '$nombre' con argumentos '$args'"
   	   	   //Obtenemos el metodo a ejecutar de la clase
   	   	   def metodoValido = ClaseInterceptada.metaClass.getMetaMethod(nombre,args)
   	   	   //Si encontro el metodo a ejecutar...
   	   	   if(metodoValido != null){
   	   	   	   //Lo invocamos desde el metodo que encontro
   	   	   	   metodoValido.invoke(this, args)
   	   	   }else{
   	   	   	   //Si no lo encuentra simplemente llamamos al mŽtodo convencionalmente
   	   	   	   ClaseInterceptada.metaClass.invokeMethod(this, nombre, args)
   	   	   }
   	   }
   }

   def c = new ClaseInterceptada()
   println c.doMetodo("@grailsmx")
Intercepción de métodos - ExpandoMetaClass
   //Ahora usemos un interceptor en una clase que no es de nosotros
   Float.metaClass.invokeMethod = { String nombre, args ->
   	   //Al igual desplegamos algo de informacion
   	   System.out.println "Ejecutando el metodo '$nombre' con argumentos '$args'"
   	   //Obtenemos el metodo del metaClass
   	   def metodoValido = Float.metaClass.getMetaMethod(nombre,args)
   	   //Si no existe dicho metodo
   	   if(metodoValido == null){
   	   	   //Entonces regresamos la ejecucion convencional
   	   	   return Float.metaClass.invokeMissingMethod(delegate,nombre,args)
   	   }
   	   //Invocamos al metodo original con sus parametros
   	   resultado = metodoValido.invoke(delegate,args)
   	   //Regresamos la ejecucion del metodo
   	   resultado
   }

   //Usemos los metodos de la clase que no es propietaria
   println 10F.intValue()
   println 100F.toString()
   try{
   	   50F.empty()
   }catch(Exception e){
   	   println e.message
   }
MOP - Inyección de métodos(Categorías)
  //Definimos la clase que permitira ser la categoria
  class VerificaGramatica{
  	 //Para que un metodo pueda ser categorizado, debe ser static
  	 def static esPalindrome(String frase){
  	 	 //Implementamos nuestra categoria
  	 	 if(frase == new StringBuilder(frase).reverse().toString())
  	 	 	 true
  	 	 else
  	 	 	 false	
  	 }
  }

  //Con ayuda de la palabra reservada 'use' aplicamos la categoria
  use(VerificaGramatica){
  	 def frase = "anitalavalatina"
  	 println frase.class.name
  	 println "Es palindrome?: " + frase.esPalindrome()
  	 println frase.class.name
  }
MOP - Inyección de métodos(ExpandoMetaClass)


         //Podemos inyectar métodos estaticos
         Integer.metaClass.esPar = { ->
            delegate % 2 == 0
         }
         //Probemos nuestro método estatico
         println "2 es Par? " + 2.esPar()
         println "3 es Par? " + 3.esPar()
MOP - Síntesis de métodos(MethodMissing)
    class Persona{
    	   String nombre
    	   Map relaciones = [:]
    	
    	   def methodMissing(String relacion, personas){
    	   	   if(relaciones.containsKey(relacion)){
    	   	   	   personas.each{ persona ->
    	   	   	   	   relaciones.get(relacion).add(persona)
    	   	   	   }
    	   	   }else{
    	   	   	   relaciones.put(relacion,personas as List)
    	   	   }
    	   }
    }

    def juan = new Persona(nombre:'Juan')
    juan.trabajaEn("SynergyJ")
    juan.trabajaCon("Manuel","Jorge")
    juan.esAmigoDe("Domingo")
    juan.trabajaCon("Andres")
    juan.esAmigoDe("George")
    juan.conoceA("Susana","Perla","Cassandra","Alejandra")

    println juan.relaciones
MOP - Síntesis de métodos(ExpandoMetaClass)
     import java.text.NumberFormat

     def valoresDeConversion = ['USD':0.07973, 'EUR':0.0644,
                          'GBP':0.0538, 'JPY':7.2361]

     BigDecimal.metaClass.methodMissing = { String methodName, args ->
         tipoDeConversion = methodName[2..-1]
         valorDeConversion = valoresDeConversion[tipoDeConversion]

         if(valorDeConversion){
             NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US)
             nf.setCurrency(Currency.getInstance(tipoDeConversion))
             return nf.format(delegate * valorDeConversion)
         }

         "No hay conversion de Pesos a ${tipoDeConversion}"
     }

     println 13.00.enUSD()
     println 16.00.enEUR()
     println 1.00.enXYZ()
Creación dinámica de clases con Expando
  def file = new File("migracion.csv")
  /*
   * Empresa,Nombre,Apellido,Puesto,correo...
   * 42 Claps,CŽsar,Salazar Hern‡ndez,CEO,c@42claps.com...
   * 4D Hispano,Dominique,Coste,Country Manager,d@4dhispano.com...
   */
  def sql = groovy.sql.Sql.newInstance("jdbc:hsqldb:file:/devDB","sa","","org.hsqldb.jdbcDriver")

  lines = file.readLines()
  atributos = lines[0].tokenize(",")
  //Creaci—n de Expando
  def persona = new Expando()
  atributos.each{
  	   //Creando atributos con Expando, tmb se pueden crear mŽtodos
  	   persona."${it.toLowerCase()}" = "${it.toLowerCase()}"
  }

  def insert = "insert into user(VERSION,PASSWD,ENABLED,USERNAME,EMAIL_SHOW,EMAIL,USER_REAL_NAME) "
  	   insert += "values(0,'7c4a8d09ca3762af61e59520943dc26494f8941b',true,?,true,?,?)"

  lines.each{ line ->
  	   valores = line.tokenize(",")
  	   index = 0
  	   atributos.each{
  	   	    persona."${it.toLowerCase()}" = "${valores[index]}"
  	   	    index++
  	   }
  	   //Uso de Expando
  	   sql.execute(insert,[persona.correo,persona.correo,"${persona.nombre} ${persona.apellido}"])
  }
¿Qué es un
 builder?
Builders
Son DSL’s internos que proveen trabajar facilmente
con ciertos tipos de problemas
De entrada, si tenemos la necesidad de trabajar
con ciertos tipos de estructuras o
representaciones, los builders son la mejor opción
Proveen una sintaxis que no ata con dicha
estructura o implementación
Solo son fachadas, por que en realidad no están
reemplazando la implementación, solamente
proveen una manera elegante de usarla
Groovy ya provee algunos, pero podemos hacer
los propios
Metaprogramación   Builders   BuilderSupport
//Instanciamos un builder
sqlBuilder = new MiSqlBuilder()
//Ejecutamos nuestro builder
sqlBuilder.build{
	 selecciona("campo1","campo2","campo3")
	 tabla("nombreDeTabla")
	 donde("1=1")
	 insertar('tabla',['campo1','campo2','campo3'],['valor1','valor2','valor3'])
	 ultimoInsert
}
class MiSqlBuilder{
	   def result = new StringWriter()
	   def build(closure){
	   	   closure.delegate = this
	   	   closure()
	   	   println result
	   }

	   def   methodMissing(String name,args){
	   	     switch(name){
	   	     	   case 'selecciona':
	   	     	   	   	   result << "nSELECT ${args.join(',')}"
	   	     	   	   break
	   	     	   case 'tabla':
	   	     	   	   	   result << " FROM ${args.join(',')}"
	   	     	   	   break
	   	     	   case 'donde':
	   	     	   	   	   result << " WHERE ${args[0]}n"
	   	     	   	   break
	   	     	   case 'insertar':
	   	     	   	   	   result << "nINSERT INTO ${args[0]}(${args[1].join(',')}) "
	   	     	   	   	   result << "VALUES('${args[2].join('','')}')n"
	   	     	   	   break
	   	     }
	   }

	   def   propertyMissing(String name){
	   	     if(name=="ultimoInsert"){
	   	     	   result << "nSELECT last_insert_id() n"
	   	     }
	   }
}
Lenguajes de dominio específico
DSL
Están enfocados a un cierto tipo de problema
La sintaxis está orientada al negocio(hay
expertos)
No lo usamos para resolver problemas de
propósito general como lo haría Java
Es pequeño, simple, expresivo y enfocado a
cierta área
Son manejados por el contexto y elocuentes
Con ayuda del MOP podemos crear DSL’s
¿Cómo desarrollar un DSL?
 Tipado dinámico y opcional
 La facilidad de usar Scripts
 ExpandoMetaClass
 Closures
 Sobrecarga de operadores
 Soporte de Builders
 Work-around de paréntesis
tomar 4.pastillas, de: lsd, en: 12.horas
class Droga {
	   String nombre
	   String toString() { nombre
	   }
}

class CantidadDeDroga {
	   int cantidad
	   String toString() {
	   	   cantidad == 1 ? "1 pastilla" : "$cantidad pastillas"
	   }
}

class TiempoDeDuracion {
	   Number numero
	   String unidad
}

Integer.metaClass.getPastillas = { -> new CantidadDeDroga(cantidad: delegate) }
Number.metaClass.getHoras = { -> new TiempoDeDuracion(numero: delegate, unidad:
"horas") }

def tomar(Map m, CantidadDeDroga cdd) {
	   println "Tomar $cdd de $m.de en $m.en.numero $m.en.unidad"
}

def oxicodona = new Droga(nombre: "Oxicodona")
def lsd = new Droga(nombre: "LSD")

tomar 2.pastillas, de: oxicodona, en: 6.horas
tomar 4.pastillas, de: lsd, en: 12.horas
Referencias
           groovy.codehaus.org
        grails.org.mx - @grailsmx
http://delicious.com/neodevelop/groovy
   Programming Groovy - Venkat S.
¡Gracias!
  Q &A
@neodevelop

Mais conteúdo relacionado

Mais procurados

Gulp js php sevilla 28 septiembre 2016
Gulp js   php sevilla 28 septiembre 2016Gulp js   php sevilla 28 septiembre 2016
Gulp js php sevilla 28 septiembre 2016Agencia INNN
 
SCJP, Clase 3: Asignaciones
SCJP, Clase 3: AsignacionesSCJP, Clase 3: Asignaciones
SCJP, Clase 3: Asignacionesflekoso
 
SCJP, Clase 8: Inner Classes
SCJP, Clase 8: Inner ClassesSCJP, Clase 8: Inner Classes
SCJP, Clase 8: Inner Classesflekoso
 
Clase n°2 3-4 java
Clase n°2 3-4 javaClase n°2 3-4 java
Clase n°2 3-4 javajbersosa
 
SCJP, Clase 7: Generics
SCJP, Clase 7: GenericsSCJP, Clase 7: Generics
SCJP, Clase 7: Genericsflekoso
 
SCJP, Clase 6: Collections
SCJP, Clase 6: CollectionsSCJP, Clase 6: Collections
SCJP, Clase 6: Collectionsflekoso
 
Clases metodos y atributos
Clases metodos y atributosClases metodos y atributos
Clases metodos y atributosRobert Wolf
 
SCJP, Clase 1: Introducción al curso, Intro a Java, Declaración y Control de ...
SCJP, Clase 1: Introducción al curso, Intro a Java, Declaración y Control de ...SCJP, Clase 1: Introducción al curso, Intro a Java, Declaración y Control de ...
SCJP, Clase 1: Introducción al curso, Intro a Java, Declaración y Control de ...flekoso
 
Elementos de script en JSP
Elementos de script en JSPElementos de script en JSP
Elementos de script en JSPjubacalo
 
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Ignacio Martín
 
Guia de estudio OCA Java SE 7 Programmer
Guia de estudio OCA Java SE 7 ProgrammerGuia de estudio OCA Java SE 7 Programmer
Guia de estudio OCA Java SE 7 ProgrammerOscar V
 
Practica utilizacion de beans en jsp
Practica  utilizacion de beans en jspPractica  utilizacion de beans en jsp
Practica utilizacion de beans en jspBoris Salleg
 
SCJP, Clase 4: Operadores
SCJP, Clase 4: OperadoresSCJP, Clase 4: Operadores
SCJP, Clase 4: Operadoresflekoso
 

Mais procurados (20)

Gulp js php sevilla 28 septiembre 2016
Gulp js   php sevilla 28 septiembre 2016Gulp js   php sevilla 28 septiembre 2016
Gulp js php sevilla 28 septiembre 2016
 
10 sintaxis oo
10 sintaxis oo10 sintaxis oo
10 sintaxis oo
 
SCJP, Clase 3: Asignaciones
SCJP, Clase 3: AsignacionesSCJP, Clase 3: Asignaciones
SCJP, Clase 3: Asignaciones
 
Empezando con Angular 2
Empezando con Angular 2Empezando con Angular 2
Empezando con Angular 2
 
SCJP, Clase 8: Inner Classes
SCJP, Clase 8: Inner ClassesSCJP, Clase 8: Inner Classes
SCJP, Clase 8: Inner Classes
 
Angular Conceptos Practicos 2
Angular Conceptos Practicos 2Angular Conceptos Practicos 2
Angular Conceptos Practicos 2
 
9.herencia en java
9.herencia en java9.herencia en java
9.herencia en java
 
Clase n°2 3-4 java
Clase n°2 3-4 javaClase n°2 3-4 java
Clase n°2 3-4 java
 
Advanced angular 2
Advanced angular 2Advanced angular 2
Advanced angular 2
 
SCJP, Clase 7: Generics
SCJP, Clase 7: GenericsSCJP, Clase 7: Generics
SCJP, Clase 7: Generics
 
SCJP, Clase 6: Collections
SCJP, Clase 6: CollectionsSCJP, Clase 6: Collections
SCJP, Clase 6: Collections
 
Semana 6 Módulos Clases y Objetos
Semana 6   Módulos Clases y ObjetosSemana 6   Módulos Clases y Objetos
Semana 6 Módulos Clases y Objetos
 
Clases metodos y atributos
Clases metodos y atributosClases metodos y atributos
Clases metodos y atributos
 
SCJP, Clase 1: Introducción al curso, Intro a Java, Declaración y Control de ...
SCJP, Clase 1: Introducción al curso, Intro a Java, Declaración y Control de ...SCJP, Clase 1: Introducción al curso, Intro a Java, Declaración y Control de ...
SCJP, Clase 1: Introducción al curso, Intro a Java, Declaración y Control de ...
 
Elementos de script en JSP
Elementos de script en JSPElementos de script en JSP
Elementos de script en JSP
 
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
 
Guia de estudio OCA Java SE 7 Programmer
Guia de estudio OCA Java SE 7 ProgrammerGuia de estudio OCA Java SE 7 Programmer
Guia de estudio OCA Java SE 7 Programmer
 
Practica utilizacion de beans en jsp
Practica  utilizacion de beans en jspPractica  utilizacion de beans en jsp
Practica utilizacion de beans en jsp
 
SCJP, Clase 4: Operadores
SCJP, Clase 4: OperadoresSCJP, Clase 4: Operadores
SCJP, Clase 4: Operadores
 
Java beans en jsp
Java beans en jspJava beans en jsp
Java beans en jsp
 

Destaque (8)

Agile Software Development
Agile Software DevelopmentAgile Software Development
Agile Software Development
 
Equipos en Scrum ¡oing!
Equipos en Scrum ¡oing!Equipos en Scrum ¡oing!
Equipos en Scrum ¡oing!
 
Herramientas para el desarrollo de software
Herramientas para el desarrollo de softwareHerramientas para el desarrollo de software
Herramientas para el desarrollo de software
 
Webapps testing with Groovy
Webapps testing with GroovyWebapps testing with Groovy
Webapps testing with Groovy
 
Groovy in Java7mx
Groovy in Java7mxGroovy in Java7mx
Groovy in Java7mx
 
A lil' of Groovy for Ruby developers
A lil' of Groovy for Ruby developersA lil' of Groovy for Ruby developers
A lil' of Groovy for Ruby developers
 
El desarrollador de software
El desarrollador de softwareEl desarrollador de software
El desarrollador de software
 
Apps software development with Vert.X
Apps software development with Vert.XApps software development with Vert.X
Apps software development with Vert.X
 

Semelhante a Creación de Builders y DSL's con Groovy

Javascript Básico
Javascript BásicoJavascript Básico
Javascript Básicocamposer
 
UTPL-PROGRAMACIÓN AVANZADA-II-BIMESTRE-(OCTUBRE 2011-FEBRERO 2012)
UTPL-PROGRAMACIÓN AVANZADA-II-BIMESTRE-(OCTUBRE 2011-FEBRERO 2012)UTPL-PROGRAMACIÓN AVANZADA-II-BIMESTRE-(OCTUBRE 2011-FEBRERO 2012)
UTPL-PROGRAMACIÓN AVANZADA-II-BIMESTRE-(OCTUBRE 2011-FEBRERO 2012)Videoconferencias UTPL
 
Fundamento de poo en php
Fundamento de poo en phpFundamento de poo en php
Fundamento de poo en phpRobert Moreira
 
Rompiendo dependencias contenidas en ensamblados .NET mediante la refactoriza...
Rompiendo dependencias contenidas en ensamblados .NET mediante la refactoriza...Rompiendo dependencias contenidas en ensamblados .NET mediante la refactoriza...
Rompiendo dependencias contenidas en ensamblados .NET mediante la refactoriza...jaircazarin
 
6 Upv Solo Pruebas 2009
6 Upv Solo Pruebas 20096 Upv Solo Pruebas 2009
6 Upv Solo Pruebas 2009Pepe
 
Java 8 introducción a expresiones lambdas y api stream
Java 8  introducción a expresiones lambdas y api streamJava 8  introducción a expresiones lambdas y api stream
Java 8 introducción a expresiones lambdas y api streamEudris Cabrera
 
Intro aplicaciones web con php
Intro aplicaciones web con phpIntro aplicaciones web con php
Intro aplicaciones web con phpFer Nando
 
Presentacion Java
Presentacion JavaPresentacion Java
Presentacion Javamaeusogo
 
Parte II. Notas Rapidas (sticky notes) App W8: MVVM y SQLite.
Parte II. Notas Rapidas (sticky notes) App W8: MVVM y SQLite.Parte II. Notas Rapidas (sticky notes) App W8: MVVM y SQLite.
Parte II. Notas Rapidas (sticky notes) App W8: MVVM y SQLite.Juan Manuel
 
Presentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic ClubPresentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic ClubGlobalLogic Latinoamérica
 
Curso de programación iPhone: Cocoa-Touch
Curso de programación iPhone: Cocoa-TouchCurso de programación iPhone: Cocoa-Touch
Curso de programación iPhone: Cocoa-Touchfrr149
 

Semelhante a Creación de Builders y DSL's con Groovy (20)

Javascript Básico
Javascript BásicoJavascript Básico
Javascript Básico
 
UTPL-PROGRAMACIÓN AVANZADA-II-BIMESTRE-(OCTUBRE 2011-FEBRERO 2012)
UTPL-PROGRAMACIÓN AVANZADA-II-BIMESTRE-(OCTUBRE 2011-FEBRERO 2012)UTPL-PROGRAMACIÓN AVANZADA-II-BIMESTRE-(OCTUBRE 2011-FEBRERO 2012)
UTPL-PROGRAMACIÓN AVANZADA-II-BIMESTRE-(OCTUBRE 2011-FEBRERO 2012)
 
Fundamento de poo en php
Fundamento de poo en phpFundamento de poo en php
Fundamento de poo en php
 
2 Introducción al lenguaje Ruby
2 Introducción al lenguaje Ruby2 Introducción al lenguaje Ruby
2 Introducción al lenguaje Ruby
 
Rompiendo dependencias contenidas en ensamblados .NET mediante la refactoriza...
Rompiendo dependencias contenidas en ensamblados .NET mediante la refactoriza...Rompiendo dependencias contenidas en ensamblados .NET mediante la refactoriza...
Rompiendo dependencias contenidas en ensamblados .NET mediante la refactoriza...
 
6 Upv Solo Pruebas 2009
6 Upv Solo Pruebas 20096 Upv Solo Pruebas 2009
6 Upv Solo Pruebas 2009
 
Ruby para Java Developers
Ruby para Java DevelopersRuby para Java Developers
Ruby para Java Developers
 
Java 8 introducción a expresiones lambdas y api stream
Java 8  introducción a expresiones lambdas y api streamJava 8  introducción a expresiones lambdas y api stream
Java 8 introducción a expresiones lambdas y api stream
 
Intro aplicaciones web con php
Intro aplicaciones web con phpIntro aplicaciones web con php
Intro aplicaciones web con php
 
MéTodos
MéTodosMéTodos
MéTodos
 
Presentacion Java
Presentacion JavaPresentacion Java
Presentacion Java
 
4 Introducción al lenguaje Scala
4 Introducción al lenguaje Scala4 Introducción al lenguaje Scala
4 Introducción al lenguaje Scala
 
Parte II. Notas Rapidas (sticky notes) App W8: MVVM y SQLite.
Parte II. Notas Rapidas (sticky notes) App W8: MVVM y SQLite.Parte II. Notas Rapidas (sticky notes) App W8: MVVM y SQLite.
Parte II. Notas Rapidas (sticky notes) App W8: MVVM y SQLite.
 
MéTodos
MéTodosMéTodos
MéTodos
 
MéTodos
MéTodosMéTodos
MéTodos
 
P2C2 Introducción a JEE5
P2C2 Introducción a JEE5P2C2 Introducción a JEE5
P2C2 Introducción a JEE5
 
Presentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic ClubPresentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic Club
 
Curso de programación iPhone: Cocoa-Touch
Curso de programación iPhone: Cocoa-TouchCurso de programación iPhone: Cocoa-Touch
Curso de programación iPhone: Cocoa-Touch
 
Jquery parte 1
Jquery parte 1Jquery parte 1
Jquery parte 1
 
Introducción a Scala
Introducción a ScalaIntroducción a Scala
Introducción a Scala
 

Mais de Jose Juan R. Zuñiga

Mirando hacia atrás: Retrospectivas
Mirando hacia atrás: RetrospectivasMirando hacia atrás: Retrospectivas
Mirando hacia atrás: RetrospectivasJose Juan R. Zuñiga
 
Integrando Groovy & Grails en el proceso de desarrollo
Integrando Groovy & Grails en el proceso de desarrolloIntegrando Groovy & Grails en el proceso de desarrollo
Integrando Groovy & Grails en el proceso de desarrolloJose Juan R. Zuñiga
 
El proceso de desarrollo con herramientas Open Source
El proceso de desarrollo con herramientas Open SourceEl proceso de desarrollo con herramientas Open Source
El proceso de desarrollo con herramientas Open SourceJose Juan R. Zuñiga
 
15a. Reunion de SpringHispano.org y grails.org.mx
15a. Reunion de SpringHispano.org y grails.org.mx15a. Reunion de SpringHispano.org y grails.org.mx
15a. Reunion de SpringHispano.org y grails.org.mxJose Juan R. Zuñiga
 
Introduccion a grails en Campusparty
Introduccion a grails en CampuspartyIntroduccion a grails en Campusparty
Introduccion a grails en CampuspartyJose Juan R. Zuñiga
 
Desarrollo en 4G(Groovy, Grails, Git, GoogleAppEngine)
Desarrollo en 4G(Groovy, Grails, Git, GoogleAppEngine)Desarrollo en 4G(Groovy, Grails, Git, GoogleAppEngine)
Desarrollo en 4G(Groovy, Grails, Git, GoogleAppEngine)Jose Juan R. Zuñiga
 

Mais de Jose Juan R. Zuñiga (11)

Mirando hacia atrás: Retrospectivas
Mirando hacia atrás: RetrospectivasMirando hacia atrás: Retrospectivas
Mirando hacia atrás: Retrospectivas
 
Integrando Groovy & Grails en el proceso de desarrollo
Integrando Groovy & Grails en el proceso de desarrolloIntegrando Groovy & Grails en el proceso de desarrollo
Integrando Groovy & Grails en el proceso de desarrollo
 
El proceso de desarrollo con herramientas Open Source
El proceso de desarrollo con herramientas Open SourceEl proceso de desarrollo con herramientas Open Source
El proceso de desarrollo con herramientas Open Source
 
15a. Reunion de SpringHispano.org y grails.org.mx
15a. Reunion de SpringHispano.org y grails.org.mx15a. Reunion de SpringHispano.org y grails.org.mx
15a. Reunion de SpringHispano.org y grails.org.mx
 
Introducción a Groovy
Introducción a GroovyIntroducción a Groovy
Introducción a Groovy
 
Introduccion a grails en Campusparty
Introduccion a grails en CampuspartyIntroduccion a grails en Campusparty
Introduccion a grails en Campusparty
 
Desarrollo en 4G(Groovy, Grails, Git, GoogleAppEngine)
Desarrollo en 4G(Groovy, Grails, Git, GoogleAppEngine)Desarrollo en 4G(Groovy, Grails, Git, GoogleAppEngine)
Desarrollo en 4G(Groovy, Grails, Git, GoogleAppEngine)
 
Redefiniendo el MVC con Grails
Redefiniendo el MVC con GrailsRedefiniendo el MVC con Grails
Redefiniendo el MVC con Grails
 
Spring
SpringSpring
Spring
 
Spring Mvc Final
Spring Mvc FinalSpring Mvc Final
Spring Mvc Final
 
Creación de Plataformas
Creación de PlataformasCreación de Plataformas
Creación de Plataformas
 

Último

Trabajando con Formasy Smart art en power Point
Trabajando con Formasy Smart art en power PointTrabajando con Formasy Smart art en power Point
Trabajando con Formasy Smart art en power PointValerioIvanDePazLoja
 
Trabajo de tecnología excel avanzado.pdf
Trabajo de tecnología excel avanzado.pdfTrabajo de tecnología excel avanzado.pdf
Trabajo de tecnología excel avanzado.pdfedepmariaperez
 
Herramientas que posibilitan la información y la investigación.pdf
Herramientas que posibilitan la información y la investigación.pdfHerramientas que posibilitan la información y la investigación.pdf
Herramientas que posibilitan la información y la investigación.pdfKarinaCambero3
 
_Planificacion Anual NTICX 2024.SEC.21.4.1.docx.pdf
_Planificacion Anual NTICX 2024.SEC.21.4.1.docx.pdf_Planificacion Anual NTICX 2024.SEC.21.4.1.docx.pdf
_Planificacion Anual NTICX 2024.SEC.21.4.1.docx.pdfBetianaJuarez1
 
LUXOMETRO EN SALUD OCUPACIONAL(FINAL).ppt
LUXOMETRO EN SALUD OCUPACIONAL(FINAL).pptLUXOMETRO EN SALUD OCUPACIONAL(FINAL).ppt
LUXOMETRO EN SALUD OCUPACIONAL(FINAL).pptchaverriemily794
 
Slideshare y Scribd - Noli Cubillan Gerencia
Slideshare y Scribd - Noli Cubillan GerenciaSlideshare y Scribd - Noli Cubillan Gerencia
Slideshare y Scribd - Noli Cubillan Gerenciacubillannoly
 
TALLER DE ANALISIS SOLUCION PART 2 (1)-1.docx
TALLER DE ANALISIS SOLUCION  PART 2 (1)-1.docxTALLER DE ANALISIS SOLUCION  PART 2 (1)-1.docx
TALLER DE ANALISIS SOLUCION PART 2 (1)-1.docxobandopaula444
 
La tecnología y su impacto en la sociedad
La tecnología y su impacto en la sociedadLa tecnología y su impacto en la sociedad
La tecnología y su impacto en la sociedadEduardoSantiagoSegov
 
Modelo de Presentacion Feria Robotica Educativa 2024 - Versión3.pptx
Modelo de Presentacion Feria Robotica Educativa 2024 - Versión3.pptxModelo de Presentacion Feria Robotica Educativa 2024 - Versión3.pptx
Modelo de Presentacion Feria Robotica Educativa 2024 - Versión3.pptxtjcesar1
 
Agencia Marketing Branding Google Workspace Deployment Services Credential Fe...
Agencia Marketing Branding Google Workspace Deployment Services Credential Fe...Agencia Marketing Branding Google Workspace Deployment Services Credential Fe...
Agencia Marketing Branding Google Workspace Deployment Services Credential Fe...Marketing BRANDING
 
#Tare10ProgramacionWeb2024aaaaaaaaaaaa.pptx
#Tare10ProgramacionWeb2024aaaaaaaaaaaa.pptx#Tare10ProgramacionWeb2024aaaaaaaaaaaa.pptx
#Tare10ProgramacionWeb2024aaaaaaaaaaaa.pptxHugoGutierrez99
 
Red Dorsal Nacional de Fibra Óptica y Redes Regionales del Perú
Red Dorsal Nacional de Fibra Óptica y Redes Regionales del PerúRed Dorsal Nacional de Fibra Óptica y Redes Regionales del Perú
Red Dorsal Nacional de Fibra Óptica y Redes Regionales del PerúCEFERINO DELGADO FLORES
 
certificado de oracle academy cetrificado.pdf
certificado de oracle academy cetrificado.pdfcertificado de oracle academy cetrificado.pdf
certificado de oracle academy cetrificado.pdfFernandoOblitasVivan
 
Análisis de Artefactos Tecnologicos (3) (1).pdf
Análisis de Artefactos Tecnologicos  (3) (1).pdfAnálisis de Artefactos Tecnologicos  (3) (1).pdf
Análisis de Artefactos Tecnologicos (3) (1).pdfsharitcalderon04
 
Análisis de los artefactos (nintendo NES)
Análisis de los artefactos (nintendo NES)Análisis de los artefactos (nintendo NES)
Análisis de los artefactos (nintendo NES)JuanStevenTrujilloCh
 
David_Gallegos - tarea de la sesión 11.pptx
David_Gallegos - tarea de la sesión 11.pptxDavid_Gallegos - tarea de la sesión 11.pptx
David_Gallegos - tarea de la sesión 11.pptxDAVIDROBERTOGALLEGOS
 
LINEA DE TIEMPO LITERATURA DIFERENCIADO LITERATURA.pptx
LINEA DE TIEMPO LITERATURA DIFERENCIADO LITERATURA.pptxLINEA DE TIEMPO LITERATURA DIFERENCIADO LITERATURA.pptx
LINEA DE TIEMPO LITERATURA DIFERENCIADO LITERATURA.pptxkimontey
 
PLANEACION DE CLASES TEMA TIPOS DE FAMILIA.docx
PLANEACION DE CLASES TEMA TIPOS DE FAMILIA.docxPLANEACION DE CLASES TEMA TIPOS DE FAMILIA.docx
PLANEACION DE CLASES TEMA TIPOS DE FAMILIA.docxhasbleidit
 
Inteligencia Artificial. Matheo Hernandez Serrano USCO 2024
Inteligencia Artificial. Matheo Hernandez Serrano USCO 2024Inteligencia Artificial. Matheo Hernandez Serrano USCO 2024
Inteligencia Artificial. Matheo Hernandez Serrano USCO 2024u20211198540
 

Último (20)

Trabajando con Formasy Smart art en power Point
Trabajando con Formasy Smart art en power PointTrabajando con Formasy Smart art en power Point
Trabajando con Formasy Smart art en power Point
 
El camino a convertirse en Microsoft MVP
El camino a convertirse en Microsoft MVPEl camino a convertirse en Microsoft MVP
El camino a convertirse en Microsoft MVP
 
Trabajo de tecnología excel avanzado.pdf
Trabajo de tecnología excel avanzado.pdfTrabajo de tecnología excel avanzado.pdf
Trabajo de tecnología excel avanzado.pdf
 
Herramientas que posibilitan la información y la investigación.pdf
Herramientas que posibilitan la información y la investigación.pdfHerramientas que posibilitan la información y la investigación.pdf
Herramientas que posibilitan la información y la investigación.pdf
 
_Planificacion Anual NTICX 2024.SEC.21.4.1.docx.pdf
_Planificacion Anual NTICX 2024.SEC.21.4.1.docx.pdf_Planificacion Anual NTICX 2024.SEC.21.4.1.docx.pdf
_Planificacion Anual NTICX 2024.SEC.21.4.1.docx.pdf
 
LUXOMETRO EN SALUD OCUPACIONAL(FINAL).ppt
LUXOMETRO EN SALUD OCUPACIONAL(FINAL).pptLUXOMETRO EN SALUD OCUPACIONAL(FINAL).ppt
LUXOMETRO EN SALUD OCUPACIONAL(FINAL).ppt
 
Slideshare y Scribd - Noli Cubillan Gerencia
Slideshare y Scribd - Noli Cubillan GerenciaSlideshare y Scribd - Noli Cubillan Gerencia
Slideshare y Scribd - Noli Cubillan Gerencia
 
TALLER DE ANALISIS SOLUCION PART 2 (1)-1.docx
TALLER DE ANALISIS SOLUCION  PART 2 (1)-1.docxTALLER DE ANALISIS SOLUCION  PART 2 (1)-1.docx
TALLER DE ANALISIS SOLUCION PART 2 (1)-1.docx
 
La tecnología y su impacto en la sociedad
La tecnología y su impacto en la sociedadLa tecnología y su impacto en la sociedad
La tecnología y su impacto en la sociedad
 
Modelo de Presentacion Feria Robotica Educativa 2024 - Versión3.pptx
Modelo de Presentacion Feria Robotica Educativa 2024 - Versión3.pptxModelo de Presentacion Feria Robotica Educativa 2024 - Versión3.pptx
Modelo de Presentacion Feria Robotica Educativa 2024 - Versión3.pptx
 
Agencia Marketing Branding Google Workspace Deployment Services Credential Fe...
Agencia Marketing Branding Google Workspace Deployment Services Credential Fe...Agencia Marketing Branding Google Workspace Deployment Services Credential Fe...
Agencia Marketing Branding Google Workspace Deployment Services Credential Fe...
 
#Tare10ProgramacionWeb2024aaaaaaaaaaaa.pptx
#Tare10ProgramacionWeb2024aaaaaaaaaaaa.pptx#Tare10ProgramacionWeb2024aaaaaaaaaaaa.pptx
#Tare10ProgramacionWeb2024aaaaaaaaaaaa.pptx
 
Red Dorsal Nacional de Fibra Óptica y Redes Regionales del Perú
Red Dorsal Nacional de Fibra Óptica y Redes Regionales del PerúRed Dorsal Nacional de Fibra Óptica y Redes Regionales del Perú
Red Dorsal Nacional de Fibra Óptica y Redes Regionales del Perú
 
certificado de oracle academy cetrificado.pdf
certificado de oracle academy cetrificado.pdfcertificado de oracle academy cetrificado.pdf
certificado de oracle academy cetrificado.pdf
 
Análisis de Artefactos Tecnologicos (3) (1).pdf
Análisis de Artefactos Tecnologicos  (3) (1).pdfAnálisis de Artefactos Tecnologicos  (3) (1).pdf
Análisis de Artefactos Tecnologicos (3) (1).pdf
 
Análisis de los artefactos (nintendo NES)
Análisis de los artefactos (nintendo NES)Análisis de los artefactos (nintendo NES)
Análisis de los artefactos (nintendo NES)
 
David_Gallegos - tarea de la sesión 11.pptx
David_Gallegos - tarea de la sesión 11.pptxDavid_Gallegos - tarea de la sesión 11.pptx
David_Gallegos - tarea de la sesión 11.pptx
 
LINEA DE TIEMPO LITERATURA DIFERENCIADO LITERATURA.pptx
LINEA DE TIEMPO LITERATURA DIFERENCIADO LITERATURA.pptxLINEA DE TIEMPO LITERATURA DIFERENCIADO LITERATURA.pptx
LINEA DE TIEMPO LITERATURA DIFERENCIADO LITERATURA.pptx
 
PLANEACION DE CLASES TEMA TIPOS DE FAMILIA.docx
PLANEACION DE CLASES TEMA TIPOS DE FAMILIA.docxPLANEACION DE CLASES TEMA TIPOS DE FAMILIA.docx
PLANEACION DE CLASES TEMA TIPOS DE FAMILIA.docx
 
Inteligencia Artificial. Matheo Hernandez Serrano USCO 2024
Inteligencia Artificial. Matheo Hernandez Serrano USCO 2024Inteligencia Artificial. Matheo Hernandez Serrano USCO 2024
Inteligencia Artificial. Matheo Hernandez Serrano USCO 2024
 

Creación de Builders y DSL's con Groovy

  • 1. Creación de builders y DSL's con Groovy @neodevelop - @synergyj
  • 3.
  • 4.
  • 5. {}
  • 6.
  • 7. {}
  • 8. Metaprogramación • Programas que escriben programas... • Soporte para agregar métodos y propiedades a un objeto en tiempo de ejecución... • Soporte de intercepción de llamadas, como AOP
  • 9. Meta-object protocol “Interpreta la semantica de un programa abierto y extensible. Determina que significa un programa y que comportamiento tiene, así también, maneja objetos que manipulan, crean, describen o implementan otros objetos” En Groovy podemos usar el MOP para invocar métodos dinámicamente y sintetizar clases y métodos al vuelo
  • 10. MOP de Groovy Todos los accesos a los métodos, propiedades, constructores, operadores, etc., pueden ser interceptados El comportamiento en Java esta fuertemente atado al tiempo de compilación, en Groovy, el comportamiento es adaptable en tiempo de ejecución
  • 11. Meta-object Protocol GroovyObject En Groovy se trabaja con 3 tipos de objetos: POJOs POGOs Groovy Interceptors
  • 12. GroovyObject package groovy.lang; public interface GroovyObject { Object invokeMethod(String name, Object args); Object getProperty(String propertyName); void setProperty(String propertyName, Object newValue); MetaClass getMetaClass(); void setMetaClass(MetaClass metaClass); }
  • 13. MetaClass public interface MetaClass extends MetaObjectProtocol { Object invokeMethod(...); Object getProperty(...); void setProperty(...); Object invokeMissingMethod(...); Object invokeMissingProperty(...); Object getAttribute(...); void setAttribute(...); void initialize(); List<MetaProperty> getProperties(); List<MetaMethod> getMethods(); ClassNode getClassNode(); List<MetaMethod> getMetaMethods(); int selectConstructorAndTransformArguments(...); }
  • 14. Intercepción de métodos - GroovyInterceptable // Definamos una clase que implemente GroovyInterceptable class ClaseInterceptada implements GroovyInterceptable{ def doMetodo(param){ "Hola $param" } //Tenemos que implementar este metodo de la interfaz def invokeMethod(String nombre, args){ System.out.println "Ejecutando el metodo '$nombre' con argumentos '$args'" //Obtenemos el metodo a ejecutar de la clase def metodoValido = ClaseInterceptada.metaClass.getMetaMethod(nombre,args) //Si encontro el metodo a ejecutar... if(metodoValido != null){ //Lo invocamos desde el metodo que encontro metodoValido.invoke(this, args) }else{ //Si no lo encuentra simplemente llamamos al mŽtodo convencionalmente ClaseInterceptada.metaClass.invokeMethod(this, nombre, args) } } } def c = new ClaseInterceptada() println c.doMetodo("@grailsmx")
  • 15. Intercepción de métodos - ExpandoMetaClass //Ahora usemos un interceptor en una clase que no es de nosotros Float.metaClass.invokeMethod = { String nombre, args -> //Al igual desplegamos algo de informacion System.out.println "Ejecutando el metodo '$nombre' con argumentos '$args'" //Obtenemos el metodo del metaClass def metodoValido = Float.metaClass.getMetaMethod(nombre,args) //Si no existe dicho metodo if(metodoValido == null){ //Entonces regresamos la ejecucion convencional return Float.metaClass.invokeMissingMethod(delegate,nombre,args) } //Invocamos al metodo original con sus parametros resultado = metodoValido.invoke(delegate,args) //Regresamos la ejecucion del metodo resultado } //Usemos los metodos de la clase que no es propietaria println 10F.intValue() println 100F.toString() try{ 50F.empty() }catch(Exception e){ println e.message }
  • 16. MOP - Inyección de métodos(Categorías) //Definimos la clase que permitira ser la categoria class VerificaGramatica{ //Para que un metodo pueda ser categorizado, debe ser static def static esPalindrome(String frase){ //Implementamos nuestra categoria if(frase == new StringBuilder(frase).reverse().toString()) true else false } } //Con ayuda de la palabra reservada 'use' aplicamos la categoria use(VerificaGramatica){ def frase = "anitalavalatina" println frase.class.name println "Es palindrome?: " + frase.esPalindrome() println frase.class.name }
  • 17. MOP - Inyección de métodos(ExpandoMetaClass) //Podemos inyectar métodos estaticos Integer.metaClass.esPar = { -> delegate % 2 == 0 } //Probemos nuestro método estatico println "2 es Par? " + 2.esPar() println "3 es Par? " + 3.esPar()
  • 18. MOP - Síntesis de métodos(MethodMissing) class Persona{ String nombre Map relaciones = [:] def methodMissing(String relacion, personas){ if(relaciones.containsKey(relacion)){ personas.each{ persona -> relaciones.get(relacion).add(persona) } }else{ relaciones.put(relacion,personas as List) } } } def juan = new Persona(nombre:'Juan') juan.trabajaEn("SynergyJ") juan.trabajaCon("Manuel","Jorge") juan.esAmigoDe("Domingo") juan.trabajaCon("Andres") juan.esAmigoDe("George") juan.conoceA("Susana","Perla","Cassandra","Alejandra") println juan.relaciones
  • 19. MOP - Síntesis de métodos(ExpandoMetaClass) import java.text.NumberFormat def valoresDeConversion = ['USD':0.07973, 'EUR':0.0644, 'GBP':0.0538, 'JPY':7.2361] BigDecimal.metaClass.methodMissing = { String methodName, args -> tipoDeConversion = methodName[2..-1] valorDeConversion = valoresDeConversion[tipoDeConversion] if(valorDeConversion){ NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US) nf.setCurrency(Currency.getInstance(tipoDeConversion)) return nf.format(delegate * valorDeConversion) } "No hay conversion de Pesos a ${tipoDeConversion}" } println 13.00.enUSD() println 16.00.enEUR() println 1.00.enXYZ()
  • 20. Creación dinámica de clases con Expando def file = new File("migracion.csv") /* * Empresa,Nombre,Apellido,Puesto,correo... * 42 Claps,CŽsar,Salazar Hern‡ndez,CEO,c@42claps.com... * 4D Hispano,Dominique,Coste,Country Manager,d@4dhispano.com... */ def sql = groovy.sql.Sql.newInstance("jdbc:hsqldb:file:/devDB","sa","","org.hsqldb.jdbcDriver") lines = file.readLines() atributos = lines[0].tokenize(",") //Creaci—n de Expando def persona = new Expando() atributos.each{ //Creando atributos con Expando, tmb se pueden crear mŽtodos persona."${it.toLowerCase()}" = "${it.toLowerCase()}" } def insert = "insert into user(VERSION,PASSWD,ENABLED,USERNAME,EMAIL_SHOW,EMAIL,USER_REAL_NAME) " insert += "values(0,'7c4a8d09ca3762af61e59520943dc26494f8941b',true,?,true,?,?)" lines.each{ line -> valores = line.tokenize(",") index = 0 atributos.each{ persona."${it.toLowerCase()}" = "${valores[index]}" index++ } //Uso de Expando sql.execute(insert,[persona.correo,persona.correo,"${persona.nombre} ${persona.apellido}"]) }
  • 21. ¿Qué es un builder?
  • 22. Builders Son DSL’s internos que proveen trabajar facilmente con ciertos tipos de problemas De entrada, si tenemos la necesidad de trabajar con ciertos tipos de estructuras o representaciones, los builders son la mejor opción Proveen una sintaxis que no ata con dicha estructura o implementación Solo son fachadas, por que en realidad no están reemplazando la implementación, solamente proveen una manera elegante de usarla Groovy ya provee algunos, pero podemos hacer los propios
  • 23. Metaprogramación Builders BuilderSupport
  • 24. //Instanciamos un builder sqlBuilder = new MiSqlBuilder() //Ejecutamos nuestro builder sqlBuilder.build{ selecciona("campo1","campo2","campo3") tabla("nombreDeTabla") donde("1=1") insertar('tabla',['campo1','campo2','campo3'],['valor1','valor2','valor3']) ultimoInsert }
  • 25. class MiSqlBuilder{ def result = new StringWriter() def build(closure){ closure.delegate = this closure() println result } def methodMissing(String name,args){ switch(name){ case 'selecciona': result << "nSELECT ${args.join(',')}" break case 'tabla': result << " FROM ${args.join(',')}" break case 'donde': result << " WHERE ${args[0]}n" break case 'insertar': result << "nINSERT INTO ${args[0]}(${args[1].join(',')}) " result << "VALUES('${args[2].join('','')}')n" break } } def propertyMissing(String name){ if(name=="ultimoInsert"){ result << "nSELECT last_insert_id() n" } } }
  • 26. Lenguajes de dominio específico
  • 27. DSL Están enfocados a un cierto tipo de problema La sintaxis está orientada al negocio(hay expertos) No lo usamos para resolver problemas de propósito general como lo haría Java Es pequeño, simple, expresivo y enfocado a cierta área Son manejados por el contexto y elocuentes Con ayuda del MOP podemos crear DSL’s
  • 28. ¿Cómo desarrollar un DSL? Tipado dinámico y opcional La facilidad de usar Scripts ExpandoMetaClass Closures Sobrecarga de operadores Soporte de Builders Work-around de paréntesis
  • 29. tomar 4.pastillas, de: lsd, en: 12.horas
  • 30. class Droga { String nombre String toString() { nombre } } class CantidadDeDroga { int cantidad String toString() { cantidad == 1 ? "1 pastilla" : "$cantidad pastillas" } } class TiempoDeDuracion { Number numero String unidad } Integer.metaClass.getPastillas = { -> new CantidadDeDroga(cantidad: delegate) } Number.metaClass.getHoras = { -> new TiempoDeDuracion(numero: delegate, unidad: "horas") } def tomar(Map m, CantidadDeDroga cdd) { println "Tomar $cdd de $m.de en $m.en.numero $m.en.unidad" } def oxicodona = new Droga(nombre: "Oxicodona") def lsd = new Droga(nombre: "LSD") tomar 2.pastillas, de: oxicodona, en: 6.horas tomar 4.pastillas, de: lsd, en: 12.horas
  • 31. Referencias groovy.codehaus.org grails.org.mx - @grailsmx http://delicious.com/neodevelop/groovy Programming Groovy - Venkat S.
  • 32. ¡Gracias! Q &A @neodevelop

Notas do Editor