Liquid::SyntaxError when variable starts with a number - ruby

When parsing a template that contains a variable that begins with a number
script = '{% if variable==true or 2variable==false%}{% remove %}{% endif %}'
I get the SyntaxError inside warnings:
Liquid::Template.parse(script, error_mode: :warn).warnings
Liquid::SyntaxError: Liquid syntax error: Expected end_of_string but found id in "variable==true or 2variable==false"
If I change the variable name to not begin with a number I don't get any warnings.
I don't understand why this scenario translates into a SyntaxError. Is there a liquid convention that states variables should not begin with a number?

Related

What value will be return that the "return another Undefined value" said in the Ansible document and when we use filter in Jinja2?

I was reading the ansible document and then it said:
Beginning in version 2.8, attempting to access an attribute of an
Undefined value in Jinja will return another Undefined value, rather
than throwing an error immediately. This means that you can now simply
use a default with a value in a nested data structure (in other words,
{{ foo.bar.baz | default('DEFAULT') }}) when you do not know if the
intermediate values are defined.
I can not understand it well. Is it said the expression "{{ foo.bar.baz | default('DEFAULT') }}" will be 'DEFAULT' when foo.bar.baz is not defined or it is said the expression "{{ foo.bar.baz }}" will be another value(mark it as VALUE) when foo.bar.baz is not defined and we need to defind another optional or seccond value like default('DEFAULT') in order to avoid making the expression return VALUE that we do not what it will be at all?
The url containing the statement is at: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#omitting-parameters
I got the quotation by a little mouse wheel rotation.
The important part of the statement is in the "when you do not know if the intermediate values are defined".
Prior to 2.8, if you had:
{{ foo.bar.baz | default('DEFAULT') }}
with foo NOT having bar defined (ie. trying to access the attribute baz of the undefined variable bar), the error would be thrown by Jinja2, before reaching the | default(). With the changes referenced by the statement, trying to access baz when bar is not defined (the intermediate value), will not throw an error, but return another undefined, so that the | default() filter will intercept and be able to return DEFAULT.

Assigning empty string if XML node doesn't exist in Freemarker

I have an XML document passed as root to a Freemarker template. I want some values from this XML to be assigned to variables as a string and later concatenate/print them out.
<#assign MyVar = root.child1.child2.child3.mynode>
The issue here is that even when a path doesn't exist MyVar gets assigned with a sequence+hash which cannot be printed out or converted to string. This variable although returns false for ?has_content, it needs an extra step for these checks and I have this same issue with many variables and across template files and modules.
The only solution I have been able to find was
<#assign MyVar = root.child1.child2.child3.mynode>
<#assign MyVar = MyVar ?has_content?then(MyVar , "")>
I am looking for something like the Default Value Operator which also checks for nulls like ?has_content.
Does Freemarker provide any simpler one line function to check if a variable has no content and assign it with a default?
In short:
<#assign myVar = root.child1.child2.child3.mynode[0]!''>
Or just <#assign myVar = root.child1.child2.child3.mynode[0]!> if the implicit multi-typed default value doesn't cause problems (like when you just print it with ${}).
Why: XML queries (just like XPath queries) always return a sequence of matching nodes. There are maybe 0 such nodes (or 1, or multiple). An empty sequence is not a "missing value" according the template language. It's an inconvenient mismatch with the XML data-model. But while the sequence always exists, its 1st element ([0]) doesn't, so you can use all the missing value handler operators with it as usual.

How do I check if variable defined in an Ansible playbook contains a string value?

I want to validate that the input values passed to the variables as extra_args.
I want to run a pre-task that passes if the variable contains a string value, else fails if it contains anything else.
The values are passed to them as extra_args when executing the playbook.
I want to run a pre-task that passes if the variable contains a string value, else fails if it contains anything else.
This task fails if the variable is not a string object:
- fail:
when: variable is not string
But be aware that all values passed as extra-vars will be strings, because that's what they are -- anything you type on your keyboard is a valid string. As there is no type declaration, even if a variable contains a numerical value, it will be stored in a string object.
It is different to variable values defined in YAML which undergo type autodetection performed by YAML parser. For example if you type myvar: true in YAML, it will be considered Boolean object true, but if you pass the same value with --extra-vars "myvar:true", it will be a string object true.
You need to specify another condition.
Here are few filters and tests in ansible you may find it useful
http://docs.ansible.com/ansible/latest/playbooks_filters.html
http://docs.ansible.com/ansible/latest/playbooks_tests.html
for validation you might use it as follows:
tasks:
- fail: msg="Variable '{{ item }}' is not a string"
when: string | search("^[a-zA-Z]*$")
I prefer to use the 'assert' module for such cases like this.
- name: Test if the type of 'variable' is string
assert:
that:
- variable is defined
- variable is string
fail_msg: |
variable: {{ variable | d() | to_nice_json }}
seealso: type check examples: https://github.com/ssato/ansible-role-assertive-programming-examples/blob/master/tasks/pre_type_checks.yml
BTW, if you want to define variables with types you want using --extra-vars (-e) option, you need to prepare yaml files define these variables and let them loaded using '#' such like '-e #/path/to/the/yaml_file'.

Conditionally Create a variable

I have some python code that runs in multiple environments. The code checks for the existence of environment variables, and takes a slightly different path depending on what environment variable is defined. The code is:
def _get_sink_to_function():
# Determine which path we send data to, based on env variables
# One of the following env variables must be present.
funcdict = {
'LOG_SINK': _post_gzip_to_log_sink,
'FIREHOSE_SINK': _post_gzip_to_firehose_sink
}
for v in funcdict.keys():
if os.environ.get(v):
return funcdict[v]
return None
main():
sink_to_func = _get_sink_to_function()
if not sink_to_func:
raise AssertionError('Missing mandatory env variable')
...
sink_to_func(gzip_data)
The environment variables are set using ansible.
Therefore, I would like to conditionally create the environement variable.
For example, in this (valid) ansible code:
LOG_SYNC: "{% if logger == 'one' %}path-to-the-log{% endif %}"
LOG_SYNC is always defined, sometimes with a value of "path-to-the-log", and sometimes as empty. I want LOG_SYNC not to be defined at all if logger is not equal to 'one'.
In other words, I want something like:
{% if logger == 'one' %}
LOG_SYNC: "path-to-the-log"
{% endif %}
But, that does not seem to be allowed.
The compiler, in whatever language you have written code, will need the variable to be defined first, if it is going to be used anytime later.
so you will need to define the variable, assign value NULL then it will not occupy any memory

Jinja2 Space in variable

"custom_fields": {
"Datacentre Code": "p",
"Region": "EU"
},
I have the data format above, and I want to access the "Datacentre code" and return the result.
To access the Region I run the below command
{% set Region = item.resultDC.custom_fields.Region %}
To access the DC code I am running the below command
{% set DCCode= item.resultDC.custom_fields.Datacentre Code %}
This gives me the following error, how do I get this variable
AnsibleError: template error while templating string: expected token 'end of statement block', got 'Code
I believe that you can use the syntax item.resultDC.custom_fields['Datacentre Code'] to access properties of a dict like that.

Resources