Groovy	
  AST	
  
Agenda	
  
‱  Local	
  ASTTransforma5ons	
  
   –  Groovy	
  
   –  Grails	
  
   –  GriïŹ€on	
  
‱  Como	
  funcionan?	
  
‱  Otras	
  formas	
  de	
  modiïŹcar	
  AST	
  
LISTADO	
  DE	
  TRANSFORMACIONES	
  
DE	
  AST	
  (NO	
  EXHAUSTIVO)	
  
@Singleton	
  
@Singleton
class AccountManager {
  void process(Account account) { ... }
}


def account = new Account()
AccountManager.instance.process(account)
@Delegate	
  
class Event {
  @Delegate Date when
  String title, url
}

df = new SimpleDateFormat("MM/dd/yyyy")
so2gx = new Event(title: "SpringOne2GX",
   url: "http://springone2gx.com",
   when: df.parse("10/19/2010"))
oredev = new Event(title: "Oredev",
   url: "http://oredev.org",
   when: df.parse("11/02/2010"))
assert oredev.after(so2gx.when)
@Immutable	
  
@Immutable
class Person {
  String name
}


def person1 = new Person("ABC")
def person2 = new Person(name: "ABC")
assert person1 == person2
person1.name = "Boom!” // error!
@Category	
  
@Category(Integer)
class Pouncer {
  String pounce() {
     (1..this).collect([]) {
        'boing!' }.join(' ')
  }
}

use(Pouncer) {
  3.pounce() // boing! boing! boing!
}
@Mixin	
  
class Pouncer {
  String pounce() {
     (1..this.times).collect([]) {
        'boing!' }.join(' ')
  }
}

@Mixin(Pouncer)
class Person{
  int times
}

person1 = new Person(times: 2)
person1.pounce() // boing! boing!
@Grab	
  
@Grab('net.sf.json-lib:json-lib:2.3:jdk15')
def builder = new
net.sf.json.groovy.JsonGroovyBuilder()

def books = builder.books {
  book(title: "Groovy in Action",
       name: "Dierk Koenig")
}

assert books.name == ["Dierk Koenig"]
@Log	
  
@groovy.util.logging.Log
class Person {
  String name
  void talk() {
    log.info("$name is talking
")
  }
}

def person = new Person(name: "Duke")
person.talk()
// Oct 7, 2010 10:36:09 PM sun.reflect.NativeMethodAccessorImpl invoke0
// INFO: Duke is talking
	
  
@InheritConstructors	
  
@groovy.transform.InheritConstructors
class MyException extends RuntimeException {}


def x1 = new MyException("Error message")
def x2 = new MyException(x1)
assert x2.cause == x1
@Canonical	
  
@groovy.transform.Canonical
class Person {
    String name
}

def person1 = new Person("Duke")
def person2 = new Person(name: "Duke")
assert person1 == person2
person2.name = "Tux"
assert person1 != person2
@Scalify	
  
trait Output {
   @scala.reflect.BeanProperty
   var output:String = ""
}


@groovyx.transform.Scalify
class GroovyOutput implements Output {
    String output
}
@En5ty	
  
@grails.persistence.Entity
class Book {
  String title
}


def book = new Book().save()
assert book.id
assert book.version
@Bindable	
  
@groovy.beans.Bindable
class Book {
  String title
}

def book = new Book()
book.addPropertyChangeListener({e ->
  println "$e.propertyName $e.oldValue ->
$e.newValue"
} as java.beans.PropertyChangeListener)
book.title = "Foo” // prints "title Foo"
book.title = "Bar” // prints "title Foo Bar"
@Listener	
  
@groovy.beans.Bindable
class Book {
  @griffon.beans.Listener(snooper)
  String title
  private snooper = {e ->
    println "$e.propertyName $e.oldValue ->
$e.newValue"
  }
}

def book = new Book()
book.title = "Foo" // prints "title Foo"
book.title = "Bar" // prints "title Foo Bar"
@EventPublisher	
  
@Singleton
@griffon.util.EventPublisher
class AccountManager {
  void process(Account account) {
      publishEvent("AccountProcessed", [account)]
  }
}
def am = AccountManager.instance
am.addEventListener("AccountProcessed") { account ->
    println "Processed account $account"
}
def acc = new Account()
AccountManager.instance.process(acc)
// prints "Processed account Account:1"
@ScaïŹ€old	
  
class Book {
  String title
}

@griffon.presentation.Scaffold
class BookBeanModel {}

def model = new BookBeanModel()
def book = new Book(title: "Foo")
model.value = book
assert book.title == model.title.value
model.value = null
assert !model.title.value
assert model.title
@En5ty	
  
@griffon.persistence.Entity(‘gsql’)
class Book {
  String title
}


def book = new Book().save()
assert book.id
assert book.version
Y	
  otras	
  tantas	
  
	
  
‱  @PackageScope	
  
‱  @Lazy	
  
‱  @Newify	
  
‱  @Field	
  
‱  @Synchronized	
  
‱  @Vetoable	
  
‱  @ToString,	
  @EqualsAndHashCode,	
  
