How to add source code snippets into _data yaml in Jekyll? - jekyll

I'm trying to create code snippets as examples for my project website created in Jekyll.
I have yaml that look like this:
-
description: "Scheme hygienic macro that creates assoc list, with macroexpand."
code: >
(define-syntax alist
(syntax-rules ()
((_) ())
((_ x y z ...)
(cons (cons x y) (alist z ...)))))
(print (alist "foo" 10 "bar" 20 "baz" 30))
;; ==> ((foo . 10) (bar . 20) (baz . 30))
(macroexpand (alist "foo" 10 "bar" 20))
;; ==> (#:cons (#:cons "foo" 10)
(#:cons (#:cons "bar" 20)
()))
the problem is that lines that are exactly the same indent as first line are concatenated into single line. I want this to be exactly like in yaml in my pre tag. If I indent every line that be one more space I get extra space in pre tag when I'm adding the snippet.
Is there any way to make it insert exactly the code I have in yaml file? how to properly indent in yaml to not have then spaces before each line and have each line in it's own line in output html?
I was trying to add quotes around the string but they are present in output and I was not able to removed them in Liquid.
I've tried this:
{% for example in site.data.examples %}
{% assign code = example.code | split: '^"|"$' %}
<li>
<div class="example">
<pre>{{ code[1] }}</pre>
</div>
<div class="description">
{{ example.description }}
</div>
</li>
{% endfor %}
but split don't accept regular expressions. I've also tried:
{% assign len = example.code.length %}
{% assign code = example.code | slice: 1,len-2 %}
but it only prints first parenthesis.
EDIT:
I've solved issue with removing quotes but that was dead end, I still got spaces before each line.
This is a way to remove quotes from string in Jekyll:
{% assign code = example.code | remove_first: '"' | rstrip | append: "__" | remove: '"__' %}

It seems that this is simpler than I though. All that is required is block in Yaml (ref: https://idratherbewriting.com/documentation-theme-jekyll/mydoc_yaml_tutorial.html)
-
description: "Scheme hygienic macro that creates assoc list, with macroexpand."
code: |
(define-syntax alist
(syntax-rules ()
((_) ())
((_ x y z ...)
(cons (cons x y) (alist z ...)))))
(print (alist "foo" 10 "bar" 20 "baz" 30))
;; ==> ((foo . 10) (bar . 20) (baz . 30))
(macroexpand (alist "foo" 10 "bar" 20))
;; ==> (#:cons (#:cons "foo" 10)
(#:cons (#:cons "bar" 20)
()))
And you can use normal variable:
{% for example in site.data.examples %}
<li>
<div class="example">
<pre>{{ example.code }}</pre>
</div>
<div class="description">
{{ example.description }}
</div>
</li>
{% endfor %}
If above don't work for you, here is old version of the answer:
Liquid is very limited but I was able to create this hack:
{% for example in site.data.examples %}
{% assign code = example.code | remove_first: '"' | rstrip | append: "__" | remove: '"__' | newline_to_br | split: "<br />" %}
<li>
<div class="example">
<pre>{% for line in code %}{{ line | remove_first: " " }}{% endfor %}</pre>
</div>
<div class="description">
{{ example.description }}
</div>
</li>
{% endfor %}
and for reference this is my examples.yaml
-
description: "Scheme hygienic macro that creates assoc list, with macroexpand."
code: >
" (define-syntax alist
(syntax-rules ()
((_) ())
((_ x y z ...)
(cons (cons x y) (alist z ...)))))
(print (alist "foo" 10 "bar" 20 "baz" 30))
;; ==> ((foo . 10) (bar . 20) (baz . 30))
(macroexpand (alist "foo" 10 "bar" 20))
;; ==> (#:cons (#:cons "foo" 10)
(#:cons (#:cons "bar" 20)
()))"

Related

Accessing values of multiple lists using an index in Django template tags [duplicate]

I have two list objects of the same length with complementary data i want to render is there a way to render both at the same time ie.
{% for i,j in table, total %}
{{ i }}
{{ j }}
{% endfor %}
or something similar?
If both lists are of the same length, you can return zipped_data = zip(table, total) as template context in your view, which produces a list of 2-valued tuples.
Example:
>>> lst1 = ['a', 'b', 'c']
>>> lst2 = [1, 2, 3]
>>> zip(lst1, lst2)
[('a', 1), ('b', 2), ('c', 3)]
In your template, you can then write:
{% for i, j in zipped_data %}
{{ i }}, {{ j }}
{% endfor %}
Also, take a look at Django's documentation about the for template tag here. It mentions all possibilities that you have for using it including nice examples.
For any recent visitors to this question, forloop.parentloop can mimic the zipping of two lists together:
{% for a in list_a %}{% for b in list_b %}
{% if forloop.counter == forloop.parentloop.counter %}
{{a}} {{b}}
{% endif %}
{% endfor %}{% endfor %}
Use python's zip function and zip the 2 lists together.
In your view:
zip(table, list)
In your template, you can iterate this like a simple list, and use the .0 and .1 properties to access the data from table and list, respectively.
If it's just the variables i and j that you're looking at then this should work -
return render_to_response('results.html',
{'data': zip(table, list)})
{% for i, j in data %}
<tr>
<td> {{ i }}: </td> <td> {{ j }} </td>
</tr>
{% endfor %}
(credit to everyone else who answered this question)
Rather than using a dictionary (which does not guarantee any kind of sorting), use the python zip function on the two lists and pass it to the template.
You'll have to do this in the view - use the builtin zip function to make a list of tuples, then iterate over it in the template.
Template logic is purposely simple, anything even slightly complex should be done in the view.

