Templates with the Power of Smalltalk
- Content agnostic.
- Can create nested closures.
- Evaluate the expression or
- Display last object of your expression as String.
- Compiles content lazily only once.
- Opening and closing tags and display token can be customized.
"In Pharo 10"
"By default it installs 'Core'"
Metacello new
baseline: 'STTemplate';
repository: 'github://sebastianconcept/STTemplate:latest';
load.
"Optionally use `load: #('Core' 'Tests')`"
Or as dependency in your Baseline
spec baseline: 'STTemplate' with: [ spec repository: 'github://sebastianconcept/STTemplate:latest' ]
STTemplate
is a Smalltalk template class.
It can run Smalltalk code found between the opening (<%
) and closing (%>
) tags when present in the content.
There are two ways to have Smalltalk running from STTemplate
instances:
Analogous to a printIt
'<p><%= ''Hello '', self %></p>' sttRenderOn: 'STT'.
"'<p>Hello STT</p>'"
Analogous to a doIt
'<p>Hello STT<% 40+2 %></p>' sttRenderOn: 'STT'.
"'<p>Hello STT</p>'"
'<p>Hello STT<% self crShow: ''Greetings from an STT closure!'' %></p>' sttRenderOn: Transcript.
"'<p>Hello STT</p>'"
"And in the Transcript you'll see:"
'Greetings from an STT closure!'
"Displays the 42 as content of the span element:"
'<p>The Answer: <span><%= 42%></span></p>' sttRenderOn: nil.
"'<p>The Answer: <span>42</span></p>'"
"Displays the 42 as content of the span element because
adds 2 to the context sent with `sttRenderOn: 40`"
'<p>The Answer: <span><%= self + 2%></span></p>' sttRenderOn: 40.
"'<p>The Answer: <span>42</span></p>'"
"Render the template on the given association, hence `self`,
so it displays `Answer: ` coming from `self key capitalized, ': '`
and then `42` coming from displaying `self value + 2`"
'<p>The <span><%= self key capitalized, '': ''%></span><span><%= self value + 2%></span></p>' sttRenderOn: (#answer -> 40).
"'<p>The <span>Answer: </span><span>42</span></p>'"
"Creates a closure that does not display but iterates an interval from 1 to 3.
In each iteration, it creates another Smalltalk closure that has access
to self as expected and uses that to display its content:"
'<% 1 to: 3 do: [ :i | %><p>The Answer is <span><%= self value + 2%>!</span></p><% ] %>' sttRenderOn: (#answer -> 40).
"'<p>The Answer is <span>42!</span></p><p>The Answer is <span>42!</span></p><p>The Answer is <span>42!</span></p>'"
Try SSTemplate on Teapot. The most basic example to use an STTemplate
to render the response of an API's endpoint.
Try an SSTemplate based counter using Teapot and htmx
See all the tests in STTemplateTest
.
On the performance side it should be noted that at least half the motivation for using templates is because they should be "cheap" to render.
That said, in the first proof of concept here, it was compiling the template sources at every evaluation and in this model used here it roughly was doing 450 renders/sec which is of course not impressive at all but also not needed.
Why extracting code and compiling at every render if you strictly need it only once?
The streams based implementation with lazily compiled method caching and using a proper model it went from ~450 renders/sec to ~72K renders/sec so that is not going to be the bottleneck of your web app:
Time to publish as a pre-release version.
While searching for a templating system in Smalltalk I've stumbled upon Norbert Hartl's Mustache implementation. I've adopted it in Ride v0.0.15
and it worked remarkably well. As per january 2024, I have it production in one case here: PhotograFX.
While Mustache templates do their job, there are two issues with them:
- The controllers having them as views, might become a bit busy in preparing the model for the Mustache templates and
- Mustache templates sometimes obscured issues in their visitor mechanism, making seemingly simple problems harder to pinpoint.
This motivated me to explore a design based on efficient templates tailored for Smalltalk, akin to what .erb
templates do for Ruby on Rails.
Despite my extensive experience with Seaside, I wanted to delve into the concept of a Smalltalk web app driven by templates. The primary motivation was to maintain easy the adoption of already made HTML and a stateless software design, anticipating linear memory scaling and maximizing operations per second per image.
Searching further resulted in finding Smalltalk/X - STT - Smalltalk Templates and a bit later this one from 2002 for Squeak Squeak STT - Smalltalk Templates. After loading it and doing some adapting it revealed not to cover needed cases but inspired creating this library here. Thank you Federico Gregorio Stilman and Diego Gomez Deck for that inspiring proof of concept.