Grails

Implementing DSLs in Groovy (OSCON slides online)

I have put the slides and some code from my OSCON 2007 talk online.

Had a lot of fun giving the talk. There is a lot of interest in Groovy out there! Thanks to everyone who came to the talk for the wonderful questions!

General
Grails
Groovy
Java
OJUG
Self
Software Development
Speaking

Comments (0)

Permalink

Groovy + Annotations + Aspects

I have been doing a lot of digging into Groovy and DSLs lately, getting ready for my OSCON talk next week.

The Groovy language has great built-in support for creating DSLs. Along with its dynamic nature, Groovy has the “use” keyword for working with categories, and coming in 1.1, the ExpandoMetaClass if you want to complete.

“Use”ing Categories

Categories help you do scoped “extension” to normal every day objects. An example is in order:

  1. class TacoCategory {
  2.         static String getTacos(final Integer i) {
  3.                 “${i} tasty tacos”
  4.         }
  5. }
  6.  
  7. use (TacoCategory.class) {
  8.         println 2.tacos     // outputs -> "2 tasty tacos"
  9. }

So, inside the use block, there is now (effectively) a new property on the Integer class, called tacos, that will return a string describing your delectable entree. This is a very powerful technique, and is very useful for developing your own expressive syntax.

One drawback to this technique is that you have to define where you are using it. Use can be extended into closures, but does not always extend into predefined methods.

  1. aClosure = { println 3.tacos }
  2.  
  3. use (TacoCategory.class) {
  4.  aClosure.call()  // outputs -> "3 tasty tacos"
  5. }
  6.  
  7. aClosure.call()  // Exception No such property: tacos for class: java.lang.Integer
  8.  
  9. // However, you can do this from Java (or Groovy):
  10. GroovyCategorySupport.use(TacoCategory.class, aClosure)  // outputs -> "3 tasty tacos"
  11.  
  12.  
  13. //  calling methods on a class:
  14. class A {
  15.         public String getTacos() {
  16.                 4.tacos  // has not been evaluated yet
  17.         }
  18. }
  19.  
  20. println new A().getTacos()   // Exception No such property: tacos for class: java.lang.Integer
  21.  
  22. // but you can do this:
  23. use(TacoCategory.class) {
  24.   println new A().getTacos() // outputs -> "4 tasty tacos"
  25. }

Groovy + Annotations + Aspects = annotated “use” clause

I was talking about Groovy Categories with Blaine Buxton, and was telling him that I did not like having the “use” block inside my DSL code, and wanted a more transparent approach. He suggested that perhaps an Annotation and an Aspect would do what I was looking for.

With the 1.1-Beta1 Release, Groovy has built in support for Java Annotations, and since Groovy compiles down to byte code, there is implicit support for Aspects. Putting these two things together allows us to create an annotation for a more powerful use:

  1. class AnnotatedObject {
  2.  
  3.   @Uses(category=[MeasurementCategory.class])
  4.   public String fourPoundsOfSomething(something) {
  5.     (4.lbs.of(something)).toString()   
  6.   }
  7. }

With a simple annotation, @Uses(category=[list of category classes]), we now can define them for any method we want, without having to put them into the method body.

Here is the Annotation code:

  1. package com.nimblelogic.groovy.dsl;
  2.  
  3. import java.lang.annotation.Inherited;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6.  
  7. @Inherited
  8. @Retention(RetentionPolicy.RUNTIME)
  9. public @interface Uses {
  10.   public Class[] category();
  11. }