   @TupleConstructor	
  
‱  @AutoClone,	
  @AutoExternalize	
  
‱  
	
  
LA	
  RECETA	
  SECRETA	
  PARA	
  HACER	
  
TRANSFORMACIONES	
  DE	
  AST	
  
1	
  DeïŹnir	
  interface	
  
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD, ElementType.TYPE})
@GroovyASTTransformationClass
("org.codehaus.griffon.ast.ListenerASTTransformation
")
public @interface Listener {
    String value();
}
2	
  Implementar	
  la	
  transformacion	
  
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.transform.ASTTransformation;
import org.codehaus.groovy.transform.GroovyASTTransformation;


@GroovyASTTransformation(phase=CompilationPhase.CANONICALIZATION)
public class ListenerASTTransformation
           implements ASTTransformation {
    public void visit(ASTNode[] nodes, SourceUnit source) {
        // MAGIC GOES HERE, REALLY! =)
    }
}
3	
  Anotar	
  el	
  codigo	
  
@groovy.beans.Bindable
class Book {
  @griffon.beans.Listener(snooper)
  String title
    private snooper = {e ->
      // awesome code 

    }
}
OTRO	
  TIPO	
  DE	
  
TRANSFORMACIONES	
  
GContracts	
  
Spock	
  
CodeNarc	
  
GriïŹ€on	
  
Gracias!	
  

        @aalmiray	
  
hXp://jroller.com/aalmiray	
  

Ast transformations

  • 1.
  • 3.
    Agenda   ‱  Local  ASTTransforma5ons   –  Groovy   –  Grails   –  GriïŹ€on   ‱  Como  funcionan?   ‱  Otras  formas  de  modiïŹcar  AST  
  • 4.
    LISTADO  DE  TRANSFORMACIONES   DE  AST  (NO  EXHAUSTIVO)  
  • 5.
    @Singleton   @Singleton class AccountManager{ void process(Account account) { ... } } def account = new Account() AccountManager.instance.process(account)
  • 6.
    @Delegate   class Event{ @Delegate Date when String title, url } df = new SimpleDateFormat("MM/dd/yyyy") so2gx = new Event(title: "SpringOne2GX", url: "http://springone2gx.com", when: df.parse("10/19/2010")) oredev = new Event(title: "Oredev", url: "http://oredev.org", when: df.parse("11/02/2010")) assert oredev.after(so2gx.when)
  • 7.
    @Immutable   @Immutable class Person{ String name } def person1 = new Person("ABC") def person2 = new Person(name: "ABC") assert person1 == person2 person1.name = "Boom!” // error!
  • 8.
    @Category   @Category(Integer) class Pouncer{ String pounce() { (1..this).collect([]) { 'boing!' }.join(' ') } } use(Pouncer) { 3.pounce() // boing! boing! boing! }
  • 9.
    @Mixin   class Pouncer{ String pounce() { (1..this.times).collect([]) { 'boing!' }.join(' ') } } @Mixin(Pouncer) class Person{ int times } person1 = new Person(times: 2) person1.pounce() // boing! boing!
  • 10.
    @Grab   @Grab('net.sf.json-lib:json-lib:2.3:jdk15') def builder= new net.sf.json.groovy.JsonGroovyBuilder() def books = builder.books { book(title: "Groovy in Action", name: "Dierk Koenig") } assert books.name == ["Dierk Koenig"]
  • 11.
    @Log   @groovy.util.logging.Log class Person{ String name void talk() { log.info("$name is talking
") } } def person = new Person(name: "Duke") person.talk() // Oct 7, 2010 10:36:09 PM sun.reflect.NativeMethodAccessorImpl invoke0 // INFO: Duke is talking
  
  • 12.
    @InheritConstructors   @groovy.transform.InheritConstructors class MyExceptionextends RuntimeException {} def x1 = new MyException("Error message") def x2 = new MyException(x1) assert x2.cause == x1
  • 13.
    @Canonical   @groovy.transform.Canonical class Person{ String name } def person1 = new Person("Duke") def person2 = new Person(name: "Duke") assert person1 == person2 person2.name = "Tux" assert person1 != person2
  • 14.
    @Scalify   trait Output{ @scala.reflect.BeanProperty var output:String = "" } @groovyx.transform.Scalify class GroovyOutput implements Output { String output }
  • 15.
    @En5ty   @grails.persistence.Entity class Book{ String title } def book = new Book().save() assert book.id assert book.version
  • 16.
    @Bindable   @groovy.beans.Bindable class Book{ String title } def book = new Book() book.addPropertyChangeListener({e -> println "$e.propertyName $e.oldValue -> $e.newValue" } as java.beans.PropertyChangeListener) book.title = "Foo” // prints "title Foo" book.title = "Bar” // prints "title Foo Bar"
  • 17.
    @Listener   @groovy.beans.Bindable class Book{ @griffon.beans.Listener(snooper) String title private snooper = {e -> println "$e.propertyName $e.oldValue -> $e.newValue" } } def book = new Book() book.title = "Foo" // prints "title Foo" book.title = "Bar" // prints "title Foo Bar"
  • 18.
    @EventPublisher   @Singleton @griffon.util.EventPublisher class AccountManager{ void process(Account account) { publishEvent("AccountProcessed", [account)] } } def am = AccountManager.instance am.addEventListener("AccountProcessed") { account -> println "Processed account $account" } def acc = new Account() AccountManager.instance.process(acc) // prints "Processed account Account:1"
  • 19.
    @ScaïŹ€old   class Book{ String title } @griffon.presentation.Scaffold class BookBeanModel {} def model = new BookBeanModel() def book = new Book(title: "Foo") model.value = book assert book.title == model.title.value model.value = null assert !model.title.value assert model.title
  • 20.
    @En5ty   @griffon.persistence.Entity(‘gsql’) class Book{ String title } def book = new Book().save() assert book.id assert book.version
  • 21.
    Y  otras  tantas  
   ‱  @PackageScope   ‱  @Lazy   ‱  @Newify   ‱  @Field   ‱  @Synchronized   ‱  @Vetoable   ‱  @ToString,  @EqualsAndHashCode,   @TupleConstructor   ‱  @AutoClone,  @AutoExternalize   ‱  
  
  • 22.
    LA  RECETA  SECRETA  PARA  HACER   TRANSFORMACIONES  DE  AST  
  • 23.
    1  DeïŹnir  interface   @Retention(RetentionPolicy.SOURCE) @Target({ElementType.FIELD, ElementType.TYPE}) @GroovyASTTransformationClass ("org.codehaus.griffon.ast.ListenerASTTransformation ") public @interface Listener { String value(); }
  • 24.
    2  Implementar  la  transformacion   import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.transform.ASTTransformation; import org.codehaus.groovy.transform.GroovyASTTransformation; @GroovyASTTransformation(phase=CompilationPhase.CANONICALIZATION) public class ListenerASTTransformation implements ASTTransformation { public void visit(ASTNode[] nodes, SourceUnit source) { // MAGIC GOES HERE, REALLY! =) } }
  • 25.
    3  Anotar  el  codigo   @groovy.beans.Bindable class Book { @griffon.beans.Listener(snooper) String title private snooper = {e -> // awesome code 
 } }
  • 26.
    OTRO  TIPO  DE   TRANSFORMACIONES  
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
    Gracias!   @aalmiray   hXp://jroller.com/aalmiray Â