[stringtemplate-interest] Managing template parameters

Nathan Ward nward at resqsoft.com
Sun Nov 23 16:38:39 PST 2008


That's more than I was suggesting.

I just wanted to be able do something like:

     myTemplateGroup.setAttribute("project", myProject);

Many of my templates don't need this object, but some low-level templates,
such as the msgKeyTag template shown below do need the project object. In
order to test msgKeyTag template with a JUnit based test, I had to make a
base template group and a temporary template that takes the project object
as a formal parameter so that the object would be available when the
msgKeyTag template was invoked. 

I would think that this situation is not uncommon. So, either other people
don't test small templates like this or other people also have to come up
with similar solutions to unit test these kinds of templates. So, I thought
this concept and API might make sense. 

// Implicit variable references: project
// Calls different template based on project.properties.msgFormatName
property,
// e.g. jstlMsgFormat() template if msgFormatName is "jstl"
msgKeyTag(msgKey) ::= <<
$(msgFormatTemplateNameMap.(project.projectOptions.msgTagType))(...)$
>>

jstlMsgFormat(msgKey) ::= <<
<fmt:message key="$msgKey$">
>>

strutsMsgFormat(msgKey) ::= <<
<bean:message key="$msgKey$">
>>

springMsgFormat(msgKey) ::= <<
<spring:message code="$msgKey$">
>>

// Maps msg format name to msg format template
// e.g. "jstl" maps to jstlMsgFormat template to produce <fmt:message ...
tag
// Used by msgKeyTag template
msgFormatTemplateNameMap ::= [
        "jstl":"jstlMsgFormat",
        "struts":"strutsMsgFormat",
        "spring":"springMsgFormat"
]


-----Original Message-----
From: Terence Parr [mailto:parrt at cs.usfca.edu] 
Sent: Friday, November 21, 2008 1:53 PM
To: Nathan Ward
Cc: stringtemplate-interest at antlr.org
Subject: Re: [stringtemplate-interest] Managing template parameters

Hi  Nathan,

Yes, in general I am very much opposed to dynamic scoping. In the   
template world, it makes a huge amount of sense. Templates are very dynamic
creatures and our combined willy-nilly into large, nested trees of sub
templates. I agree, however, that like we do with parameters, it could be
useful to say "heh,I'm going to reference this dynamically scoped
variable... if it does not exist when I'm evaluated, throw an exception".
sort of like

foo(a,b) uses title ::= "<a> <b> are parameters, <title> is from above"

Is that what you're talking about?

Ter

On Nov 14, 2008, at 10:10 AM, Nathan Ward wrote:

