Adding Tags to your Tags

I'm working on a Plugin to allow for Grails to easily draw HTML5 Graphs using the RGraph library. The problem with this plugin is that we have to provide a lot of attributes for any given tag to work, for example, the Rose Chart:

<rgraph:rose name="rosetest"
title="Rose Chart Test"
values="[1, 2, 3]"
tooltips="['Fred', 'Rich', 'Alex']"
zoom="true"
width="500" height="500"/>

The code for this tag was the following:

def rose = { attrs ->
    draw(new Rose(attrs))
}

But this is what I wanted:

<rgraph:rose title="Rose Chart Test" zoom="true">
    <rgraph:attribute name="rosetest2"/>
    <rgraph:attribute values="[1, 2, 3,]"/>
    <rgraph:attribute tooltips="['Fred', 'Rich', 'Alex']"/>
    <rgraph:attribute width="500"/>
    <rgraph:attribute height="500"/>
<rgraph:rose>

My objective is to be able to use a map given by the controller to draw the whole thing in three lines of code. But first, to acomplish this, we need to create a new tag named attribute which saves the given attribute to the page scope:

def attribute = { attrs ->
    this.pageScope.attrs << attrs
}

Now we modify our tags to look like:

def rose = { attrs, body ->
    attrs << initTag(body)
    draw(new Rose(attrs))
}

This new method initTag is for DRY:

def initTag(def body){
    this.pageScope.attrs = [:]
    body()
    return this.pageScope.attrs
}

Here initTag instantiates a new map in the attrs key in the page scope. Then, the body is executed, which continues processing the tags inside the rose tag and reads all the attribute tags. Each attribute tag simply saves the given attributes to the scope. Finally, when all attribute tags are read, initTag method returns all the attributes to the tags attrs variable, which is used to instantiate a new Rose object used to paint the JavaScript code.

function rosetestRGraphDraw(){
    var rosetest = new RGraph.Rose('rosetest', [1, 2, 3]);
    rosetest.Set('chart.zoom', true);
    rosetest.Set('chart.width', 500);
    rosetest.Set('chart.tooltips', ["Fred", "Rich", "Alex"]);
    rosetest.Set('chart.title', "Rose Chart Test");
    rosetest.Set('chart.height', 500);
    rosetest.Draw();
}

Now, this works, but what if I wanted to, instead of putting each attribute by it's own, to have the controller return a map with all the data I need and use the g:each tag. Then, we need to modify the attribute tag like this:

def attribute = { attrs ->
    def m = [:]
    m.put attrs.name, attrs.value
    this.pageScope.attrs << m
}

<rgraph:rose title="Rose Chart Test 2" zoom="true">
    <rgraph:attribute name="name" value="rosetest2"/>
    <rgraph:attribute name="values" value="[1, 2, 3]"/>
    <rgraph:attribute name="tooltips" value="['Fred', 'Rich', 'Alex']"/>
    <rgraph:attribute name="width" value="500"/>
    <rgraph:attribute name="height" value="500"/>
</rgraph:rose>

The HTML might not be so nice this way, but now we can do this:

<rgraph:rose title="Rose Chart Test 2" zoom="true">
    <g:each in="${someMap}" var="${entry}">
        <rgraph:attribute name="${entry.key}" value="${entry.value}"/>
    </g:each>
</rgraph:rose>