Created
November 3, 2013 12:47
-
-
Save robpatrick/7289943 to your computer and use it in GitHub Desktop.
The Grails ORM technology - GORM, has the ability to auto-timestamp GORM objects with the date and time they were created and last updated, details can be found here: http://grails.org/doc/latest/guide/GORM.html#eventsAutoTimestamping Our developers didn't like to have to litter our GORM code with these attributes so I created an AST transformat…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.alkalinezoo.transform | |
import java.lang.annotation.Target | |
import java.lang.annotation.ElementType | |
import java.lang.annotation.Retention | |
import java.lang.annotation.RetentionPolicy | |
import org.codehaus.groovy.transform.GroovyASTTransformationClass | |
/** | |
* Simple annotation that when used on a class adds two new fields of type {@code java.util.Date} | |
* the fields are: | |
* <p> | |
* {@code Date dateCreated} | |
* <p> | |
* {@code Date lastUpdated} | |
* <p> | |
* This annotation is primarily designed to be used with the GORMs auto-timestamp functionality, but can | |
* be used on any groovy class that requires these fields. | |
* | |
* @author robpatrick | |
*/ | |
@Target([ElementType.TYPE]) | |
@Retention(RetentionPolicy.SOURCE) | |
@GroovyASTTransformationClass(['com.alkalinezoo.transform.CreatedAndLastUpdatedASTTransformation']) | |
@interface CreatedAndLastUpdated { | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.alkalinezoo.transform | |
import org.codehaus.groovy.transform.GroovyASTTransformation | |
import org.codehaus.groovy.control.CompilePhase | |
import org.codehaus.groovy.transform.ASTTransformation | |
import org.codehaus.groovy.ast.ASTNode | |
import org.codehaus.groovy.control.SourceUnit | |
import com.alkalinezoo.transform.CreatedAndLastUpdated | |
import org.codehaus.groovy.ast.ClassNode | |
import org.codehaus.groovy.ast.FieldNode | |
import org.codehaus.groovy.ast.PropertyNode | |
import org.codehaus.groovy.ast.AnnotationNode | |
/** | |
* AST Transformation that add the {@code dateCreated} and {@code lastUpdated} | |
* properties to the class annotated with the {@Link CreatedAndLastUpdated} | |
* annotation. | |
* | |
* @author robpatrick | |
*/ | |
@GroovyASTTransformation( phase = CompilePhase.CANONICALIZATION ) | |
class CreatedAndLastUpdatedASTTransformation implements ASTTransformation { | |
private static final String DATE_CREATED = 'dateCreated' | |
private static final String LAST_UPDATED = 'lastUpdated' | |
/** | |
* The main method to implement from ASTTransformation that is called by the compiler. | |
* It looks for the annotated node and adds the two new properties it. | |
* | |
* @param astNodes The astNodes from the annotated class. | |
* @param sourceUnit Provides an anchor for a single source unit (usually a script file) | |
* as it passes through the compiler system. Not used. | |
*/ | |
@SuppressWarnings( 'UnusedMethodParameter' ) | |
void visit( ASTNode[] astNodes, SourceUnit sourceUnit ) { | |
if ( safeToAddProperties( astNodes, CreatedAndLastUpdated ) ) { | |
ClassNode classNode = (ClassNode)astNodes[1] | |
if ( !classNode.getDeclaredField( DATE_CREATED ) ) { | |
addProperty( classNode, DATE_CREATED, Date ) | |
} | |
if ( !classNode.getDeclaredField( LAST_UPDATED ) ) { | |
addProperty( classNode, LAST_UPDATED, Date ) | |
} | |
} | |
} | |
/** | |
* This method adds a new property to the class. Groovy automatically handles adding the getters and setters so you | |
* don't have to create special methods for those. | |
* | |
* @param classNode The node to which the properties will be added. | |
* @param propertyName name of the property to add to the node. | |
* @param propertyType the type of the property to add to the class. | |
*/ | |
private void addProperty( ClassNode classNode, String propertyName, Class propertyType ) { | |
FieldNode field = new FieldNode( propertyName, PropertyNode.ACC_PUBLIC, new ClassNode( propertyType ), new ClassNode( classNode.class ), null ) | |
classNode.addProperty( new PropertyNode( field, PropertyNode.ACC_PUBLIC, null, null ) ) | |
} | |
/** | |
* Defensive code against the compiler changing in future versions and not passing what we are expecting. More details can be | |
* found here: {@link http://joesgroovyblog.blogspot.co.uk/2011/09/ast-transformations-transformation.html}. | |
* | |
* @param astNodes The astNodes from the annotated class. | |
* @param annotationType The class of the annotation for which the transformation should have been called. | |
* @return true if it is sage to | |
*/ | |
private Boolean safeToAddProperties( ASTNode[] astNodes, Class annotationType ) { | |
if ( !astNodes || !astNodes[0] || !astNodes[1] || !( astNodes[0] instanceof AnnotationNode ) || astNodes[0].classNode?.name != annotationType.name ) { | |
return false | |
} | |
true | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.alkalinezoo.example | |
import com.alkalinezoo.transform.CreatedAndLastUpdated | |
/** | |
* Example of how the {@link CreatedAndLastUpdated} annotation is used in a Grails GORM class. | |
* | |
* @author robpatrick | |
*/ | |
@CreatedAndLastUpdated | |
class CreatedAndLastUpdatedExample { | |
String name | |
String description | |
static constraints = { | |
description maxSize: 1000 | |
} | |
} |
If you're using a sufficiently recent version of Groovy a trait is a simpler solution, eg. .
trait Timestamped {
Date dateCreated
Date lastUpdated
}
class MyDomainClass implements Timestamped {
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@sbglasius I think it is already. Since writing this I have been told about this plugin: http://grails.org/plugin/timestamped I've not used it myself, but it might be worth looking at.