Remove preceding or following newline if a conditional directive is empty - freemarker

Suppose I have a template like this:
some long text
<#if some_condition>
more text
<#elseif another_condition>
different more text
</#if>
trailing long text
If some_condition or another_condition is true, then I get expected output:
some long text
more text
trailing long text
But if both conditions are false, then I get an extra empty line:
some long text
trailing long text
I would prefer to have only one empty line in such case:
some long text
trailing long text
I can achieve this by putting an empty line inside each branch of the condition:
some long text
<#if some_condition>
more text
<#elseif another_condition>
different more text
</#if>
trailing long text
But this makes the template really ugly.
Is there a way to make Freemarker remove an empty line before or after an empty directive block?

Nope. Generating output that's truly whitespace-sensitive (like plain-text documents that humans will read) is generally not practical in FreeMarker, unless the template is quite trivial. What you can do sometimes is post-processing the output, like in this case, remove duplicate line-breaks, but of course that only works if you know that there's never a duplicate line-break intentionally.
By the way, it's a problem with template engines in general. The intent of the template author is often not clear, as the template contains both template source code formatting, and output formatting. And the rabbit hole runs deep, if you also want proper indentation, etc.

Related

Reading and writing back yaml files with multi-line strings

I have to read a yaml file, modify it and write back using pyYAML. Every thing works fine except when there is multi-line string values in single quotes e.g. if input yaml file looks like
FOO:
- Bar: '{"HELLO":
"WORLD"}'
then reading it as data=yaml.load(open("foo.yaml")) and writing it yaml.dump(data, fref, default_flow_style=False) generates something like
FOO:
- Bar: '{"HELLO": "WORLD"}'
i.e. without the extra line for Bar value. Strange thing is that if input file has something like
FOO:
- Bar: '{"HELLO":
"WORLD"}'
i.e. one extra new line for Bar value then writing it back generates the correct number of new lines. Any idea what I am doing wrong?
You are not doing anything wrong, but you probably should have read more of the YAML specification.
According to the (outdated) 1.1 spec that PyYAML implements, within
single quoted scalars:
In a multi-line single-quoted scalar, line breaks are subject to (flow) line folding, and any trailing white space is excluded from the content.
And line-folding:
Line folding allows long lines to be broken for readability, while retaining the original semantics of a single long line. When folding is done, any line break ending an empty line is preserved. In addition, any specific line breaks are also preserved, even when ending a non-empty line.
This means that your first two examples are the same, as the
line-break is read as if there is a space.
The third example is different, because it actually contains a newline after loading, because "any line break ending an empty line is preserved".
In order to understand why that dumps back as it was loaded, you have to know that PyYAML doesn't
maintain any information about the quoting (nor about the single newline in the first example), it
just loads that scalar into a Python string. During dumping PyYAML evaluates how that string
can best be written and the options it considers (unless you try to force things using the default_style argument to dump()): plain style, single quoted style, double quoted style.
PyYAML will use plain style (without quotes) when possible, but since
the string starts with {, this leads to confusion (collision) with
that character's use as the start of a flow style mapping. So quoting
is necessary. Since there are also double quotes in the string, and
there are no characters that need backslash escaping the "cleanest"
representation that PyYAML can choose is single quoted style, and in
that style it needs to represent a line-break by including an emtpy
line withing the single quoted scalar.
I would personally prefer using a block style literal scalar to represent your last example:
FOO:
- Bar: |
{"HELLO":
"WORLD"}
but if you load, then dump that using PyYAML its readability would be lost.
Although worded differently in the YAML 1.2 specification (released almost 10 years ago) the line-folding works the same, so this would "work" in a similar way with a more up-to-date YAML loader/dumper. My package ruamel.yaml, for loading/dumping YAML 1.2 will properly maintain the block style if you set the attribute preserve_quotes = True on the YAML() instance, but it will still get rid of the newline in your first example. This could be implemented (as is shown by ruamel.yaml preserving appropriate newline positions in folded style block scalars), but nobody ever asked for that, probably because if people want that kind of control over wrapping they use a block style to start with.

How to I write a string with quotes and hash tags in yaml?

I have blocks of text that I want to import into a yaml file. The text includes special characters and I do not want to have to go line by line and escape each special character. How do I write the following as a string in Yaml without it converting anything to comments or choaking?
html: <p class='myParagraph'>The teacher said, "Use a #2 pencil."<br/>Question 1: 1\2=?</p>
There are single quotes, double quotes, angle brackets, forward and back slashes, and a hash.
Context
YAML 1.1
Problem
OlegOlaf McCoyly wishes to specify a string in YAML, where one or more embedded characters in the string could cause delimiter collision with the YAML syntax itself, thus breaking the YAML file.
Solution
Specify the block of text as a YAML multiline-string:
html: |
<p class='myParagraph'>"Use a #2 pencil."<br/>Question 1: 1\2=?</p>

Remove lines in Freemarker output

I am generating an odt file using Freemarker template.
My code is:
${(addressline1)!}
${(addressline2)!}
${(addressline3)!}
The problem is that when addresline1 or addressline2 or is empty, it generates an empty line. If all the fields are empty it generates three empty lines.
I have tried the following things:
[#if addressline2??]${(addressline2)!} [/#if]
[#if addressline2?has_content]${(addressline2)!} [/#if]
compress directive
[#compress]
${(addressline1)!}
${(addressline2)!}
${(addressline3)!} [/#compress]
but nothing seems to be working.
Do you edit the template in OpenOffice/LibreOffice or such visual editor? Because then the problem is that although you just see a ${something} there, FreeMarker sees <p>${something}</p>. As it's just a general purpose template text engine, it won't remove the <p></p> that remains there when the inserted text was empty. The application that uses it for ODT should give a way of purging empty paragraphs.
You should be able to get it working by doing this:
<#if addressline1 !== ''>${addressline1}</#if>
Just repeat the process to check for all 3 variables. Alternatively, if they don't exist rather than them just being empty, this code should do the work:
<#if addressline1?exists>${addressline1}</#if>
You can find further documentation in the Freemarker Manual website: http://freemarker.org/docs/index.html
Had the same problem. This is what I ended up with:
<#assign i2 = ""?left_pad(2*4)>
<#assign i3 = ""?left_pad(3*4)>
...
<#if dep.version??>${i3}<version>${dep.version}</version>${"\n"}</#if><#t>
...
Copied from my bug report:
I'd like to be able to have the NEW LINE generated conditionally, with 1 line of template source code.
<#if dep.classifier??><classifier>${dep.classifier}</classifier></#if>
This generates the indent spaces and the newline, no matter what.
{{#t}} and {{#rt}} and {{#lt}} apply at compile time, so they don't care about the condition. So they strip even if the content is there, which I don't want.
I could do this:
<#if dep.classifier??>${" "}<classifier>${dep.classifier}</classifier>${"\n"}</#if><#t>
So that's the request. It can also be anything what helps me achieve that.
Until then, I have to do:
<#if dep.classifier??>
<classifier>${dep.classifier}</classifier>
</#if>
Which I don't like because it makes the templates way longer.
Or
<#if dep.classifier??><classifier>${dep.classifier}</classifier>
</#if><#if dep.classifier??><classifier>${dep.classifier}</classifier>
</#if>
...
Which I don't like because it's messy.
Or
<#if dep.classifier??>${" "}<classifier>${dep.classifier}</classifier>${"\n"}</#if><#t>
Which I don't like because it adds superfluous pieces to the template.
The trick is to place starting <#compress> tag in the previous line of the line you need to remove if empty.
${keep this line always}<#compress>
${remove this line if empty}</#compress>
${keep this line always}
Works with freemarker 2.3.31

Freemarker Interpolation stripping whitespace?

I seem to be having issues with leading/trailing spaces in textareas!
If the last user has typed values into a textarea with leading/trailing spaces across multiple lines, they all disappear with exception to one space in the beginning & end.
Example:
If the textbox had the following lines: (quotes present only to help illustrate spaces)
" 3.0"
" 2.2 "
"0.3 "
it would be saved in the backend as
"<textarea id=... > 3.0/n 2.2 /n0.3 </textarea>"
My template (for this part) is fairly straightforward (entire template, not as easy...): ${label} ${textField}
When I load up the values again, I notice getTextField() is properly getting the desired string, quoted earlier... But when I look at the html page it's showing
" 3.0"
"2.2"
"0.3 "
And of course when "View Sourcing" it doesn't have the string seen in getTextField()
What I've tried:
Ensure the backend has setWhitespaceStripping(false); set
Adding the <#ftl strip_whitespace=false>
Adding the <#nl> on the same line as ${textField}
No matter what I've tried, I'm not having luck keeping the spaces after the interpolation.
Any help would be very appreciated!
Maybe you are inside a <#compress>...</#compress> (or <#compress>...</#compress>) block. Those filter the whole output on runtime and reduce whitespace regardless where it comes from. I recommend not using this directive. It makes the output somewhat smaller, but it has runtime overhead, and can corrupt output in cases like this.
FreeMarker interpolations don't remove whitespace from the inserted value, or change the value in any way. Except, if you are lexically inside an <#escape ...>....</#escape>, block, that will be automatically applied. But it's unlikely that you have an escaping expression that corrupts whitespace. But to be sure., you can check if there's any <#escape ...> in the same template file (no need to check elsewhere, as it's not a runtime directive).
strip_whitespace and #nt are only removing white-space during parsing (that's before execution), so they are unrelated.
You can also check if the whitespace is still there in the inserted value before inserting like this:
${textField?replace(" ", "[S]")?replace("\n", "[N]")?replace("\t", "[T]")}
If you find that they were already removed that probably means that they were already removed before the value was put into the data-model. So then if wasn't FreeMarker.

Using an "uncommon" delimiter for creating arrays in Ruby on Rails

I am building an app in Ruby on Rails in which I am pulling in content another file, and wonder if there's any simple way to create a unique delimiter for separating string content, or whether there's another approach I should take.
Let's say I have a paragraph of text, I'd like to pull in, and let's say I don't know what the text will contain.
What I would like to do is put some sort of delimiter at, let's say, 5 random points in the paragraph so that, later on, an array can be created in which content up to that delimiter can be separated out into an individual element.
For a bit of context, let's say I have a paragraph pulled in as a string:
Hello, this is a paragraph of text which will be delimited. Goodbye.
Now, let's say I add a delimiter at various points, as follows (I know how to do this in code):
Hello, this [DELIMITER] is a paragraph [DELIMITER] of text which [DELIMITER] will [DELIMITER] be delimitted. Goodbye.
Again, I know how to do this, but let's say I'm able to use the above to create an array as follows:
my_array = ["Hello, this", "is a paragraph", "of text which", "will", "be delimitted. Goodbye"
I'm confident of achieving all of the above. The challenge I'm having is: what should my delimiter be?
Normally, commas are used as delimiters but, if the text already includes a comma, this will result in delimitations where I do not wish them to occur. In the above example, for example, the comma between "Hello" and "this" would cause the "Hello, this" element to be split up into "Hello" and "this"—not what I want.
What I have thought of doing is using a random (hex) number generator to create a new delimiter each time the page is loaded, e.g. "Hello, this 023ABCDEF is a paragraph 023ABCDEF...", but I'm not sure this is the correct approach.
Is there a simpler solution?
Multipart mime messages take (more or less) the approach of a GUID separator; it's adequate.
I view this as a different type of problem, though, closer to a text editor marking sections of text bold, or italic, etc. That can be handled via string parsing (a la Markdown, SO's formatting) or data structures.
The text editor approach is generally more flexible, and instead of a simple collection of strings, uses a collection (or tree) of structures that hold metadata about the section (type, formatting, whatever).
The best approach depends on your needs:
Are sections nestable?
Will this be rendered?
If so, do section "types" need specific rendering?
Are there section "types", or are they all the same?
Will the text in question be edited before, during, or after sectioning?
Etc.

Resources