> Terence,
>
> I got this working. My code is included below in case it would be of 
> use to someone else. I would think this would be a common problem 
> because I would think it is pretty common for template parameters to 
> become "tramp data" as Meiler Page-Jones defined in the book 
> Structured Systems Design many years ago (i.e. passing parameters 
> around for use in code that is called later down the line.
>
> I also think there is an interesting StringTemplate design issue 
> here... The solution described in the Meiler Page-Jones book was to 
> use "Information Hiding" is similar to using object-oriented design 
> but was before OOD. I had done this kind of thing when programming in 
> C where I would group data and processing (i.e. C functions) in the 
> same file. The variables defined at the top of the file where like 
> object attributes. Not exactly OO, but not bad when programming in C.
>
> Given that StringTemplate is designed with a syntax that encourages 
> good template design, I would think that implicit access to variables 
> like I am now doing should not be allowed, but an alternative should 
> be provided by allowing template groups to accept parameters. This 
> would be similar to an object having instance member variables. This 
> makes the intension to share data across templates more explicit and 
> help the template designer think about if the data really should be 
> shared this way or should be treated as formal parameters. This would 
> also eliminate the need for me to create this temporary template and 
> template group for testing purposes.
>
> Shared.stg:
>
> // Implicit variable references: screen 
> screenElementResourceBundleKey(screenElement, screenElementType) ::= 
> << 
> <formClassName(screen)>.<screenElementType>.<screenElement.text>.Text
>>>
>
>
> SharedTemplateTestCase.java:
>
>    public void testScreenElementResourceBundleKey() {
>
>        Screen screen = new Screen();
>        screen.setName("MY_SCREEN");
>
>        Button button = new Button();
>        button.setText("MyButtonText");
>
>        super.setTemplateAttribute("screen", screen);
>        super.setTemplateAttribute("screenElement", button);
>        super.setTemplateAttribute("screenElementType", "Button");
>        String templateString =
> super.executeTemplateWithImplicitVariables(
>                templateGroup, templateGroup, 
> "screenElementResourceBundleKey");
>        super.logTemplateOutput(templateString);
>        TestCase.assertEquals("MyScreenForm.Button.MyButtonText.Text",
> templateString);
>
>    }
>
> BaseTemplateTestCase.java:
>
>    /**
>     * Use this method to test templates that use one or more 
> <i>implicit variables</i>
>     * rather than using only formal parameters. Implicit variables 
> should be used only
>     * in cases where the immediate template doesn't need the parameter 
> other than to
>     * pass it to other templates d several levels down in the template 
> calling chain.
>     *
>     * This method execute the template given by the templateName 
> parameter from a temporary
>     * template that this method creates in the template group so that 
> the parameters
>     * previously added by calling setTemplateAttribute() method will 
> be accessible to
>     * the template under test.
>     *
>     * If the template under test does not use implicit variables,
>     * (i.e. only uses formal parameters), then the StringTemplate API 
> can be used directly
>     * rather than using this method.
>     *
>     * @param rootTemplateGroup The rootTemplateGroup in the template 
> group inheritance hierarchy
>     * for the template group that the template under test is in. If 
> the target template group
>     * does not inherit from another template group, then just pass the 
> target template group
>     * as this parameter and also pass the same template group as the 
> targetTemplateGroup parameter.
>     * @param targetTemplateGroup The template group in which the 
> template under test is defined.
>     * @param templateName The name of the template under test.
>     * @return
>     */
>    public String
> executeTemplateWithImplicitVariables(StringTemplateGroup
> rootTemplateGroup,
>            StringTemplateGroup targetTemplateGroup, String
> templateName) {
>
>        StringBuilder temporaryTemplateGroup = new StringBuilder();
>        temporaryTemplateGroup.append("group temp;");
>        temporaryTemplateGroup.append(newline);
>        temporaryTemplateGroup.append("aTemplate(");
>        boolean notFirst = false;
>        for (final String parmName : parms.keySet()) {
>            if (notFirst) {
>                temporaryTemplateGroup.append(", ");
>            }
>            else {
>                notFirst = true;
>            }
>            temporaryTemplateGroup.append(parmName);
>        }
>        temporaryTemplateGroup.append(") ::= <<");
>        temporaryTemplateGroup.append(newline);
>        temporaryTemplateGroup.append("<");
>        temporaryTemplateGroup.append(templateName);
>        temporaryTemplateGroup.append("(...) >");
>        temporaryTemplateGroup.append(newline);
>        temporaryTemplateGroup.append(">>");
>
>        String temporaryTemplateGroupString = 
> temporaryTemplateGroup.toString();
>
>        StringTemplateGroup newParentGroup = new 
> StringTemplateGroup(new StringReader(
>                temporaryTemplateGroupString));
>
>        // Make the rootTemplateGroup inherit from the newParentGroup 
> that we created
>        // from temporaryTemplateGroupString
>        rootTemplateGroup.setSuperGroup(newParentGroup);
>
>        // Get an instance of the temporary template that we have 
> created named aTemplate
>        // from the targetTemplateGroup. Important that we get the 
> instance from the targetTemplateGroup
>        // rather than directly from the newParentGroup template so 
> that the template has access to
>        // all other templates in the template group inheritance 
> hierarchy.
>        StringTemplate template =
> targetTemplateGroup.getInstanceOf("aTemplate");
>
>        for (final Map.Entry<String, Object> entry :  
> parms.entrySet()) {
>            template.setAttribute(entry.getKey(), entry.getValue());
>        }
>        String templateString = template.toString();
>        return templateString;
>    }
>
>    public void setTemplateAttribute(String parmName, Object value) {
>
>        this.parms.put(parmName, value);
>    }
>
> -----Original Message-----
> From: Terence Parr [mailto:parrt at cs.usfca.edu]
> Sent: Thursday, November 13, 2008 5:58 PM
> To: Nathan Ward
> Cc: stringtemplate-interest at antlr.org
> Subject: Re: [stringtemplate-interest] Managing template parameters
>
>
> On Nov 13, 2008, at 11:50 AM, Nathan Ward wrote:
>
>> I'm aware of that, but how can I make a JUnit test for the 
>> subtemplate if it uses an attribute that is not passed as a formal 
>> parameter?
>> Hmmm, I guess I can create a template inline in my test code that 
>> uses the template that I'm testing, which is a string group template 
>> file.
>> I'll try that. Thanks!
>
> That'll work. :)
> Ter
>
>





More information about the stringtemplate-interest mailing list