For loop in jinja using exported variable

I am trying to generate some code in a for loop and the limit is set dynamically from an exported variable. This is what I was trying
In test.mk
export num_iterations = 3
In test.j2
{%- for i in range(0, {{ num_iterations | int }}) %}
Do something with {{i}}
{%- endfor %}
Error I get
jinja2.exceptions.TemplateSyntaxError: expected token ':', got '}'
If I use range(0, 3), the code generation works fine.
Thanks
Try removing the inner {{ }} so your for looks like this:
{%- for i in range(0, num_iterations | int ) %}

Ansible jinja2 loops over complex structures

I have this complex variable:
vars:
- data_sources:
- source1:
- attrA: foo1
- attrB: bar1
- source2:
- attrA: foo2
- attrB: bar2
and I would like to loop over the structured variable with jinja2 to
generate some xml:
{% for d in {{data_sources}} %}
...
{% endfor %}
but I am getting error: expected token ':', got '}'", 'failed': True} for the very first line of the for loop. Any idea why and a solution? Thanks
UPADTE I made some corrections to the original example.
You must not use curly braces in jinja directives but only in the body:
{% for d in data_sources %}
{{ d['attrA'] }}
{% endfor %}
Also your data_sources is not what it meant to be:
vars:
data_sources:
- source1:
attrA: foo1
attrB: bar1
- source2:
attrA: foo2
attrB: bar2

Get substring from string in Liquid?

I am working with Jekyll and I have the string balh blah blah&garbage **&*&% garbage <h1>TITLE</h1> &^*$%"
Is there a way to grab TITLE? I have looked at the functions here but I don't see something that I can use.
split to the rescue !
{% assign str = 'garbage <h1>TITLE</h1> moregarbage' %}
{% assign a = str | split: '<h1>' %}
We now have garbage in a[0] and TITLE</h1> moregarbage in a[1]
{% assign b = a[1] | split: '</h1>' %}
We now have TITLE in b[0] and moregarbage in b[1]
I know this is ancient, but for anyone else coming across it: https://shopify.github.io/liquid/basics/operators/
contains contains checks for the presence of a substring inside a
string.
{% if product.title contains "Pack" %} This product's title contains
the word Pack. {% endif %}
contains can also check for the presence of a string in an array of
strings.
{% if product.tags contains "Hello" %} This product has been tagged
with "Hello". {% endif %}
contains can only search strings. You cannot use it to check for an
object in an array of objects.

Jekyll print custom JSON string from data

Let's say I have an array of hashes like so;
site.foo = [
{
"foo": "bar",
"baz": ["a", "b", "c"]
},
{
"foo": "baz",
"baz": ["x", "y"]
},
...
]
I want to get all values (bar, a, b, c, baz, x, y) in an array and output it as JSON in a javascript variable in a template.
How would I do this?
I assume that you know how your datas are organized.
{% assign splitMark = "###" %}
{% assign arr = "" %}
{% for el in site.foo %}
{% assign arrSize = arr | size %}
<!-- if we already have something in our string
add the splitMark at the end -->
{% if arrSize > 0 %}
{% capture arr %}{{arr}}{{splitMark}}{%endcapture%}
{% endif %}
<!-- array + foo.foo value + splitMark -->
{% capture arr %}{{arr}}{{el.foo}}{{splitMark}}{%endcapture%}
<!-- transform an array to a string
with elements delimited by splitMark -->
{% assign bazStr = el.baz | join: splitMark %}
<!-- array + transformed array -->
{% capture arr %}{{arr}}{{bazStr}}{%endcapture%}
{% endfor %}
<!-- transform our string to an array splitting at the splitMark -->
{% assign arr = arr | split: splitMark %}
<script>
var my_var = {{ arr | jsonify }};
</script>
If you don't know your exact data structure, eg:
-
foo: "bar"
baz: ["a", "b", "c", 10]
-
foo: baz
baz: ["x", "y"]
any: other
key: ['toto','titi']
You have to go the plugin way. Create a file _plugins/flatten.rb containing :
module Jekyll
module FlattenArray
def flatten( datas )
datas.flat_map(&:values).flatten
end
end
end
Liquid::Template.register_filter(Jekyll::FlattenArray)
You can now flatten your datas like this :
var my_var = {{ site.foo | flatten | jsonify }};