Curried Closures XML Markup

After having played with Groovy closures for a bit I thought it would be an excellent way to complement my REST webservice while rendering the XML for one of my domain classes. While I was trying not to duplicate to much code by using Groovy Closures, I ran into a few bumps. Here’s the short version.

At first I had this construction for my BookController with ony a book action:

	def book = {
		if(params.id && Book.exists(params.id)) {
			def b = Book.get(params.id)
			render b as XML
		}
		else {
			def all = Book.list()
			render b as XML
		}
	}

This uses either grails.converters.XML or grails.converters.deep.XML, but doesn’t include my Book’s own defined getFormattedTitle() method in the output on the place that I want, so I needed to adjust things and list the properties myself:

	def book = {
		if(params.id && Book.exists(params.id)) {
			def b = Book.get(params.id)
			render(contentType:"text/xml") {
				book(id:b.id) {
					title(b.formattedTitle)
				}
			}
		}
		else {
			def all = Book.list()
			render(contentType:"text/xml") {
				list {
					all.each { b ->
						book(id:b.id) {
							title(b.formattedTitle)
						}
					}
				}
			}
		}
	}

Now I have complete control what properties and or methods are used for the output, but I did violate the DRY principle by having to repeat each modification for a book in the output for a single one or multiple ones. The information I want to show for a Book is the same (whether displayed for a single book of for multiple) so I’m going to use a Closure for it. This is the Closure I initially thought up:

	def singleBook = { b ->
		book(id:b.id) {
			title(b.formattedTitle)
		}
	}

See it it works just for a single book e.g. when /book/1 is called.

	...
	def b = Book.get(params.id)
	render(contentType:"text/xml", singleBook.curry(b))

Here’s where it went wrong. I thought the Closure returned by curry() would be enough, but the following stacktrace indicated another object was passed along to the Closure.

Caused by: groovy.lang.MissingMethodException: No signature of method: BookController$_closure6.doCall() is applicable for argument types: (Book, groovy.xml.streamingmarkupsupport.BaseMarkupBuilder$Document) values: {Book : 1, groovy.xml.streamingmarkupsupport.BaseMarkupBuilder$Document@1cf6203}

After adding another parameter to the Closure it finally worked, but still for a single Book. The new code (notice the additional 'x'):

	def singleBook = { b, x ->
		book(id:b.id) {
			title(b.formattedTitle)
		}
	}

Now we certainly can make a 2nd Closure which re-uses singleBook right? The draft of the Closure and the adjustment to the calling code:

	def singleBook = { b, x ->
		book(id:b.id) {
			title(b.formattedTitle)
		}
	}

	def multipleBook = { books, x ->
		list {
			books.each { b ->
				singleBook(b, x)
			}
		}
	}

	def book = {
		if(params.id && Book.exists(params.id)) {
			def b = Book.get(params.id)
			render(contentType:"text/xml", singleBook.curry(b))
		}
		else {
			def all = Book.list()
			render(contentType:"text/xml", multipleBook.curry(all))
		}
	}

No failures here, but this resulted in the following output:

	<list>
  <singleBook>
    <each>groovy.xml.streamingmarkupsupport.BaseMarkupBuilder$Document@3c72d1</each>
  </singleBook>
  <singleBook>
    <each>groovy.xml.streamingmarkupsupport.BaseMarkupBuilder$Document@3c72d1</each>
  </singleBook>
</list>

Not exactly what we want! Somehow my singleBook is not called correctly. Failing to grasp the entire inner workings of Groovy closures in this markup context, led my to asking a few questions on the Grails user mailinglist. A followup of Peter Ledbrook on Daniel J. Lauk’s reply on my question gave The Hint:

The reason the closure call doesn’t work is that it’s being invoked from a builder, an XML markup builder in this case. Builders typically work by invoking methods on the closure delegate, so if the delegate is wrong, bad stuff happens.

I’ve duplicated the Closure (so the original stays untouched) and changed the delegate of the singleBook clone to the one of multipleBook. The implicit variable delegate (just as this and owner inside a Closure) is by default the same as owner (the enclosing object – a surrounding Closure or this) is typically used by these builders – so we have to change this in order to achieve the functionality we want.

	def multipleBook = { books, x ->
		list {
			books.each { b ->
				def closure = singleBook.clone()
				closure.delegate = delegate
				closure(b, x)
			}
		}
	}

Bob’s your uncle! Now we have defined the structure of our Book only in a single point, by using an XML markup builder and Closures. Hopefully someone finds this useful when struggling with the same issues. Happy coding!