I was curious how the abstract BuildSupport class is working that does all those great things for e.g. the SwingBuilder and AntBuilder.
So I wrote the following Groovy Test that exposes its behaviour:
package groovy.util class SpoofBuilder extends BuilderSupport{ def log = [] protected void setParent(Object parent, Object child){ log << "sp" log << parent log << child } protected Object createNode(Object name){ log << 'cn1' log << name return 'x' } protected Object createNode(Object name, Object value){ log << 'cn2' log << name log << value return 'x' } protected Object createNode(Object name, Map attributes){ log << 'cn3' log << name attributes.each{entry -> log << entry.key; log << entry.value} return 'x' } protected Object createNode(Object name, Map attributes, Object value){ log << 'cn4' log << name attributes.each{entry -> log << entry.key; log << entry.value} log << value return 'x' } protected void nodeCompleted(Object parent, Object node) { log << 'nc' log << parent log << node } } // simple node def b = new SpoofBuilder() assert b.log == [] def node = b.foo() assert b.log == ['cn1','foo','nc',null, node] // simple node with value def b = new SpoofBuilder() def node = b.foo('value') assert b.log == ['cn2','foo','value', 'nc',null,node] // simple node with one attribute def b = new SpoofBuilder() def node = b.foo(name:'value') assert b.log == [ 'cn3','foo', 'name','value', 'nc',null,'x'] // how is closure applied? def b = new SpoofBuilder() b.foo(){ b.bar() } assert b.log == [ 'cn1','foo', 'cn1','bar', 'sp', 'x', 'x', 'nc','x','x', 'nc',null,'x']
The SpoofBuilder is a sample instance of the abstract BuilderSupport class that does nothing but logging how it was called, returning 'x' for each node.
The test sections call the SpoofBuilder in various ways and the log reveals what methods were called during the "Build".
This test allowed me to verify my assumption on how the builder pattern works here. I used this knowledge to write a specialized AntBuilder for
Canoo WebTest. This "MacroStepBuilder" allows using the Canoo WebTest "steps" (that walk through a webapp for testing) from Groovy Code. Groovy has now become a first-class citizen in the
Canoo WebTest Community.
When writing the above test I stumbled over a few things, here are two of them:
- I was not able to write a fully fledged subclass of GroovyTestCase with separate methods for the various tests. I couldn't find out how to make the SpoofBuilder an inner class of my TestCase. I would very much appreciate help on this.
- Coming from Ruby I expected the << operator on Strings to operate on the String itself (like it does on Lists) rather than giving back a modified copy. It appears to me that << on Strings and on Lists is not consistent. Same with the "+" operator.
What I especially appreciated:
- == on Lists is clear and compact
- display of evaluated expression when assert fails saves a lot of work when writing assertions. Most of the time you need no extra message.
keep up the good work!
[mittie]
Comments (2)
Sep 11, 2004
John Wilson says:
In Groovy, as in Java, Strings are immutable. This makes interworking with Java ...In Groovy, as in Java, Strings are immutable. This makes interworking with Java code very easy. However it does mean that operations on Strings have to produce new instances rather than modifying the existing instance.
John Wilson
Sep 23, 2004
dierk says:
I understand you trade interoperability with Java for consistency inside Groovy....I understand you trade interoperability with Java for consistency inside Groovy. But there may be a solution...
to clarify my point:
There are lots of commonalities with String and List like
and even more like behaviour of the 'each' method.
But
x=[] assert x === x << 'a'works fine while
x='' assert x === x << 'a'breaks.
It seems to that the 'leftShift' method is expected to modify the receiver object but the implementation on String just doesn't allow that.
I would propose to remove (or deprecate) that method from String. One can still use the + operator to achieve the old result. And + is expected to leave the receiver object unchanged
.
BTW: I very much like the Ruby notion of trailing ! chars in method names that modify the receiver (I understand that this is not possible for operators). So leftShift! would be a such a name.