Mark Fisher
Mark Fisher

Reputation: 9886

Groovy Nested Templates - Cannot invoke methods in class from within template

I'm trying to use Groovy's SimpleTemplateEngine to generate some output instead of StringBuilders, but I can't get templates to call other methods in my class successfully (which ultimately invoke other Templates to form a nested chain).

When the template (example below) invokes the method <% generateItemOutput(itemDescriptor.itemList) %> I get an exception:

groovy.lang.MissingMethodException: No signature of method:
SimpleTemplateScript1.generateItemOutput() is applicable for
argument types: (java.util.ArrayList)

I've tried making all the methods static, and adding an import to the static method in the template (as suggested here) but I get unable to resolve class ... to the import I use:

SimpleTemplateScript1.groovy: 1: unable to resolve class uk.co.e2x.convert.XMLToSlurp.generateItemOutput
 @ line 1, column 20.
   out.print(""""""); import uk.co.e2x.convert.XMLToSlurp.generateItemOutput ;
                      ^

Here's a sample of what I'm doing without the statics:

public String generateItemDescriptorOutput(itemDescriptor) {
    def params = [itemDescriptor: itemDescriptor]
    def template = new groovy.text.SimpleTemplateEngine().createTemplate(itemDescriptorTemplate())
    def writeable = template.make(params)
    return writeable.toString()
}

public String generateItemOutput(itemList) {
    ...
}

public String itemDescriptorTemplate() {
    return = '''\
${itemDescriptor.name} {
repository = "${itemDescriptor.repository}"
props = [
    <% generateItemOutput(itemDescriptor.itemList) %>
]
}
'''
}

I invoke it with java -cp ... but got same problem invoking via groovy. The reason I moved to invoking it through java was I can't work out the package path to a static method in a groovy script.

Has anyone got any concrete examples of calling your own script/classes methods from within a template, or can tell me what I'm doing wrong?

Cheers.

Upvotes: 2

Views: 1932

Answers (2)

Joman68
Joman68

Reputation: 2850

I include nested templates inside a parent template using a static utility method like this:

class TemplateUtils {

    static String generateOutput(String templateFilename, Map templateBinding) {
        URL templateResource = TemplateUtils.classLoader.getResource(templateFilename)
        assert templateResource : "Resource for '${templateFilename}' not found"
        new GStringTemplateEngine().createTemplate(templateResource).make(templateBinding)
    }
}

Then in the parent template:

Dear $to
<% out.println(com.example.TemplateUtils.generateOutput("letterContent.template", variables)) %>
Regards $from

The parent template's bound variables are relayed to the 'letterContent' template in the variables variable.

Upvotes: 0

Mark Fisher
Mark Fisher

Reputation: 9886

Ok, I managed to get it working and it was fairly simple in the end. I had to use statics for all the generate and template methods and then add the fully qualified package name to the methods inside the template instead of trying to do an import.

Also found I was missing a print in the command too. Here's the fixed template:

public static String itemDescriptorTemplate() {
    return = '''\
${itemDescriptor.name} {
repository = "${itemDescriptor.repository}"
props = [
    <% print uk.co.e2x.convert.XMLToSlurp.generateItemOutput(itemDescriptor.itemList) %>
]
}
'''

Upvotes: 2

Related Questions