Internal DSLs, Groovy style
In a previous post, I described a simple framework for creating copasetic specifications– these are written in Groovy and run via a simple Java runner. What’s makes this little framework simple is its specification format– that is, the DSL, which facilitates defining specifications for hip objects.
As is the case for a lot of things in Groovy, defining DSLs, in this case, internal DSLs, couldn’t be any easier, man. For example, the base format for defining a specification is as follows:
before "description", {
//initialize needed objects
}
it "should do what ever you'd like", {
//behavior of object
//you can use any JBehave ensure style method too
}
You can, of course, have as many it’s as you need (if it’s your bag, baby); what’s more, the before clause is completely optional and follows the traditional xUnit fixture behavior– that is, the before clause is run before any it clause. Note, for the Groovy savvy, both clauses (it and before) are closures, baby. If you just want to have a one time initialization, then just initialize objects before the it clauses. For example:
//initialize foo once for entire specification
myFoo = new Foo()
it "should do something", {
myFoo.doSomething()
}
it "should do another thing", {
myFoo.again()
}
Because the specification file is logically a Groovy script, the behavior is run top down, so to speak; hence, the one time initialization works, man.
Creating internal DSLs in Groovy, in this case, means logically defining closures that match the desired keywords. For instance, the specification DSL demonstrated above has two trippin’ closures:
it, which has two parameters:- a
String - and a
Closure
- a
beforewhich also has two hip parameters:- a
String - and a
Closure
- a
Accordingly, the hip it closure looks like this:
binding.it = { spec, closure ->
closure.delegate = delegate
try{
if(beforeIt != null){
beforeIt()
}
closure()
listener.gotResult(new Result(spec, "Specification", Result.SUCCEEDED))
}catch(ex){
listener.gotResult(new Result(spec, "Specification", ex))
}
}
There is a lot going on in this method; however, the basic behavior is as follows: the body of the it closure (the stuff between the {}’s) is executed via the closure() invocation; moreover, if a before closure has been defined (it’s referenced via the beforeIt closure internally), this closure is first executed. If you are in the groove and are familiar with the internals of JBehave, the listener stuff then makes sense– otherwise, don’t worry too much, suffice to say that results are captured of the individual specifications.
Probably the most smokin’ aspect of the it closure is the the closure.delegate = delegate call. Essentially, as the it closure takes a closure as a parameter, that incoming closure is assumed to use JBehave calls, such as ensureThat(queue.dequeue(), eq(2)), for instance. Therefore, a delegate is set, which essentially means that stuff like ensureThat will be looked for, so to speak, in the delegate, baby
The delegate is quite simple– I wanted to reuse everything from JBehave, so I just created a Java class that extends from JBehave’s UsingMiniMock object. What’s more, because you can’t define inner classes in Groovy, I had to create a new method to facilitate using JBehave’s exception expectations (i.e. ensuring that certain calls throw exceptions).
For instance, the JBehaveDelegate is defined like this:
public class JBehaveDelegate extends UsingMiniMock {
public void ensureThrows(Class clzz, final Closure closure) throws Exception{
runAndCatch(clzz, new Block(){
public void run() throws Exception{
closure.call();
}
});
}
}
Note that the ensureThrows method takes a closure– so, in a Groovy specification script, you can use it like this:
it "should throw an exception when null is enqueued", {
ensureThrows(RuntimeException.class){
queue.enqueue(null)
}
}
I was able to put everything together (via the copasetic help of two ragingly cool cats, baby: Venkat Subramaniam and Jeff Brown) by then combining the specification DSL with Groovy’s GroovyShell object. Essentially, because it’s our bag, and instance of the shell is created with a custom Binding object that contains the hip closures defined above. The specification script is then passed into the shell via the evaluate call.
As you can see, internal DSLs in Groovy are incredibly hip, not to mention quite easy to define– the hardest part is defining the format and then figuring out how to enable people to use it. In this case, users are allowed to create a Groovy script that looks quite free in form– that script must then be passed to a runner (which, because it’s my bag, is a normal Java object) which has the smarts to evaluate the language. If that’s not groovy, then I’m livin’ in the wrong decade, baby!
Monday 05 Nov 2007 | Andy | Groovy
Great post Andy. When I read your article on developerworks about JBehave, I immediately thought that it would be interesting to see it used with Groovy. The JBehaveDelegate is awesome and really demonstrates how hip closures are. What do you think about using Groovy and JMock 2 for BDD?
[…] Having seen the light with RSpec and indeed, rbehave, I found myself wanting the same simple expressiveness in Java– accordingly, I began experimenting with Groovy and internal DSLs (with the help of some friends) and came up with something I find quite simple and expressive. […]
[…] I’ll be speaking about BDD, Groovy, and a Martin Fowler inspired subject regarding the future of build languages. My BDD talk will obviously focus on how BDD can drive development more effectively than TDD and I’ll demonstrate some JBehave, RSpec, and a Groovy inspired RSpec knock-off. My Groovy session is a veritable crash course on using Groovy quickly– 101 style, baby. Lastly, the build languages openspaces session is an open discussion regarding Ant style (i.e. XML driven platforms like NAnt and MSBuild) build platforms versus more expressive platforms like Rake, Raven, and Gant, for example. […]
John- indeed, Groovy does present some interesting options– check out Groovy’s own mocking too. It may be easier than incorporating JMock 2.
[…] The Disco Blog » Blog Archive » Internal DSLs, Groovy style: גרובי - שפת ×”×¡×§×¨×™×¤×˜×™× ×©×œ×ט ל×ט ×§×•× ×” לה קהל ×ž×¢×¨×™×¦×™× ×¢×™×§×©, ועכשיו: יצירת שפות תלויות מרחב. […]