[REBOL] RWT: Rebol Server Pages the Manual
From: maarten:koopmans:surfnet:nl at: 3-Mar-2003 19:40
Introduction to REBOL Server Pages (RSP).
Ernie van der Meer ([e--a--vdmeer--net--hcc--nl])
Q: REBOL Server Pages? That sounds familiar!
A: It should! The idea for REBOL Server Pages (RSP) was borrowed directly
from Java Server Pages (JSP). Those familiar with JSP should feel right
at home.
Q: So what does it do?
A: Well, generally speaking, it allows you to write a template and then
have the process-rsp function fill it in for you. The template can be
stored in a file or in a REBOL string.
Q: Okay, so I need to write a template. How do I go about that?
A: The template text is just a normal piece of text like any other.
However, it includes specially tagged sections, delimited by <% and %>.
When run through the process-rsp function, all the tagged sections are
processed and the result of that processing is returned to you as a
string.
There are two types of sections: code sections and value sections. Code
sections are delimited by <% and %>, while value sections are delimited
by <%= and %>.
Q: I understand ... sort of. Why have code and value sections? I'm not sure
I understand the difference.
A: The difference it quite subtle, but very important. A value section must
contain REBOL code that reduces to a single value. The value will
replace the value section in the text. The content of a code section,
however, is executed as-is. No direct output of a code section will be
visible. The section will simply disappear completely. Unlike a value
section, a code section need not contain valid REBOL code in itself. It
can be combined with other code sections to make the REBOL code
complete.
Q: That all sounds pretty confusing. Can you give me an example?
A: Its really not as difficult as it sounds. Consider the following
template text:
text: "Today is <% if not windy [ %>not<% ] %>windy"
By changing the value of the word 'windy, we can conditionally include
the word "not" in the output of the template:
>> windy: yes
== true
>> process-rsp text
== "Today is windy"
>> windy: no
== false
>> process-rsp text
== "Today is not windy"
If you look more closely at 'text, you can see that there are two code
sections in it. the first contains " if not windy [ ", and the second
contains only a closing bracket " ] ". Neither code section contains
valid REBOL code in itself, but when combined, they result in
conditional inclusion of a section of text.
Consider another example:
text: "<% foreach item [ 1 2 3 4 ] [ %>item: <%= item %> <% ] %>"
Passing this through the RSP processor yields the following result:
>> text: "<% foreach item [ 1 2 3 4 ] [ %>item: <%= item %> <% ] %>"
== {<% foreach item [ 1 2 3 4 ] [ %>item: <%= item %> <% ] %>}
>> process-rsp text
== "item: 1 item: 2 item: 3 item: 4 "
Once again we have two code sections that, together, form valid REBOL
code. However, in between, we have a value section. The value section is
evaluated for every iteration of the surrounding 'foreach code section.
Q: Cool! Is there anything else I should be aware of?
A: Yeah, sure. Template code, generated from your template text by
process-rsp will ONLY be able to access variables in the GLOBAL context.
That means that locals in a function and/or context/object are not
accessible! So don't go doing stuff like this and expect it to work:
do %rsp.r
f: func
[
/local template my-name
]
[
my-name: "Ernie"
template: "My name is <%= my-name %>"
print process-rsp template
]
if you execute f, you will get the following output:
>> f
** Script Error: my-name has no value
** Where: process-rsp
** Near: append/only result my-name
Since 'my-name is a local, it is not accessible in the global context.
Q: Hey! That's lame! How come you don't fix that?
A: I would if I knew an elegant way to do it. The problem here is a very
basic problem in REBOL. As we all know, words in REBOL have a binding to
a context in which they are defined. Words can be rebound to any other
context using the 'bind function. Values, however, also seem to have a
binding in REBOL. This is nicely illustrated by the following function:
g: func [ /local text ] [ text: "hello" do "print text" ]
Executing this yields:
>> g
** Script Error: text has no value
** Where: g
** Near: print text
That's the whole problem in a nutshell. When 'do evaluates the code
passed to it, it automatically evaluates it in the global context.
Therefore, it can't access the locally defined variable. As far as I
know, there is no way to get 'do to evaluate the code in the string
while having access to local variables.
Q: How do I create a template that includes "<%" and/or "%>" as normal
text? I don't want them processed as tags!
A: Sorry! Some things were just not meant to be. This would boost the
complexity of the processor by at least an order of a magnitude. Maybe
you can work around the problem by using "< %" and/or "% >" instead.
Q: How come I need both the "<%" and the "<%=" tags? Surely I can do
everything with only the "<%=" tag?
A: Like I said before, the difference between the two tag types is subtle.
The answer to your question is, of course, no. You need both tag types.
A good example is the 'foreach loop described above. You definitely
don't want to see the return value of the foreach loop (being the last
value of the last iteration of the block) added to the output. In other
cases, however, you must have the return value of the statement in the
output. Since REBOL is completely dynamic, there is no chance of
guessing which output should be included and which shouldn't. That's why
you need the different tag types so you can specify which statements
should produce output.
Consider the following two templates:
t1: "<%= mold foreach x [ 1 2 3 ] [ append [] x * x ] %>"
t2: "[<% foreach x [ 1 2 3 ] [ %><%= x * x %> <% ] %>]"
>> process-rsp t1
== "[1 4 9]"
>> process-rsp t2
== "[1 4 9 ]"
Both give similar, if not identical results. Both use a 'foreach loop to
produce the output. The use of "<%=" versus "<%" is essential to force
different behavior for both 'foreach statements.
Q: What does this give me that rejoin doesn't?
A: Functionally: nothing! However, it is very inconvenient to use double
quotes or braces/brackets ({) around all your strings. This is
especially true when the bulk of your template is text. It's a bit
similar to the discussion in JSP. When is it profitable to use JSP?
General rule of thumb: when most of your template is static and only
a small part is dynamic.
Q: Okay. Filling in a template is nice, but what can I do with RSP in the
real world?
A: Like JSP, you could use RSP to create dynamic web pages. Most HTML
editing tools will ignore <%..%> sections in a page. They will not be
touched and will be displayed by some small icon only. This allows you
to edit the main content/layout of your page with your favorite HTML
tool. But why limit yourself to HTML? Consider the following XML
template:
xml-template: {
<?xml version="1.0" ?><% foreach record get-my-data [ %>
<transaction>
<name><%= record/1 %></name>
<street><%= record/2 %></street>
<product>
<id><%= record/3 %></id>
<version><%= record/4 %></version>
</product>
</transaction><% ] %>}
This must be the easiest way to produce XML! All that's needed is a
single function 'get-my-data that retrieves the source data for you.
When combined with REBOL database access, it becomes really easy to
obtain data from your favorite database product in XML.
RSP can also be very helpful when sending out bulk email messages where
a small personal touch is required. Just write the email once and send
it out running through your customer database one record at a time.
Q: I want to print a block of data like this:
process-rsp "<%= [ 1 2 3 ] %>"
and all it produces is "123"! That ain't right!
A: Actually, it is. Have you tried "append {} [1 2 3]" in REBOL? This
produces exactly the output you got. If you need a block as a block, try
using "<%= mold [1 2 3] %>" "<%= form [1 2 3] %>" instead.