luoma 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +2 -0
- data/.rdoc_options +16 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +68 -0
- data/Rakefile +69 -0
- data/Steepfile +41 -0
- data/certs/jgrp.pem +27 -0
- data/docs/configuration.md +180 -0
- data/docs/custom_filters.md +3 -0
- data/docs/custom_tags.md +3 -0
- data/docs/expressions.md +3 -0
- data/docs/extension_types.md +41 -0
- data/docs/filter_reference.md +1702 -0
- data/docs/index.md +73 -0
- data/docs/luoma_for_template_authors.md +3 -0
- data/docs/markdown.md +111 -0
- data/docs/predicate_reference.md +3 -0
- data/docs/static_analysis.md +3 -0
- data/docs/tag_reference.md +352 -0
- data/docs/template_loaders.md +177 -0
- data/docs/undefined_variables.md +3 -0
- data/docs-requirements.txt +11 -0
- data/docs_/cycle.md +17 -0
- data/docs_/font_rendering_example.md +157 -0
- data/docs_/header_block_example.md +51 -0
- data/docs_/increment_and_decrement.md +49 -0
- data/docs_/logo_style_example.md +40 -0
- data/docs_/migration.md +11 -0
- data/docs_/notes.md +19 -0
- data/lib/luoma/cache.rb +80 -0
- data/lib/luoma/chain_hash.rb +54 -0
- data/lib/luoma/context.rb +227 -0
- data/lib/luoma/drop.rb +84 -0
- data/lib/luoma/drops/block.rb +33 -0
- data/lib/luoma/drops/expression.rb +29 -0
- data/lib/luoma/drops/html_safe.rb +36 -0
- data/lib/luoma/drops/range.rb +69 -0
- data/lib/luoma/drops/undefined.rb +119 -0
- data/lib/luoma/environment.rb +479 -0
- data/lib/luoma/errors.rb +79 -0
- data/lib/luoma/escape.rb +35 -0
- data/lib/luoma/expression.rb +1271 -0
- data/lib/luoma/filter.rb +97 -0
- data/lib/luoma/filters/array.rb +372 -0
- data/lib/luoma/filters/date.rb +19 -0
- data/lib/luoma/filters/default.rb +16 -0
- data/lib/luoma/filters/json.rb +14 -0
- data/lib/luoma/filters/math.rb +84 -0
- data/lib/luoma/filters/size.rb +13 -0
- data/lib/luoma/filters/slice.rb +52 -0
- data/lib/luoma/filters/sort.rb +113 -0
- data/lib/luoma/filters/string.rb +186 -0
- data/lib/luoma/fnv.rb +15 -0
- data/lib/luoma/lexer.rb +106 -0
- data/lib/luoma/lexer_legacy.rb +396 -0
- data/lib/luoma/lexer_unified.rb +279 -0
- data/lib/luoma/loader.rb +50 -0
- data/lib/luoma/loaders/choice_loader.rb +20 -0
- data/lib/luoma/loaders/file_system_loader.rb +75 -0
- data/lib/luoma/loaders/mixins.rb +52 -0
- data/lib/luoma/markup.rb +109 -0
- data/lib/luoma/parser.rb +252 -0
- data/lib/luoma/parser_unified.rb +979 -0
- data/lib/luoma/predicates/blank.rb +19 -0
- data/lib/luoma/predicates/defined.rb +10 -0
- data/lib/luoma/predicates/empty.rb +15 -0
- data/lib/luoma/predicates/type.rb +35 -0
- data/lib/luoma/static_analysis.rb +427 -0
- data/lib/luoma/tags/assign.rb +63 -0
- data/lib/luoma/tags/break.rb +23 -0
- data/lib/luoma/tags/capture.rb +43 -0
- data/lib/luoma/tags/case.rb +137 -0
- data/lib/luoma/tags/comment.rb +17 -0
- data/lib/luoma/tags/continue.rb +23 -0
- data/lib/luoma/tags/define.rb +42 -0
- data/lib/luoma/tags/else.rb +30 -0
- data/lib/luoma/tags/for.rb +108 -0
- data/lib/luoma/tags/if.rb +109 -0
- data/lib/luoma/tags/import.rb +91 -0
- data/lib/luoma/tags/include.rb +77 -0
- data/lib/luoma/tags/output.rb +20 -0
- data/lib/luoma/tags/raw.rb +26 -0
- data/lib/luoma/tags/render.rb +92 -0
- data/lib/luoma/tags/with.rb +55 -0
- data/lib/luoma/template.rb +178 -0
- data/lib/luoma/token.rb +84 -0
- data/lib/luoma/version.rb +5 -0
- data/lib/luoma.rb +75 -0
- data/sig/luoma/cache.rbs +44 -0
- data/sig/luoma/chain_hash.rbs +26 -0
- data/sig/luoma/context.rbs +115 -0
- data/sig/luoma/drop.rbs +44 -0
- data/sig/luoma/drops/block.rbs +10 -0
- data/sig/luoma/drops/expression.rbs +10 -0
- data/sig/luoma/drops/html_safe.rbs +14 -0
- data/sig/luoma/drops/range.rbs +15 -0
- data/sig/luoma/drops/undefined.rbs +27 -0
- data/sig/luoma/environment.rbs +212 -0
- data/sig/luoma/errors.rbs +67 -0
- data/sig/luoma/escape.rbs +15 -0
- data/sig/luoma/expression.rbs +514 -0
- data/sig/luoma/filter.rbs +66 -0
- data/sig/luoma/filters/array.rbs +74 -0
- data/sig/luoma/filters/date.rbs +9 -0
- data/sig/luoma/filters/default.rbs +8 -0
- data/sig/luoma/filters/json.rbs +7 -0
- data/sig/luoma/filters/math.rbs +37 -0
- data/sig/luoma/filters/size.rbs +6 -0
- data/sig/luoma/filters/slice.rbs +10 -0
- data/sig/luoma/filters/sort.rbs +17 -0
- data/sig/luoma/filters/string.rbs +103 -0
- data/sig/luoma/fnv.rbs +3 -0
- data/sig/luoma/lexer.rbs +42 -0
- data/sig/luoma/lexer_legacy.rbs +81 -0
- data/sig/luoma/lexer_unified.rbs +49 -0
- data/sig/luoma/loader.rbs +40 -0
- data/sig/luoma/loaders/choice_loader.rbs +9 -0
- data/sig/luoma/loaders/file_system_loader.rbs +19 -0
- data/sig/luoma/loaders/mixins.rbs +16 -0
- data/sig/luoma/markup.rbs +68 -0
- data/sig/luoma/parser.rbs +105 -0
- data/sig/luoma/parser_unified.rbs +150 -0
- data/sig/luoma/predicates/blank.rbs +6 -0
- data/sig/luoma/predicates/defined.rbs +6 -0
- data/sig/luoma/predicates/empty.rbs +6 -0
- data/sig/luoma/predicates/type.rbs +21 -0
- data/sig/luoma/static_analysis.rbs +141 -0
- data/sig/luoma/tags/assign.rbs +12 -0
- data/sig/luoma/tags/break.rbs +11 -0
- data/sig/luoma/tags/capture.rbs +14 -0
- data/sig/luoma/tags/case.rbs +36 -0
- data/sig/luoma/tags/comment.rbs +11 -0
- data/sig/luoma/tags/continue.rbs +11 -0
- data/sig/luoma/tags/define.rbs +16 -0
- data/sig/luoma/tags/else.rbs +17 -0
- data/sig/luoma/tags/for.rbs +26 -0
- data/sig/luoma/tags/if.rbs +45 -0
- data/sig/luoma/tags/import.rbs +13 -0
- data/sig/luoma/tags/include.rbs +13 -0
- data/sig/luoma/tags/output.rbs +13 -0
- data/sig/luoma/tags/raw.rbs +11 -0
- data/sig/luoma/tags/render.rbs +13 -0
- data/sig/luoma/tags/with.rbs +15 -0
- data/sig/luoma/template.rbs +117 -0
- data/sig/luoma/token.rbs +81 -0
- data/sig/luoma.rbs +11 -0
- data/zensical.toml +363 -0
- data.tar.gz.sig +0 -0
- metadata +233 -0
- metadata.gz.sig +0 -0
data/docs/index.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Luoma - Ruby
|
|
2
|
+
|
|
3
|
+
Luoma is a modern template engine with a well-defined, composable, implementation agnostic expression language.
|
|
4
|
+
|
|
5
|
+
Luoma markup will be familiar to anyone who's used [Liquid](https://github.com/Shopify/liquid), [Jinja](https://jinja.palletsprojects.com/en/stable/) or [Django's template language](https://docs.djangoproject.com/en/6.0/topics/templates/#the-django-template-language), but with a strictly immutable data model, first-class blocks and expressions, and functional primitives for when data transformation is necessary.
|
|
6
|
+
|
|
7
|
+
If you're a template author, start with [Luoma for template authors](./luoma_for_template_authors.md). The rest of this documentation covers how to install, configure, use and extend Luoma if you're an application developer.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
Add `'luoma'` to your Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'luoma', '~> 0.1.0'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
gem install luoma
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
bundle add luoma
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
### Render
|
|
32
|
+
|
|
33
|
+
Render a template by passing a string to `Luoma.render(source, data = nil)`. If _data_ is given it should be a hash mapping strings to objects. Hash values will be available as template variables bound to their associated keys.
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
require "luoma"
|
|
37
|
+
|
|
38
|
+
puts luoma.render("Hello, {{ you }}!", "you" => "World") # Hello, World!
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`Luoma.render` is a convenience method equivalent to `Luoma::DEFAULT_ENVIRONMENT.parse(source).render(data)`.
|
|
42
|
+
|
|
43
|
+
### Parse
|
|
44
|
+
|
|
45
|
+
Often you'll want to render the same template multiple times with different variables. We can parse source text without immediately rendering it using `Luoma.parse(source, globals: nil)`. `Luoma.parse` returns an instance of `Luoma::Template` with a `render(data)`method.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
require "luoma"
|
|
49
|
+
|
|
50
|
+
template = Luoma.parse("Hello, {{ you }}!")
|
|
51
|
+
puts template.render("you" => "World") # Hello, World!
|
|
52
|
+
puts template.render("you" => "Luoma") # Hello, Luoma!
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
If _globals_ is given, data from _globals_ is pined to the resulting template and merged into data from `Luoma::Template#render` every time the template is rendered, with `render` arguments taking priority over pinned data.
|
|
56
|
+
|
|
57
|
+
`Luoma.parse` is a convenience method equivalent to `Luoma::DEFAULT_ENVIRONMENT.parse(source)` or `Luoma::Environment.new.parse(source)`.
|
|
58
|
+
|
|
59
|
+
### Configure
|
|
60
|
+
|
|
61
|
+
Both `Luoma.parse` and `Luoma.render` are convenience functions that use the default Luoma environment. For all but the simplest of cases you'll want to configure your own instance of `Luoma::Environment`, then load and render templates from that.
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
require "luoma"
|
|
65
|
+
|
|
66
|
+
env = Luoma::Environment.new(
|
|
67
|
+
loader: Luoma::CachingFileSystemLoader.new("templates/")
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
template = env.get_template("index.luoma")
|
|
71
|
+
another_template = env.parse("{% render 'index.luoma' %}")
|
|
72
|
+
# ...
|
|
73
|
+
```
|
data/docs/markdown.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: simple/markdown
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Markdown in 5min
|
|
6
|
+
|
|
7
|
+
## Headers
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
# H1 Header
|
|
11
|
+
## H2 Header
|
|
12
|
+
### H3 Header
|
|
13
|
+
#### H4 Header
|
|
14
|
+
##### H5 Header
|
|
15
|
+
###### H6 Header
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Text formatting
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
**bold text**
|
|
22
|
+
*italic text*
|
|
23
|
+
***bold and italic***
|
|
24
|
+
~~strikethrough~~
|
|
25
|
+
`inline code`
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Links and images
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
[Link text](https://example.com)
|
|
32
|
+
[Link with title](https://example.com "Hover title")
|
|
33
|
+

|
|
34
|
+

|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Lists
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
Unordered:
|
|
41
|
+
|
|
42
|
+
- Item 1
|
|
43
|
+
- Item 2
|
|
44
|
+
- Nested item
|
|
45
|
+
|
|
46
|
+
Ordered:
|
|
47
|
+
|
|
48
|
+
1. First item
|
|
49
|
+
2. Second item
|
|
50
|
+
3. Third item
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Blockquotes
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
> This is a blockquote
|
|
57
|
+
> Multiple lines
|
|
58
|
+
>> Nested quote
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Code blocks
|
|
62
|
+
|
|
63
|
+
````
|
|
64
|
+
```javascript
|
|
65
|
+
function hello() {
|
|
66
|
+
console.log("Hello, world!");
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
````
|
|
70
|
+
|
|
71
|
+
## Tables
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
| Header 1 | Header 2 | Header 3 |
|
|
75
|
+
|----------|----------|----------|
|
|
76
|
+
| Row 1 | Data | Data |
|
|
77
|
+
| Row 2 | Data | Data |
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Horizontal rule
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
---
|
|
84
|
+
or
|
|
85
|
+
***
|
|
86
|
+
or
|
|
87
|
+
___
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Task lists
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
- [x] Completed task
|
|
94
|
+
- [ ] Incomplete task
|
|
95
|
+
- [ ] Another task
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Escaping characters
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
Use backslash to escape: \* \_ \# \`
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Line breaks
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
End a line with two spaces
|
|
108
|
+
to create a line break.
|
|
109
|
+
|
|
110
|
+
Or use a blank line for a new paragraph.
|
|
111
|
+
```
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# Built-in tags
|
|
2
|
+
|
|
3
|
+
## Comments
|
|
4
|
+
|
|
5
|
+
```title="Syntax"
|
|
6
|
+
{# <comment text> #}
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Comments can be used to add documentation to your templates or "comment out" chunks of markup so that it wont be rendered.
|
|
10
|
+
|
|
11
|
+
```liquid2
|
|
12
|
+
{# This is a comment #}
|
|
13
|
+
{#
|
|
14
|
+
Comments can
|
|
15
|
+
span
|
|
16
|
+
multiple lines
|
|
17
|
+
#}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
You can safely comment out markup containing comments by adding hashes, as long as the number of hashes is balanced.
|
|
21
|
+
|
|
22
|
+
```liquid2
|
|
23
|
+
{##
|
|
24
|
+
{# existing comment #}
|
|
25
|
+
{{ something }}
|
|
26
|
+
##}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
By convention, comments that start with a single hash on every line are considered doc comments to be parsed by a documentation generator.
|
|
30
|
+
|
|
31
|
+
```liquid2
|
|
32
|
+
{##
|
|
33
|
+
# @param font
|
|
34
|
+
# @returns CSS
|
|
35
|
+
##}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Output
|
|
39
|
+
|
|
40
|
+
```title="Syntax"
|
|
41
|
+
{{ <expression> }}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
An expression surrounded by double curly braces (`{{` and `}}`) is an _output statement_. When rendered, the expression will be evaluated and the result written to the output stream.
|
|
45
|
+
|
|
46
|
+
In this example the expression is the variable `you` - which will be resolved to a value and the value's string representation will be written to the output stream - but output statements can contain any expression.
|
|
47
|
+
|
|
48
|
+
```liquid2
|
|
49
|
+
Hello, {{ you }}!
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## assign
|
|
53
|
+
|
|
54
|
+
```title="Syntax"
|
|
55
|
+
{% assign <identifier> = <expression> [, <identifier> = <expression> ...] %}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The `assign` tag binds the result of evaluating an expression to a name, creating or updating a variable.
|
|
59
|
+
|
|
60
|
+
```liquid2
|
|
61
|
+
{% assign foo = "bar" %}
|
|
62
|
+
{% assign foo = 42 %}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
A single `assign` tag can define multiple variables, separated by commas. Earlier bindings are immediately available in later expressions.
|
|
66
|
+
|
|
67
|
+
```liquid2
|
|
68
|
+
{% assign
|
|
69
|
+
a = 1,
|
|
70
|
+
b = a + 1
|
|
71
|
+
%}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## capture
|
|
75
|
+
|
|
76
|
+
```title="Syntax"
|
|
77
|
+
{% capture <identifier> %} <markup> {% endcapture %}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The `capture` tag renders a block of markup to a string variable instead of writing it directly to the output stream.
|
|
81
|
+
|
|
82
|
+
```liquid2
|
|
83
|
+
{% assign first_name = "Sam" %}
|
|
84
|
+
{% assign points = 120 %}
|
|
85
|
+
|
|
86
|
+
{% capture message %}
|
|
87
|
+
Hello {{ first_name }}!
|
|
88
|
+
You have {{ points }} reward points.
|
|
89
|
+
{% endcapture %}
|
|
90
|
+
|
|
91
|
+
<p>{{ message }}</p>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```title="Output"
|
|
95
|
+
<p>Hello Sam!
|
|
96
|
+
You have 120 reward points.</p>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
See also the [`define`](#define) tag.
|
|
100
|
+
|
|
101
|
+
## case
|
|
102
|
+
|
|
103
|
+
```title="Syntax"
|
|
104
|
+
{% case <expression> %}
|
|
105
|
+
[ {% when <expression | predicate> [, <expression | predicate>] %} <markup> ] ...
|
|
106
|
+
[ {% else %} <markup> ]
|
|
107
|
+
{% endcase %}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The `case` tag provides a multi-way conditional. It compares the result of a single expression against the result of one or more candidate expressions, and renders the block following the first matching `{% when %}` tag. If no branch matches, the optional `{% else %}` branch is rendered.
|
|
111
|
+
|
|
112
|
+
```liquid2
|
|
113
|
+
{% assign day = "Monday" %}
|
|
114
|
+
|
|
115
|
+
{% case day %}
|
|
116
|
+
{% when "Monday" %}
|
|
117
|
+
Start of the work week!
|
|
118
|
+
{% when "Friday" %}
|
|
119
|
+
It's almost the weekend!
|
|
120
|
+
{% when "Saturday" or "Sunday" %}
|
|
121
|
+
Enjoy your weekend!
|
|
122
|
+
{% else %}
|
|
123
|
+
Just another weekday.
|
|
124
|
+
{% endcase %}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`when` tags also understand _bare predicates_, like `.defined?` and `.empty?` in place of a standard expression.
|
|
128
|
+
|
|
129
|
+
```liquid2
|
|
130
|
+
{% case items %}
|
|
131
|
+
{% when empty? %}
|
|
132
|
+
No items.
|
|
133
|
+
{% else %}
|
|
134
|
+
{{ items | size }} items.
|
|
135
|
+
{% endcase %}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## define
|
|
139
|
+
|
|
140
|
+
```title="Syntax"
|
|
141
|
+
{% define <identifier> [ visibility: ("public" | "private") ] %} <markup> {% enddefine %}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The `define` tag captures a block of markup for later rendering. `define` captures nothing about the scope in which it rendered. Defined blocks are rendered in the scope in which they are output or coerced to a string.
|
|
145
|
+
|
|
146
|
+
```liquid2
|
|
147
|
+
{% assign name = "Alice" %}
|
|
148
|
+
|
|
149
|
+
{% define greeting %}
|
|
150
|
+
Hello {{ name }}!
|
|
151
|
+
{% enddefine %}
|
|
152
|
+
|
|
153
|
+
{% assign name = "Bob" %}
|
|
154
|
+
|
|
155
|
+
{{ greeting }}
|
|
156
|
+
{{ greeting | upcase }}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```title="Output"
|
|
160
|
+
Hello Bob!
|
|
161
|
+
HELLO BOB!
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`define` can be though of as a deferred [`capture`](#capture). See also [`import`](#import).
|
|
165
|
+
|
|
166
|
+
## for
|
|
167
|
+
|
|
168
|
+
```title="Syntax"
|
|
169
|
+
{% for <identifier> [, identifier [, identifier]] in <expression> %}
|
|
170
|
+
<markup>
|
|
171
|
+
[ {% else %} <markup> ]
|
|
172
|
+
{% endfor %}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The `for` tag renders its block once for each item in an iterable object. If the iterable is empty and an `else` block given, it will be rendered instead.
|
|
176
|
+
|
|
177
|
+
```liquid2
|
|
178
|
+
{% for product in collection %}
|
|
179
|
+
- {{ product.title }}
|
|
180
|
+
{% else %}
|
|
181
|
+
No products available
|
|
182
|
+
{% endfor %}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Optionally, the current zero-based iteration index can be bound to a name.
|
|
186
|
+
|
|
187
|
+
```liquid2
|
|
188
|
+
{% for product, index in collection %}
|
|
189
|
+
- {{ product.title }} at index {{ index }}
|
|
190
|
+
{% endfor %}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
And the optional third identifier binds the array being iterated to a name.
|
|
194
|
+
|
|
195
|
+
```liquid2
|
|
196
|
+
{% for product, index, array in collection %}
|
|
197
|
+
{%- assign last = index == (array | size) - 1 -%}
|
|
198
|
+
...
|
|
199
|
+
{% endfor %}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### break
|
|
203
|
+
|
|
204
|
+
You can exit a loop early using the `break` tag.
|
|
205
|
+
|
|
206
|
+
```liquid2
|
|
207
|
+
{% for product in collection.products %}
|
|
208
|
+
{% if product.title == "Shirt" %}
|
|
209
|
+
{% break %}
|
|
210
|
+
{% endif %}
|
|
211
|
+
|
|
212
|
+
- {{ product.title }}
|
|
213
|
+
{% endfor %}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### continue
|
|
217
|
+
|
|
218
|
+
You can skip all or part of a loop iteration with the `continue` tag.
|
|
219
|
+
|
|
220
|
+
```liquid2
|
|
221
|
+
{% for product in collection.products %}
|
|
222
|
+
{% if product.title == "Shirt" %}
|
|
223
|
+
{% continue %}
|
|
224
|
+
{% endif %}
|
|
225
|
+
|
|
226
|
+
- {{ product.title }}
|
|
227
|
+
{% endfor %}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## if
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
{% if <expression> %}
|
|
234
|
+
<markup>
|
|
235
|
+
[ {% elsif <expression> %} <markup> [ {% elsif <expression> %} ... ]]
|
|
236
|
+
[ {% else %} <markup> ... ]
|
|
237
|
+
{% endif %}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The `if` tag conditionally renders its block if its expression evaluates to be truthy. Any number of `elsif` blocks can be given to add alternative conditions, and an `else` block is used as a default if no preceding conditions were truthy.
|
|
241
|
+
|
|
242
|
+
```liquid2
|
|
243
|
+
{% if product.title == "OK Hat" %}
|
|
244
|
+
This hat is OK.
|
|
245
|
+
{% elsif product.title == "Rubbish Tie" %}
|
|
246
|
+
This tie is rubbish.
|
|
247
|
+
{% else %}
|
|
248
|
+
Not sure what this is.
|
|
249
|
+
{% endif %}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## import
|
|
253
|
+
|
|
254
|
+
```title="Syntax"
|
|
255
|
+
{% import <string> [ as <identifier>] %}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The `import` tag loads and renders a named template for its side effects, discarding its output. `import` is used to bring variables (including blocks defined with the [`define` tag](#define)) into scope.
|
|
259
|
+
|
|
260
|
+
```liquid2
|
|
261
|
+
{% import button_utils as buttons %}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## include
|
|
265
|
+
|
|
266
|
+
```title="Syntax"
|
|
267
|
+
{% include <template name>
|
|
268
|
+
[[,] <identifier>: <expression> [, [<identifier>: <expression> ... ]]]
|
|
269
|
+
%}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The `include` tag loads and renders a named template, inserting the resulting text in its place. The name of the template to include can be a string literal or a variable resolving to a string. When rendered, the included template will share the same scope as the "calling" template.
|
|
273
|
+
|
|
274
|
+
```liquid2
|
|
275
|
+
{% include "snippets/header.html" %}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Keyword arguments
|
|
279
|
+
|
|
280
|
+
Additional keyword arguments given to the `include` tag will be added to the included template's scope, then go out of scope after the included template has been rendered.
|
|
281
|
+
|
|
282
|
+
```liquid2
|
|
283
|
+
{% include "partial_template" greeting: "Hello", num: 3, skip: 2 %}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## raw
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
{% raw %} <text> {% endraw %}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Any text between `{% raw %}` and `{% endraw %}` will not be interpreted as markup, but output as plain text instead.
|
|
293
|
+
|
|
294
|
+
```liquid2
|
|
295
|
+
{% raw %}
|
|
296
|
+
This will be rendered {{verbatim}}, with the curly brackets.
|
|
297
|
+
{% endraw %}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## render
|
|
301
|
+
|
|
302
|
+
```title="Syntax"
|
|
303
|
+
{% render <string>
|
|
304
|
+
[[,] <identifier>: <expression> [, [<identifier>: <expression> ... ]]]
|
|
305
|
+
%}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The `render` tag loads and renders a named template, inserting the resulting text in its place. The name of the template to include **must** be a string literal. When rendered, the included template will have its onw scope, without variables define in the calling template.
|
|
309
|
+
|
|
310
|
+
```liquid2
|
|
311
|
+
{% render "snippets/header.html" %}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Keyword arguments
|
|
315
|
+
|
|
316
|
+
Additional keyword arguments given to the `render` tag will be added to the rendered template's scope, then go out of scope after the it has been rendered.
|
|
317
|
+
|
|
318
|
+
```liquid2
|
|
319
|
+
{% render "partial_template" greeting: "Hello", num: 3, skip: 2 %}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## with
|
|
323
|
+
|
|
324
|
+
```title="Syntax"
|
|
325
|
+
{% with <identifier> = <expression> [, <identifier> = <expression> ... ] %}
|
|
326
|
+
<markup>
|
|
327
|
+
{% endwith %}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The `with` tag introduces block-scoped variables. These variables have the potential to shadow global variables or variables assigned with `{% assign %}`, `{% capture %}` and `{% define %}`.
|
|
331
|
+
|
|
332
|
+
```liquid2
|
|
333
|
+
{% with p = collection.products.first %}
|
|
334
|
+
{{ p.title }}
|
|
335
|
+
{% endwith %}
|
|
336
|
+
|
|
337
|
+
{% with a = 1, b = 3.4 %}
|
|
338
|
+
{{ a }} + {{ b }} = {{ a + b }}
|
|
339
|
+
{% endwith %}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
When multiple bindings are given, earlier variables are in scope for later expressions.
|
|
343
|
+
|
|
344
|
+
```liquid2
|
|
345
|
+
{% with
|
|
346
|
+
a = 1,
|
|
347
|
+
b = 3.4,
|
|
348
|
+
c = a + b,
|
|
349
|
+
%}
|
|
350
|
+
{{ a }} + {{ b }} = {{ c }}
|
|
351
|
+
{% endwith %}
|
|
352
|
+
```
|