Open perchingeagle opened 8 years ago
GRMustache.swift is able to do it properly:
do {
let data = [
"lambda": Box(Lambda { ">" })
]
let template = try! Template(string: "<{{lambda}}{{{lambda}}}")
// <>>
let rendering = try! template.render(Box(data))
}
do {
let data = [
"planet": Box("world"),
"lambda": Box(Lambda { "{{planet}}" })
]
let template = try! Template(string: "Hello, {{lambda}}")
// Hello, world
let rendering = try! template.render(Box(data))
}
do {
let data = [
"planet": Box("<"),
"lambda": Box(Lambda { "{{planet}}" })
]
let template = try! Template(string: "Hello, {{lambda}}")
// Hello, <
let rendering = try! template.render(Box(data))
}
It is able to do so by processing the string returned by the lambda as a text template (a template that does not escape double-mustache tags), and then escaping the full rendering of the text lambda.
I think this avoids total brain-fucked rendering of the majority of lambdas (those with double-mustache tags). But it requires introducing the concept of a text template in the rendering implementation. And it breaks when one wants to use triple-mustache tags in lambdas (note how both {{planet}}
and {{{planet}}}
have the same rendering below):
do {
let data = [
"planet": Box("<"),
"lambda": Box(Lambda { "{{planet}} {{{planet}}}" })
]
let template = try! Template(string: "Hello, {{lambda}}")
// Hello, < <
let rendering = try! template.render(Box(data))
}
Well, what is the conclusion? The lambda spec is broken. A proper lambda shouldn't return a template string, but allow user to perform rendering (and interact with the rendering engine HTML escaping). This is the way chosen by GRMustache and Handlebars. GRMustache implements lambdas on top of this more general solution, but with the caveats implied by the specification.
FYI, the GRMustache way of dealing properly with a lambda that renders "{{planet}} {{{planet}}}"
is the following.
First with the general rendering solution:
let data = [
"planet": Box("<"),
// RenderingInfo contains all necessary input, and the Rendering result
// is a string tagged with a content type (HTML or text).
"lambda": Box({ (info: RenderingInfo) -> Rendering in
try! Template(string: "{{planet}} {{{planet}}}").render(info.context)
})
]
let template = try! Template(string: "Hello, {{lambda}}")
// Hello, < <
let rendering = try! template.render(Box(data))
Or with a template instead of a lambda (this technique is also called dynamic partial:
let lambdaTemplate = try! Template(string: "{{planet}} {{{planet}}}")
let data = [
"planet": Box("<"),
"lambda": Box(lambdaTemplate)
]
let template = try! Template(string: "Hello, {{lambda}}")
// Hello, < <
let rendering = try! template.render(Box(data))
First of all, thank you for taking some time out of your schedule to read and write a response to my problem / question. I thought as much that the specification is broken. If those two rules weren't implemented together, there would be no problem, or if interpolations didn't have lambdas as an option, then there wouldn't have been this issue. While the triple lambda solves this problem, the triple lambda also has it own problem with delimiters for example:
{{regular-tag}} {{{triple-mustache}}} {{=[ ]=}} [regular-tag] [{triple-mustache}]
You're welcome, @perchingeagle. But don't expect the spec to change: it's stuck (it hasn't changed in years despite the work done by many implementors).
The way to avoid HTML escaping with custom delimiters is &
tags: {{{foo}}}
is equivalent to {{&foo}}
, and to {{=[ ]=}} [&foo]
. &
was introduced specifically for custom delimiters. Quoting https://mustache.github.io/mustache.5.html:
You can also use & to unescape a variable: {{& name}}. This may be useful when changing delimiters (see "Set Delimiter" below).
I guess the current solution would be the choice of which tag to use for each situation:
{{& tag }} // for delimiters
{{{ tag }}} // for mutual lambda interpolation expansion and escaping
Thanks :+1:
I guess it is safe to say that this issue is now closed again @groue thank you
Why did you close this issue? The lambda spec has an issue - there's no point hiding it under the carpet, is it?
I have reopened it.
There is a rule for each of the first two codes, but if you trying using them mutually, like in the third example, it creates a problem.
Lambda Escaping (code 1) lambda rule 5
Lambda Interpolation Expansion (code 2) lambda rule 2
Now simply change the value of data['planet'] from 'world' to '<' and there is the problem:
And this is what the rule currently specifies. Is there a clause in the rules or specification that prevents this?