Hi All,

To solve this problem I do this:��

replaceTokensIn: aString
^self tokens inject: aString into: [:str :assoc |��
((str explode: assoc key) mergeDelimited: assoc value value)].

tokens is a collection of key value pairs.

aString := 'Dear {!user}'.

self tokens: {'{!user}' -> [self user]}.

self replaceTokensIn: aString.

I like using the blocks because they can be almost anything and since they are defined by the developer the security issue goes away.�� It's not a streaming solution which would be nice for a much longer text and it does rewrite the string for every token.����



On Thu, Sep 28, 2017 at 10:27 AM, phil@highoctane.be <phil@highoctane.be> wrote:
We also have��http://norbert.hartl.name/blog/2013/10/03/mustache-templates-for-smalltalk/

Phil

On Thu, Sep 28, 2017 at 4:20 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Hi,

I got into a little office discussion about string interpolation as it is done in different programming languages.

In Pharo we have String>>#format: which is pretty nice. It works as follows:

| x y |
x := 123.
y := #foo.
'x={1} and y={2}' format: { x. y }.

It is also possible to use a dictionary with keys, like this:

| x y |
x := 123.
y := #foo.
'x={x} and y={y}' format: { #x->x. #y->y } asDictionary.

But this is not true string interpolation as described in [ https://en.wikipedia.org/wiki/String_interpolation ]. The idea is to write the value generating expressions directly inside the strings.

Since in Pharo we add features not by extending the syntax but by adding messages I wondered if it could be done for string interpolation. The goal is to make the following work:

| x y |
x := 123.
y := #foo.
'It seems x equals {x} and y equals {y} while Pi is still {Float pi}' interpolate.

��=> 'It seems x equals 123 and y equals foo while Pi is still 3.141592653589793'

Here is the implementation I came up with:

String>>#interpolate
�� "Format the receiver by interpolating the evaluation of expressions
�� in between curly brackets in the context of the sender as in the following 3 oneline examples.
�� 'Today is {Date today}' interpolate.
�� | x | x := 123. 'x equals {x} and pi equals {Float pi}' interpolate.
�� 'In {#strings} you can escape \{ by prefixing it with \\' interpolate."

�� | senderContext |
�� senderContext := thisContext sender.
�� ^ self class new: self size streamContents: [ :out | | stream |
�� �� �� stream := self readStream.
�� �� �� [ stream atEnd ] whileFalse: [ | currentChar |
�� �� �� �� (currentChar := stream next) == ${
�� �� �� �� �� ifTrue: [ | expression result |
�� �� �� �� �� �� expression := stream upTo: $}.
�� �� �� �� �� �� result := Compiler new
�� �� �� �� �� �� �� evaluate: expression in: senderContext to: nil notifying: nil ifFail: [ ^ nil ] logged: false.
�� �� �� �� �� �� out nextPutAll: result asString ]
�� �� �� �� �� ifFalse: [
�� �� �� �� �� �� currentChar == $\
�� �� �� �� �� �� �� ifTrue: [ stream atEnd ifFalse: [ out nextPut: stream next ] ]
�� �� �� �� �� �� �� ifFalse: [ out nextPut: currentChar ] ] ] ]

It is a hack that could certainly be improved. And there is of course an obvious security problem.

Thoughts ?

Sven