Wednesday, March 31, 2010

Gradle Plugin Conventions: Groovy Magic Explained

Hackergarten last Friday was a big success. About 10 of us (not all Canooies!) stayed up late and created an Announce plugin for Gradle. In the 0.9 release you will be able to announce build events to Twitter, Snarl, and the Ubuntu notification service (sorry, no Growl yet, it is coming). It was fun, educational, and energizing, and you should sign up for the Google Group and come to the next event.

While writing the plugin, we were briefly delayed by a technical detail. The user guide explains how to pass simple parameters to a plugin, but does not explain how to nest parameters within a closure. For instance, in the announce plugin you specify the Twitter username and password within an announce block:

announce {
username 'your-username'
password 'your-password'
}
It turns out to be easy (just not documented, but we will fix that soon). Here is the full gradle build that shows you how to do it, with a detailed explanation of how it works below. As an example, we'll extend the Gradle documentation greeter custom plugin. This whole thing is runnable from the command line with "gradle hello":
apply plugin: GreetingPlugin

greet {
message = 'Hi from Gradle'
}

class GreetingPlugin implements Plugin<Project> {
def void apply(Project project) {

project.convention.plugins.greeting = new GreetingPluginConvention()
project.task('hello') << {
println project.convention.plugins.greeting.message
}
}
}

class GreetingPluginConvention {
String message

def greet(Closure closure) {
closure.delegate = this
closure()
}
}
Starting at the top... there is a property declaration called "message" within a "greet" block. This is a much nicer configuration option than just using build-global variables, and your plugin should do something like this. It is important to understand what this block actually is and how it works. In Groovy, parentheses on method calls are optional and curly braces declare a closure. So this greet block is really an invocation of a method with type "Object greet(Closure)". To read and capture this message variable, you must somewhere declare and wire in this "Object greet(Closure)" method within your plugin.

Moving on to the Plugin declaration. Plugin configuration is captured in something Gradle calls a "Convention" object. It is just a plain old Groovy object, and there is no Gradle magic... all the magic happending is standard Groovy. Let's examine the apply() method closely:
def void apply(Project project) {

project.convention.plugins.greeting = new GreetingPluginConvention()
project.task('hello') &lt;&lt; {
println project.convention.plugins.greeting.message
}
}
During the configuration phase (before any targets are run) you will be given a Project object, and that Project object has a Convention object that holds onto a Map of all the project conventions. Three pieces of Groovy magic: Getters can be accessed with property notation, Map entries can be accessed with dot notation, and Map.put can be called with the = sign. So this line:
project.convention.plugins.greeting = new GreetingPluginConvention()
Is the same as the Java equivalent:
project.getConvention().getPlugins().put("greeting", new GreetingPluginConvention());
During the build, Gradle is going to execute the build script. Any time an unknown property is assigned or an undeclared method is invoked, Gradle will look at this plugins Map and use the respondTo method to check if any of the Plugin conventions accepts that variable. If it does, then your convention object gets invoked and that variable is set. Gradle takes care of translating an unknown method call of "Object greet(Closure)" into a call to your convention object. All that you need to do is make sure the greet(Closure) method is defined properly:
class GreetingPluginConvention {
String message

def greet(Closure closure) {
closure.delegate = this
closure()
}
}
Now it is up to you to dispatch the parameters declared within the closure into fields on your convention object. If you just try to execute the closure then you will receive a nice PropertyMissingException because the "message" field is not declared anywhere. You get around this by defining a "message" field on your object and then setting "this" to the closure's delegate. Groovy method dispatch is flexible (complex?). Closures have a delegate object that will act as a sort of "this" reference when the closure executes. So the message field was out of scope when the closure was created, but adding a delegate makes the message field from the convention object in scope. When the closure assigns message a value, it will be the message on your Convention object. Check out my old blog post Fun with Closures for a more in depth explanation.

Now your convention object has the message String, hooray! You can do something interesting with it, like printing it out to the console:
println project.convention.plugins.greeting.message
Or maybe you had something more interesting in mind for your cool plugin. Let us hope.

By the way, you can just as easily declare a settings variable. This is an equivilent implementation of apply:
def settings = new GreetingPluginConvention()
project.convention.plugins.greeting = settings
project.task('hello') << {
println settings.message
}
And now there is just one last piece of Groovy magic left unexplained:
apply plugin: GreetingPlugin
This too is plain old Groovy. Method parenthesis are optional. Method parameters can be passed in as name value pairs. And Class literals can be referenced without the .class extension. The apply plugin statement is really just a plain old method call in disguise, and it is an invocation of apply(Map). The apply statement can be written in Java as:
Map map = new HashMap();
map.put("plugin", GreetingPlugin.class);
apply(map);
Groovy magic is good. Thanks for taking some time to understand it. Hopefully this example makes it to the Gradle user guide in the next few days.

Tuesday, March 23, 2010

Unrolling Spock: Advanced @Unroll Usages in 0.4

Some of the Spock Framework 0.4 features are starting to see the light of day, with the Data Tables being explained last week in a nice blog post from Peter Niederwieser. One of the new features that I had not seen before is the new advanced @Unroll usage. Mixed with Data Tables, it produces some very cool results, and it can still be used with 0.3 style specs as well. Here's the juice:

JUnit Integration and @Unroll
Spock is built on JUnit, and has always had good IDE support without any effort from you as a user. For the most part, the IDEs just think Spock is another unit test. Here's the a Spock spec for the new Data Tables feature and how it shows up in an IDE.

import spock.lang.*

