[Helma-dev] a better "as-string"
Hannes Wallnoefer
hannes at helma.at
Fri Nov 14 16:07:51 CET 2003
I noticed that people are having a hard time making rendering functions
work for both direct-to-response and as-string scenarios. The reason is
that it's easy to write strings to the response buffer, but it's hard
the other way round - at least that's what most people think ;-)
The solution is often to render to an intermediary string or string
buffer, and than either return it or write it to the response buffer:
function renderFooAsString() {
var str = new java.lang.StringBuffer(),
str.append(renderSkinAsString("www"));
str.append("xxx");
str.append("yyy");
str.append(renderSkinAsString("zzz"));
return str.toString();
}
function renderFoo() {
res.write(renderFooAsString());
}
While this is basically ok and not as eval as using raw string
concatenation ( str = str + "xxx"), which allocates a new string for
every step, it's not really the best solution. Creating the StringBuffer
is clunky, and because the overloaded append() method the rhino invoker
has a hard time and it's risky that it will invoke the wrong one (for
example, you get 1 rendered as 1.0 this way).
So here are two functions in the response objects which have been there
for some time, but haven't been documented until today:
res.pushStringBuffer() <http://helma.org/stories/77550/>
res.popStringBuffer() <http://helma.org/stories/77551/>
These methods are used internally by Helma, and they have been there for
a while. The thing is that internally, the helma response object does
not just have one string buffer to write to, but a stack of string
buffers. "Pushing" a string buffer on the stack will cause all
res.write()s to be directed to the new string buffer. "Popping" a string
buffer from the stack will take the upmost string buffer and return its
content as string.
So with these two functions, we can rewrite our code above as follows:
function renderFooAsString() {
res.pushStringBuffer();
renderFoo();
return res.popStringBuffer();
}
function renderFoo() {
renderSkin("www");
res.write("xxx");
res.write("yyy");
renderSkin("zzz");
res.write();
}
The really nice thing about this (which may not be obvious until you
experience it) is that always writing to the intrinsic response string
buffer is much easier than managing your own strings and string buffers.
If you call another function that does some rendering in the middle of
renderFoo(), it will automatically write to the string buffer you set up
in the response object for the task. You don't have to worry about
concatenating the result of that function, or worry that it will write
to the response rather than returning the result as string. If that
function does its own pushing/popping of string buffers, that will be
transparent to the calling code.
Hope this info is useful to some of you ;-)
Hannes
More information about the Helma-dev
mailing list