redefining Go templates: sometimes works, sometimes fails - go

The following Go template processes without error:
{{block "A" "hello"}}{{end}}
{{define "A"}}{{.}}{{end}}
The output is "hello", as I would expect from reading the documentation. In contrast, the following template does not parse:
{{block "A" "hello"}}A{{end}}
{{define "A"}}{{.}}{{end}}
Here I get the error message
template: multiple definition of template "A"
Why does the second template give an error while the first does not? Is this difference intended?
Complete code is here: https://play.golang.org/p/CNAqllVLjB

The answer lies in the doc of html/template.Parse():
[...] It is an error if a resulting template is non-empty (contains content other than template definitions) and would replace a non-empty template with the same name. (In multiple calls to Parse with the same receiver template, only one call can contain text other than space, comments, and template definitions.)
Your first template works because {{block "A"}} defines an empty template, and so it is allowed to be re-defined.
Your second template fails because {{block "A"}} defines a non-empty template and yet you try to re-define it with a non-empty template.
One thing to note here: I quoted the doc from the html/template package, which should be "identical" to text/template. It is most of the time, but text/template.Parse() is different, and leaves out this important detail, but they work the same way. This is a documentation inconsistency, filed an issue which can be tracked here: issue #17360.

Related

How can I reference a YAML anchor from another YAML anchor?

