2017-07-16 23:43:04 +02:00
# Liquid Template Parser
2017-07-02 14:47:10 +02:00
2021-06-26 08:43:59 +02:00
[![go badge][go-svg]][go-url]
[![Golangci-lint badge][golangci-lint-svg]][golangci-lint-url]
2021-06-26 08:54:25 +02:00
[![Go Report Card badge][go-report-card-svg]][go-report-card-url]
[![Go Doc][godoc-svg]][godoc-url]
[![MIT License][license-svg]][license-url]
2017-06-25 17:23:20 +02:00
2021-06-26 08:54:25 +02:00
`liquid` is a pure Go implementation of [Shopify Liquid
templates](https://shopify.github.io/liquid). It was developed for use in the
[Gojekyll ](https://github.com/osteele/gojekyll ) port of the Jekyll static site
generator.
2017-07-02 06:10:54 +02:00
2017-07-02 02:24:27 +02:00
<!-- TOC -->
2017-07-16 23:43:04 +02:00
- [Liquid Template Parser ](#liquid-template-parser )
2018-06-05 22:01:28 +02:00
- [Installation ](#installation )
- [Usage ](#usage )
- [Command-Line tool ](#command-line-tool )
- [Documentation ](#documentation )
- [Status ](#status )
- [Drops ](#drops )
- [Value Types ](#value-types )
2017-07-02 02:24:27 +02:00
- [References ](#references )
2018-06-05 22:01:28 +02:00
- [Contributing ](#contributing )
2018-05-30 22:54:10 +02:00
- [Contributors ](#contributors )
2018-06-05 22:01:28 +02:00
- [Attribution ](#attribution )
- [Other Implementations ](#other-implementations )
- [Go ](#go )
- [Other Languages ](#other-languages )
- [License ](#license )
2017-07-02 02:24:27 +02:00
<!-- /TOC -->
2017-06-25 17:23:20 +02:00
2018-06-05 22:01:28 +02:00
## Installation
2017-06-25 17:23:20 +02:00
2017-07-16 17:15:31 +02:00
`go get gopkg.in/osteele/liquid.v1` # latest snapshot
2017-07-10 05:15:05 +02:00
2017-07-21 02:49:35 +02:00
`go get -u github.com/osteele/liquid` # development version
2017-07-10 05:15:05 +02:00
## Usage
2017-06-25 17:23:20 +02:00
2017-07-10 05:15:05 +02:00
```go
2017-07-16 03:18:10 +02:00
engine := liquid.NewEngine()
2017-07-10 05:15:05 +02:00
template := `<h1>{{ page.title }}</h1>`
bindings := map[string]interface{}{
"page": map[string]string{
"title": "Introduction",
},
}
out, err := engine.ParseAndRenderString(template, bindings)
if err != nil { log.Fatalln(err) }
fmt.Println(out)
// Output: < h1 > Introduction< / h1 >
```
2018-06-05 22:01:28 +02:00
See the [API documentation][godoc-url] for additional examples.
2017-07-10 05:15:05 +02:00
### Command-Line tool
2021-06-26 08:54:25 +02:00
`go install gopkg.in/osteele/liquid.v0/cmd/liquid` installs a command-line
`liquid` executable. This is intended to make it easier to create test cases for
bug reports.
2017-07-10 05:15:05 +02:00
```bash
$ liquid --help
usage: liquid [FILE]
$ echo '{{ "Hello World" | downcase | split: " " | first | append: "!"}}' | liquid
hello!
```
2017-07-05 19:46:30 +02:00
2018-06-05 22:01:28 +02:00
## Documentation
### Status
These features of Shopify Liquid aren't implemented:
2021-07-06 04:31:20 +02:00
- Filter keyword parameters, for example `{{ image | img_url: '580x', scale: 2
}}`. [[Issue #42 ](https://github.com/osteele/liquid/issues/42 )]
2018-06-05 22:01:28 +02:00
- Warn and lax [error modes ](https://github.com/shopify/liquid#error-modes ).
- Non-strict filters. An undefined filter is currently an error.
- Strict variables. An undefined variable is not an error.
### Drops
2021-06-26 08:54:25 +02:00
Drops have a different design from the Shopify (Ruby) implementation. A Ruby
drop sets `liquid_attributes` to a list of attributes that are exposed to
Liquid. A Go drop implements `ToLiquid() interface{}` , that returns a proxy
object. Conventionally, the proxy is a `map` or `struct` that defines the
exposed properties. See < http: / / godoc . org / github . com / osteele / liquid # Drop > for
additional information.
2018-06-05 22:01:28 +02:00
### Value Types
2017-07-21 18:06:39 +02:00
2021-06-26 08:54:25 +02:00
`Render` and friends take a `Bindings` parameter. This is a map of `string` to
`interface{}` , that associates template variable names with Go values.
2017-07-21 18:06:39 +02:00
Any Go value can be used as a variable value. These values have special meaning:
- `false` and `nil`
2021-06-26 08:54:25 +02:00
- These, and no other values, are recognized as false by `and` , `or` , `{% if
%}`, `{% elsif %}` , and `{% case %}` .
2017-07-21 18:06:39 +02:00
- Integers
2021-06-26 08:54:25 +02:00
- (Only) integers can be used as array indices: `array[1]` ; `array[n]` , where
`array` has an array value and `n` has an integer value.
- (Only) integers can be used as the endpoints of a range: `{% for item in
(1..5) %}`, `{% for item in (start..end) %}` where `start` and `end` have
integer values.
2017-07-21 18:06:39 +02:00
- Integers and floats
2021-06-26 08:54:25 +02:00
- Integers and floats are converted to their join type for comparison: `1 ==
1.0` evaluates to `true` . Similarly, `int8(1)` , `int16(1)` , `uint8(1)` etc.
are all `==` .
2017-07-21 18:06:39 +02:00
- [There is currently no special treatment of complex numbers.]
- Integers, floats, and strings
2021-06-26 08:54:25 +02:00
- Integers, floats, and strings can be used in comparisons `<` , `>` , `<=` ,
`>=` . Integers and floats can be usefully compared with each other. Strings
can be usefully compared with each other, but not with other values. Any
other comparison, e.g. `1 < "one"` , `1 > "one"` , is always false.
2017-07-21 18:06:39 +02:00
- Arrays (and slices)
2021-06-26 08:54:25 +02:00
- An array can be indexed by integer value: `array[1]` ; `array[n]` where `n`
has an integer value.
- Arrays have `first` , `last` , and `size` properties: `array.first ==
array[0]`, `array[array.size-1] == array.last` (where `array.size > 0` )
2017-07-21 18:06:39 +02:00
- Maps
2021-06-26 08:54:25 +02:00
- A map can be indexed by a string: `hash["key"]` ; `hash[s]` where `s` has a
string value
2017-07-21 18:06:39 +02:00
- A map can be accessed using property syntax `hash.key`
- Maps have a special `size` property, that returns the size of the map.
- Drops
2021-06-26 08:54:25 +02:00
- A value `value` of a type that implements the `Drop` interface acts as the
value `value.ToLiquid()` . There is no guarantee about how many times
`ToLiquid` will be called. [This is in contrast to Shopify Liquid, which
both uses a different interface for drops, and makes stronger guarantees.]
2017-07-21 18:06:39 +02:00
- Structs
2018-05-30 22:54:10 +02:00
- A public field of a struct can be accessed by its name: `value.FieldName` , `value["fieldName"]` .
2017-07-23 17:49:09 +02:00
- A field tagged e.g. `liquid:”name”` is accessed as `value.name` instead.
2021-06-26 08:54:25 +02:00
- If the value of the field is a function that takes no arguments and
returns either one or two arguments, accessing it invokes the function,
and the value of the property is its first return value.
2017-07-21 18:06:39 +02:00
- If the second return value is non-nil, accessing the field panics instead.
2021-06-26 08:54:25 +02:00
- A function defined on a struct can be accessed by function name e.g.
`value.Func` , `value["Func"]` .
2017-07-21 18:06:39 +02:00
- The same rules apply as to accessing a func-valued public field.
2021-06-26 08:54:25 +02:00
- Note that despite being array- and map-like, structs do not have a special
`value.size` property.
2017-07-23 17:49:09 +02:00
- `[]byte`
2021-06-26 08:54:25 +02:00
- A value of type `[]byte` is rendered as the corresponding string, and
presented as a string to filters that expect one. A `[]byte` is not
(currently) equivalent to a `string` for all uses; for example, `a < b` , `a
contains b`, `hash[b]` will not behave as expected where `a` or `b` is a
`[]byte` .
2018-06-05 22:01:28 +02:00
- `MapSlice`
2021-06-26 08:54:25 +02:00
- An instance of `yaml.MapSlice` acts as a map. It implements `m.key` ,
`m[key]` , and `m.size` .
2017-07-21 18:06:39 +02:00
2018-06-05 22:01:28 +02:00
### References
2021-06-26 08:54:25 +02:00
- [Shopify.github.io/liquid ](https://shopify.github.io/liquid )
- [Liquid for Designers ](https://github.com/Shopify/liquid/wiki/Liquid-for-Designers )
- [Liquid for Programmers ](https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers )
2021-07-06 04:31:20 +02:00
- [Help.shopify.com ](https://help.shopify.com/themes/liquid )
2018-06-05 22:01:28 +02:00
2017-07-05 15:51:00 +02:00
## Contributing
2017-06-25 18:36:28 +02:00
2017-07-05 15:51:00 +02:00
Bug reports, test cases, and code contributions are more than welcome.
Please refer to the [contribution guidelines ](./CONTRIBUTING.md ).
2017-06-28 00:06:26 +02:00
2018-06-05 22:01:28 +02:00
### Contributors
2017-06-28 00:06:26 +02:00
2018-06-05 22:01:28 +02:00
Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
<!-- ALL - CONTRIBUTORS - LIST:START - Do not remove or modify this section -->
2021-06-27 05:06:14 +02:00
<!-- prettier - ignore - start -->
<!-- markdownlint - disable -->
< table >
< tr >
< td align = "center" > < a href = "https://osteele.com/" > < img src = "https://avatars2.githubusercontent.com/u/674?v=4?s=100" width = "100px;" alt = "" / > < br / > < sub > < b > Oliver Steele< / b > < / sub > < / a > < br / > < a href = "https://github.com/osteele/liquid/commits?author=osteele" title = "Code" > 💻< / a > < a href = "https://github.com/osteele/liquid/commits?author=osteele" title = "Documentation" > 📖< / a > < a href = "#ideas-osteele" title = "Ideas, Planning, & Feedback" > 🤔< / a > < a href = "#infra-osteele" title = "Infrastructure (Hosting, Build-Tools, etc)" > 🚇< / a > < a href = "https://github.com/osteele/liquid/pulls?q=is%3Apr+reviewed-by%3Aosteele" title = "Reviewed Pull Requests" > 👀< / a > < a href = "https://github.com/osteele/liquid/commits?author=osteele" title = "Tests" > ⚠️< / a > < / td >
< td align = "center" > < a href = "https://github.com/thessem" > < img src = "https://avatars0.githubusercontent.com/u/973593?v=4?s=100" width = "100px;" alt = "" / > < br / > < sub > < b > James Littlejohn< / b > < / sub > < / a > < br / > < a href = "https://github.com/osteele/liquid/commits?author=thessem" title = "Code" > 💻< / a > < a href = "https://github.com/osteele/liquid/commits?author=thessem" title = "Documentation" > 📖< / a > < a href = "https://github.com/osteele/liquid/commits?author=thessem" title = "Tests" > ⚠️< / a > < / td >
< td align = "center" > < a href = "http://nosmileface.ru" > < img src = "https://avatars2.githubusercontent.com/u/12567?v=4?s=100" width = "100px;" alt = "" / > < br / > < sub > < b > nsf< / b > < / sub > < / a > < br / > < a href = "https://github.com/osteele/liquid/commits?author=nsf" title = "Code" > 💻< / a > < a href = "https://github.com/osteele/liquid/commits?author=nsf" title = "Tests" > ⚠️< / a > < / td >
< td align = "center" > < a href = "https://tobias.salzmann.berlin/" > < img src = "https://avatars.githubusercontent.com/u/796084?v=4?s=100" width = "100px;" alt = "" / > < br / > < sub > < b > Tobias Salzmann< / b > < / sub > < / a > < br / > < a href = "https://github.com/osteele/liquid/commits?author=Eun" title = "Code" > 💻< / a > < / td >
2021-07-05 08:29:24 +02:00
< td align = "center" > < a href = "https://github.com/bendoerr" > < img src = "https://avatars.githubusercontent.com/u/253068?v=4?s=100" width = "100px;" alt = "" / > < br / > < sub > < b > Ben Doerr< / b > < / sub > < / a > < br / > < a href = "https://github.com/osteele/liquid/commits?author=bendoerr" title = "Code" > 💻< / a > < / td >
2022-02-12 16:28:57 +01:00
< td align = "center" > < a href = "https://daniil.it/" > < img src = "https://avatars.githubusercontent.com/u/7339644?v=4?s=100" width = "100px;" alt = "" / > < br / > < sub > < b > Daniil Gentili< / b > < / sub > < / a > < br / > < a href = "https://github.com/osteele/liquid/commits?author=danog" title = "Code" > 💻< / a > < / td >
< td align = "center" > < a href = "https://github.com/carolynvs" > < img src = "https://avatars.githubusercontent.com/u/1368985?v=4?s=100" width = "100px;" alt = "" / > < br / > < sub > < b > Carolyn Van Slyck< / b > < / sub > < / a > < br / > < a href = "https://github.com/osteele/liquid/commits?author=carolynvs" title = "Code" > 💻< / a > < / td >
< / tr >
< tr >
< td align = "center" > < a href = "https://github.com/kke" > < img src = "https://avatars.githubusercontent.com/u/224971?v=4?s=100" width = "100px;" alt = "" / > < br / > < sub > < b > Kimmo Lehto< / b > < / sub > < / a > < br / > < a href = "https://github.com/osteele/liquid/commits?author=kke" title = "Code" > 💻< / a > < / td >
< td align = "center" > < a href = "https://vito.io/" > < img src = "https://avatars.githubusercontent.com/u/77198?v=4?s=100" width = "100px;" alt = "" / > < br / > < sub > < b > Victor "Vito" Gama< / b > < / sub > < / a > < br / > < a href = "https://github.com/osteele/liquid/commits?author=heyvito" title = "Code" > 💻< / a > < / td >
2021-06-27 05:06:14 +02:00
< / tr >
< / table >
<!-- markdownlint - restore -->
<!-- prettier - ignore - end -->
2018-06-05 22:01:28 +02:00
<!-- ALL - CONTRIBUTORS - LIST:END -->
2021-06-26 08:54:25 +02:00
This project follows the
[all-contributors ](https://github.com/kentcdodds/all-contributors )
specification. Contributions of any kind welcome!
2017-06-28 00:06:26 +02:00
2018-06-05 22:01:28 +02:00
### Attribution
2017-06-25 22:21:31 +02:00
2017-07-22 15:14:32 +02:00
| Package | Author | Description | License |
|-----------------------------------------------------|-----------------|----------------------|--------------------|
| [Ragel ](http://www.colm.net/open-source/ragel/ ) | Adrian Thurston | scanning expressions | MIT |
| [gopkg.in/yaml.v2 ](https://github.com/go-yaml/yaml ) | Canonical | MapSlice | Apache License 2.0 |
2017-06-28 00:06:26 +02:00
2021-06-26 08:54:25 +02:00
Michael Hamrah's [Lexing with Ragel and Parsing with Yacc using
Go](https://medium.com/@mhamrah/lexing-with-ragel-and-parsing-with-yacc-using-go-81e50475f88f)
was essential to understanding `go yacc` .
2017-06-26 14:23:50 +02:00
2021-06-26 08:54:25 +02:00
The [original Liquid engine ](https://shopify.github.io/liquid ), of course, for
the design and documentation of the Liquid template language. Many of the tag
and filter test cases are taken directly from the Liquid documentation.
2017-06-28 00:06:26 +02:00
2017-06-28 15:45:20 +02:00
## Other Implementations
2017-07-02 02:24:27 +02:00
### Go
2021-06-26 08:54:25 +02:00
- [karlseguin/liquid ](https://github.com/karlseguin/liquid ) is a dormant
implementation that inspired a lot of forks.
- [acstech/liquid ](https://github.com/acstech/liquid ) is a more active fork of
Karl Seguin's implementation.
- [hownowstephen/go-liquid ](https://github.com/hownowstephen/go-liquid )
2017-06-28 15:45:20 +02:00
2017-07-02 02:24:27 +02:00
### Other Languages
2017-06-28 15:45:20 +02:00
2017-07-02 02:24:27 +02:00
See Shopify's [ports of Liquid to other environments ](https://github.com/Shopify/liquid/wiki/Ports-of-Liquid-to-other-environments ).
2017-06-28 15:45:20 +02:00
2017-06-26 14:23:50 +02:00
## License
MIT License
2017-07-07 11:41:37 +02:00
[coveralls-url]: https://coveralls.io/r/osteele/liquid?branch=master
[coveralls-svg]: https://img.shields.io/coveralls/osteele/liquid.svg?branch=master
2021-07-06 04:15:01 +02:00
[go-url]: https://github.com/osteele/liquid/actions?query=workflow%3A%22Build+Status%22
2021-06-26 09:07:52 +02:00
[go-svg]: https://github.com/osteele/liquid/actions/workflows/go.yml/badge.svg
2021-06-26 08:43:59 +02:00
2021-07-06 04:18:22 +02:00
[golangci-lint-url]: https://github.com/osteele/liquid/actions?query=workflow%3Lint
2021-06-26 09:07:52 +02:00
[golangci-lint-svg]: https://github.com/osteele/liquid/actions/workflows/golangci-lint.yml/badge.svg
2021-06-26 08:43:59 +02:00
2017-07-07 11:41:37 +02:00
[godoc-url]: https://godoc.org/github.com/osteele/liquid
[godoc-svg]: https://godoc.org/github.com/osteele/liquid?status.svg
[license-url]: https://github.com/osteele/liquid/blob/master/LICENSE
[license-svg]: https://img.shields.io/badge/license-MIT-blue.svg
[go-report-card-url]: https://goreportcard.com/report/github.com/osteele/liquid
[go-report-card-svg]: https://goreportcard.com/badge/github.com/osteele/liquid