[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