class TableTest extends Specification {

def "maximum of two numbers"() {
expect:
Math.max(a, b) == c

where:
a | b | c
3 | 7 | 7
5 | 4 | 5
9 | 9 | 9
}
}
The assertion will be run 3 times: once for each row in the data table. And JUnit faithfully reports the method name correctly, even when the method names has a space in it:

The problem with data driven tests and xUnit is poor error location. When a test fails you will receive an error stating which method is the culprit... but what if the method runs an assertion across 50 or 60 pieces of data? The cause of a failure is almost never clear with data driven tests. At it's worst you have to step through several iterations of code waiting for an exception. Good tests have a clear point of failure, but good tests also do not repeat themselves with boilerplate. This is exactly why Spock has the @Unroll annotation. As a test author you get to write one concise unit test, and JUnit does the work of reporting results that help you isolate failures. Consider the same test method with the @Unroll annotation and the accompanying IDE output.
@Unroll
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c

where:
a | b | c
3 | 7 | 7
5 | 4 | 5
9 | 9 | 9
}
When executed, JUnit sees three test methods instead of one: one for each row in the data table:

The end result for you as a test writer is accurate failure resolution. You can pinpoint exactly which row failed. This feature is available in Spock 0.3 and you can use it today. What is new in 0.4 is the ability to change the test name dynamically. Here is a full @Unroll annotation that changes the method name:
@Unroll("maximum of #a and #b is #c")
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c

where:
a | b | c
3 | 7 | 7
5 | 4 | 5
9 | 9 | 9
}
Notice the #variable syntax in the annotation parameter. The # produces a sort of GString-like variable substitution that lets you bind columns from your data table into your test name. The annotation parameter references #a, #b, and #c, which aligns with the data table definition of a | b | c. Check out the IDE output:

Previously, the test name was just the iteration number within the test. The new @Unroll parameter allows you to make the test name much more meaningful. Your tests will improve because failures become more descriptive. Unrolled failure messages before simply had the iteration name embedded in them, while now they can have meaningful data that you prescribe.

My favorite part of playing with the new @Unroll was to see the default value of the parameter within the Spock source code:
java.lang.String value() default "#featureName[#iterationCount]";
Talk about eating your own dog food... the default value is a test name template, just like you could have written in your own test. Makes you wonder what other variables are in scope, huh?

Spock snapshot builds for 0.4 are available at: http://m2repo.spockframework.org. Get it before the link breaks.

Friday, March 19, 2010

10 Lessons Learned from Usability Testing

This morning I published an article on my lessons learned from 7 years of experimenting with usability testing. It is called "10 Lessons Learned from Usability Testing". Creative, huh? The post is on the Canoo blog instead of here... most modern web browsers will show you the article if you click the link above. I say most because I can't trust IE 6 to do anything right.


If you like it then please do the up-vote thing at DZone.

Happy Friday everyone.

Thursday, March 18, 2010

Hackergarten in Basel Next Friday

There is a new group starting up, and if you're anywhere near Basel Switzerland next Friday (March 26th) then you definitely want to stop by and participate.


Hackergarten is a group for people who define themselves by what they make, not what they consume. We've decided to meet regularly to create things as a group and give our work back to the community. Our goal is to create something that others can use; whether it be working software, improved documentation, or better educational materials. Our intent is to end each meeting with a patch or similar contribution submitted to an open and public project. Membership is open to anyone willing to contribution their time.

Our first event this Friday is a "Gradle Jam" at the Canoo offices in Basel. We are planning to add an "announce" plugin to Gradle that integrates with Twitter, Mongrel, and other messaging services. I expect a lot of pair programming, so please do not worry if you have no Gradle, Groovy, or Java experience.

The best way to get started is to join the mailing list and lurk around or follow the Twitter account @Hackergarten.

I expect this to be a lot of fun, a little work, and ultimately rewarding. Please do stop by!

Saturday, March 6, 2010

Upcoming Gigs: Cologne, Frankfurt, Copenhagen, and Prague

The Groovy roadshow is on tour this Spring! My benevolent benefactors are letting me out of the office to visit a few user groups and conferences:


March 8 - Cologne JUG - Groovy AST Transformations
May 3rd - 7th - Jax.de - Code Generation on the JVM
May 19th to 20th - GR8 Conference - Groovy AST Transformations
June 28th - Czech JUG - Code Generation on the JVM

Sadly, I am not going to be at GR8 North America... but if you are in the US then that is the event of the Spring, in my opinion.

I'm crazy excited to visit places I have never been to and meet other programmers. If you have any desire to see any of my talks then drop me an email. I think these are my three best sessions:

Code Generation on the JVM
We're seeing more and more JVM frameworks designed to generate code at compile time: AST Transforms, Project Lombok, Spring Roo, Clojure Macros, and more. This session reviews these approaches, including examples of how and why we'd want to do this. We'll see the newest Groovy language tools, write our own AST Transform, and look at some amazing libraries based on these techniques.

Slimmed Down Software: A Lean, Groovy Approach
The Groovy Programming Language advertises itself as an "agile and dynamic Language for the JVM", but what does this mean exactly? This sessions explain Lean Software Development, and shows how your choice of programming language can your entire process remain nimble and adaptive. Come learn about the principles of Lean, and see how Groovy and the associated ecosystem help eliminate waste, defer commitment, and build quality into your product.

Legacy Code, Groovy, and You
Thinking about writing Groovy unit tests for your legacy Java code? This session is an honest discussion about what Groovy will gain you and what it won't. Come learn the engineering practices and tools that you can use to battle tight coupling, monolithic projects, and tangled dependencies, and then decide for yourself whether Groovy is the answer for your project. Plan on returning to work with a vision of what your team can do to write better software.