And here is the Aspect code:

  1. package com.nimblelogic.groovy.dsl;
  2.  
  3. import groovy.lang.Closure;
  4.  
  5. import java.util.ArrayList;
  6. import java.util.List;
  7.  
  8. import org.aspectj.lang.JoinPoint;
  9. import org.aspectj.lang.annotation.SuppressAjWarnings;
  10. import org.aspectj.lang.reflect.MethodSignature;
  11. import org.codehaus.groovy.runtime.GroovyCategorySupport;
  12.  
  13. public aspect UsesAspect {
  14.        
  15.      pointcut allMethods(): call(@Uses * *(..));
  16.    
  17.     @SuppressAjWarnings
  18.     Object around(): allMethods() {
  19.         List< class >  classes = getCategories(thisJoinPoint);
  20.  
  21.         Object result = null;
  22.         if ( classes.isEmpty() ) {
  23.             result = proceed();
  24.         } else {
  25.                 result =
  26.                         GroovyCategorySupport.use(classes,
  27.                             new Closure(thisJoinPoint.getTarget()) {
  28.                                 @Override
  29.                                 public Object call() {
  30.                                     return proceed();
  31.                                 }                         
  32.                             });
  33.         }      
  34.        
  35.         return result;
  36.         }
  37.    
  38.     @SuppressWarnings(“unchecked”)
  39.         private List< class > getCategories(JoinPoint thisJoinPoint) {
  40.         List< class > classes = new ArrayList< class >();
  41.        
  42.         // get class level categories (if any)
  43.         Class cl = thisJoinPoint.getTarget().getClass();
  44.    
  45.         addCategories((Uses) cl.getAnnotation(Uses.class), classes);
  46.        
  47.         // get method level categories (if any)
  48.         if ( thisJoinPoint.getKind().equals(JoinPoint.METHOD_CALL)) {
  49.             MethodSignature sig = (MethodSignature) thisJoinPoint.getSignature();
  50.             addCategories((Uses) sig.getMethod().getAnnotation(Uses.class), classes);
  51.         }
  52.  
  53.         return classes;
  54.     }
  55.    
  56.    
  57.     private void addCategories(Uses uses, List< class > classList) {
  58.         if ( uses != null ) {
  59.                 Class[] classes = uses.category();
  60.                 for(Class cl : classes)
  61.                     classList.add(cl);
  62.         }
  63.     }
  64. }

One thing to note when working with Annotations / Aspects in Groovy: you have to keep track of the build order. I struggled for a while to figure out why I was not seeing the annotation in my Groovy classes. It was because it had not been built yet! I ended up having to split my compilation into 4 parts:

  1. Build the Annotation
  2. Compile the Groovy code
  3. Compile my other Java code (just test code)
  4. Weave the Aspects

Design
General
Grails
Groovy
Java
Software Development
Speaking
Thought

Comments (4)

Permalink

InfoTec 2007 Presentations are online

I put my InfoTec 2007 presentations online last night. If you don’t want to hear about how much fun I had giving them, here is the link.

My first talk was a 4 hour “Introduction to Ruby on Rails”. It had a decent turnout, and was a lot of fun to give. Thanks to a IM chat with Harish the night before the talk, I gave the best line I’ve given so far in a talk (IMHO): “So, what type of web application would you like to build today?”

My second talk was “Agile Java Web Frameworks”. There were twice as many people in this talk that had signed up before the conference started, though slightly fewer than in my rails talk. A slightly disjointed talk, I hit upon some of the more interesting web frameworks in the java world: Struts2, Spring MVC/WebFlow, Click, Rife, and then the dynamic contenders: Grails, Rails, and Lift.

Click was an interesting experience, as I had only learned about it the night before from Stephen Haberman. It looks pretty promising.

We only had enough time to quickly dive into one of the frameworks, and the audience chose Grails. So we delved into the bowels of GroovyQuiz and I showed them how it works.

Overall I had a lot of fun, and am looking forward to next years InfoTec.

AJAX/Web 2.0
Agile
General
Grails
Java
Rails
Ruby
Self
Speaking

Comments (1)

Permalink

GroovyQuiz.com is finally here!

Earlier today I put GroovyQuiz.com online, along with its first quiz (Anyone up for a game of Life?)

It amazing to see the response its getting already… there are a number of sites linking to it already (Thank you aboutGroovy.com and groovyblogs.org… you both rock!). And I’ve got a number of solutions (one in under 3 hours!) to post as soon as I finish up that section of the site.

General
Grails
Groovy
Software Development

Comments (3)

Permalink

Creating an RSS feed in Grails

I am in love with Groovy’s Builders… they make working with heirarchical structures a breeze. For instance, I needed to create an RSS feed for GroovyQuiz.com, and the builders make that so easy:

  1. // … in a Controller somewhere far far away …
  2.         def rss = {
  3.                 // render some XML markup to the response
  4.                 def url = “http://groovyquiz.com/groovyquiz/main/quiz”
  5.                 render(contentType:“text/xml”) {
  6.                         rss(version: “2.0″) {
  7.                                 channel {
  8.                                         title “GroovyQuiz.com - Latest Quizzes”
  9.                                         link “http://groovyquiz.com”
  10.                                         description “Weekly Groovy language challenges to help you learn”
  11.  
  12.                                         for(quiz in Quiz.latestActive())
  13.                                                 item {
  14.                                                         title   quiz.title
  15.                                                         link    “${url}/${quiz.number}”
  16.                                                         description     quiz.description
  17.                                                         pubDate quiz.publishedDate()
  18.                                                         guid    “${url}/${quiz.number}”
  19.                                                 }
  20.  
  21.                                 }
  22.                         }                     
  23.                 }            
  24.         }

General
Grails
Groovy
Java
Software Development

Comments (1)

Permalink