I have a YAML file which has several different keys that I want to provide the same value for. Additionally, I want this value to be easily configurable.
See YAML for my specific use-case below:
---
# Options for attribute_value
att_value_1: &att_value_1
att_value_2: &att_value_2
# There could be more options...
# This is where the constant is being set.
attribute_value: &attribute_value *att_value1
items:
- name: item1
attributes:
attribute_for_item1: *attribute_value
- name: item2
attributes:
attribute_for_item2: *attribute_value
Here is a simplified YAML which demonstrates the problem:
---
foo: &foo "Hello World!"
bar: &bar *foo
Error (it's complaining about the first line that has "Hello World!" on it):
(<unknown>): did not find expected key while parsing a block mapping at line 2 column 1
I expect the value to propagate.
Error (it's complaining about the first line that has "Hello World!" on it):
You have to tell us which YAML implementation you're using. PyYAML and NimYAML both correctly report that the error is in the third line; the second line is okay.
I expect the value to propagate.
There is nothing in the specification that backs this expectation. The spec says:
Note that an alias node must not specify any properties or content, as these were already specified at the first occurrence of the node.
Properties are anchors and tags. You cannot put an anchor on an alias node since it already has an anchor.
In your example,
---
foo: &foo "Hello World!"
bar: &bar *foo
&bar is completely superfluous since you already can refer to the "Hello World" node via *foo and therefore, there is no point in introducing &bar.
To answer your question: You cannot reference a YAML anchor from another YAML anchor, because a YAML anchor is not a node. YAML represents a graph, i.e. nodes and directed edges. Anchors and aliases are used to reference the same node multiple times (not to copy values as you might think). This also means that everything within a YAML file is content. There is no such thing as variable declarations.
It seems you are using the wrong tool for your use-case. To configure YAML files with external values, you would typically use a templating engine (SaltStack and Ansible use Jinja for example) and generate your YAML file from a template. You could supply your options in another YAML file. This would clearly separate the configuration options from the content.

Where are Freemarker Functions set?

I have not used Freemarker before and inherited some templates that won't compile. I have narrowed down the line that is causing problems to:
${text('Text and {0}', 'More Text')}
Where does this text() function come from? I understand that its trying to return the second string injected into the first like: "Text and More Text".
Here is the error:
FreeMarker template error:
For "...(...)" callee: Expected a method, but this has evaluated to an extended_hash (wrapper: f.t.SimpleHash):
modernizr-2.6.2.js is the only javascript imported.
Any ideas why this line is causing problems and where this function should be declared??
It can come from a few places:
From the data-model (one of the parameters you pass to Template.process)
From a shared variable, which is added to the Configuration singleton.
From an #include-d template (or from the template you are in), via #function text or #assign text = ... (or #global text = ...)
From an #import-ed template via #global text = ... (highly unlikely...)
But one potentially interesting thing is that the error says that text does exist, but it's a hash (a Map-like thing), not a callable thing. Maybe something that's also called text shadows the good text? What does ${.data_model.text('Text and {0}', 'More Text')} say?
You mention a JavaScript file. FreeMarker has nothing to do with JavaScript (and it runs on the server, inside the JVM).

How to get list of parameters in a template?

Using Templates how can I get list parameters that are defined in a template. For example if I have the template:
t, _ := template.New("template_name").Parse("<h1>{{.title}}</h1>r{{.release}}")
How can I get title and release? I intend to iterate over list of parameters and search them in multiple locations. How this could be done is not clear from the template documentation.
I would assume you could walk through the parse.Tree that is inside both html/template.Template as well as text/template.Template. You'd have to check every node in the tree recursively to see if it's a FieldNode or not (and I don't really know, how variables defined within a template - like in a range operator - are handled). If all fields are as simple as in your example, you could probably just plain-text search your template ... ;o)

Xpath - How to navigate to a value (Ruby Nokogiri)

If I want to grab a currencies rate, say "USD", given a certain time, say "2015-02-09", how would I go about doing this?
I tried the following:
/gesmes:Envelope/def:Cube/def:Cube[#time="2014-11-19"]/def:Cube[#currency="USD"]/#rate
Though I suppose due a lack of understanding this is wrong, well at least, I know it is wrong because Nokogiri does not run it.
http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml
EDIT:
I'm going to go ahead and guess that I am not correctly using Nokogiri and XPath.
#doc = Nokogiri::XML(File.open("exchange_data.xml"))
#values = #doc.xpath('XPATH HERE')
#values.each {|i| puts i}
I have read the tutorial, and managed to get it working for other xml files, but this one seems harder to crack.
require 'nokogiri'
doc = Nokogiri::XML(File.open("xml4.xml"))
target_date = "2015-02-09"
target_currency = 'USD'
xpaths = [
"//gesmes:Envelope",
"/xmlns:Cube",
"/xmlns:Cube[#time='#{target_date}']",
"/xmlns:Cube[#currency='#{target_currency}']",
]
xpath = xpaths.join
target_cube = doc.at_xpath(xpath)
puts target_cube.attribute('rate')
--output:--
1.1297
Response to comment:
Your root tag:
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01"
xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
...declares two namespaces with xmlns, which stands for xml namespace. The namespace:
xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01"
declares that any child tag whose name is prefixed by gesmes, e.g.:
<gesmes:subject>
...
</gesmes:subject>
will actually have a tag name that incorporates the specified url into the tag name, something like this:
<http://www.gesmes.org/xml/2002-08-01:subject>
...
</http://www.gesmes.org/xml/2002-08-01:subject>
The reason you would want to use a namespace is to create a unique name for the Cube tag, so that it doesn't clash with another xml document's Cube tag.
The second namespace declaration:
xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref"
is a default namespace declaration. It declares that any child tag that does not specify a prefix will have the specified url incorporated into its tag name. So a tag like this:
<Cube>
...
</Cube>
becomes something like this:
<http://www.ecb.int/vocabulary/2002-08-01/eurofxref:Cube>
...
</http://www.ecb.int/vocabulary/2002-08-01/eurofxref:Cube>
However, it would be unwieldy to have to write a tag name like that in your xpaths, so in place of the url you instead use the shortcut xmlns:
/xmlns:Cube
This might be due to the namespaces in this document:
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
To test this hypothesis, apply the following XPath expression:
/*[local-name() = 'Envelope']/*[local-name() = 'Cube']/*[local-name() = 'Cube'][#time="2014-11-19"]/*[local-name() = 'Cube'][#currency="USD"]/#rate
and let me know what you get. If you are otherwise correctly using XPath, you should end up with:
rate="1.2535"
If not, you are not using the XPath facilities of Nokogiri correctly, and then you'd really need to show all of your Ruby code to get help.
EDIT
Responding to a comment:
I look forward to seeing some examples added to your answer, so that I can learn something new about xml namespaces. – 7stud
7stud already gave the correct answer, I'll only add info I think is missing from this answer.
Explicit namespaces
First of all, if a namespace URI is explicitly present on an element, the correct syntax uses curly brackets, both for a prefixed and default namespace:
<{http://www.gesmes.org/xml/2002-08-01}subject>
Internally, this is how namespaces could be represented on elements (although some applications have other ways to associate elements with namespaces). Prefixes and default namespaces are there to simplify this process.
Namespaces in Nokogiri
Prefixes (gesmes:) do not have any inherent meaning. They can be associated with an arbitrary namespace URI and every document can use gesmes: to mean something different. Namespace declarations are not available to an XPath engine per se - usually, if you'd like to use a prefix in an XPath expression, you need to declare this namespace again for the XPath processor.
Yet, Nokogiri tries to simplify namespace handling for you by redeclaring namespace declarations found on the root element of the input document. This is important because it allows you to reuse the prefixes declared on the root element of the input without actually declaring the namespace. For default namespaces declared on the root element that do not have a prefix, Nokogiri has defined a special syntax:
xmlns:Cube
Namespaces that are present in the document, but declared on an element other than the root element:
<root>
<child xmlns:gesmes="http://other.com"/>
</root>
must be explicitly declared in Nokogiri:
#doc.xpath('//other:Cube', 'other' => 'http://other.com/')
What's wrong with your original code?
Your code:
/gesmes:Envelope/def:Cube/def:Cube[#time="2014-11-19"]/def:Cube[#currency="USD"]/#rate
does not work because you are using an unknown prefix def:. This prefix is not declared on the root element of the input, and neither did you declare it with Nokogiri. The Cube elements are in the default namespace, and, as we have seen, the correct way to address them is
/gesmes:Envelope/xmlns:Cube
and so on, 7stud gave you the correct answer.

Spread Liquid `include` over mutliple lines

I have an "included" template with several parameters. The contents of the parameters get a bit muddled if I cram them all into a single line, so I would prefer something like this:
{% include product_details
weight= "5.8lbs (2.6 kg)"
width= "22" (56cm)"
length= "49" (125cm)"
thickness= "1¼" (3cm)"
case= "MT51413"
%}
However, this gives me the following error when generating the site:
error: Tag '{%' was not properly terminated with regexp: /\%}/.
Is there any way to spread a Liquid include over several lines?
Sorry you can't, the liquid parser expects the whole statement in one line, this is true for all statements not only for include, try to split an if statement and you receive the same error

Resources