{"id":13483862,"url":"https://github.com/straight-shoota/crinja","last_synced_at":"2025-10-04T17:50:44.875Z","repository":{"id":20702638,"uuid":"90669203","full_name":"straight-shoota/crinja","owner":"straight-shoota","description":"Implementation of Jinja2 template language in Crystal","archived":false,"fork":false,"pushed_at":"2025-08-11T13:42:27.000Z","size":9209,"stargazers_count":133,"open_issues_count":9,"forks_count":11,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-08-11T15:24:37.312Z","etag":null,"topics":["crystal","jinja","jinja2","template-engine"],"latest_commit_sha":null,"homepage":"https://straight-shoota.github.io/crinja/","language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/straight-shoota.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-05-08T20:24:10.000Z","updated_at":"2025-08-11T13:42:12.000Z","dependencies_parsed_at":"2024-04-25T18:25:27.122Z","dependency_job_id":"f54ffca7-2c16-4d75-99f1-292e36fb63b3","html_url":"https://github.com/straight-shoota/crinja","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/straight-shoota/crinja","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/straight-shoota%2Fcrinja","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/straight-shoota%2Fcrinja/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/straight-shoota%2Fcrinja/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/straight-shoota%2Fcrinja/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/straight-shoota","download_url":"https://codeload.github.com/straight-shoota/crinja/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/straight-shoota%2Fcrinja/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278350959,"owners_count":25972675,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crystal","jinja","jinja2","template-engine"],"created_at":"2024-07-31T17:01:16.174Z","updated_at":"2025-10-04T17:50:44.387Z","avatar_url":"https://github.com/straight-shoota.png","language":"Crystal","funding_links":[],"categories":["Template Engine"],"sub_categories":[],"readme":"# crinja\n\n[![Build Status](https://travis-ci.org/straight-shoota/crinja.svg?branch=master)](https://travis-ci.org/straight-shoota/crinja)\n[![CircleCI](https://circleci.com/gh/straight-shoota/crinja.svg?style=svg)](https://circleci.com/gh/straight-shoota/crinja)\n[![Open Source Helpers](https://www.codetriage.com/straight-shoota/crinja/badges/users.svg)](https://www.codetriage.com/straight-shoota/crinja)\n\nCrinja is an implementation of the [Jinja2 template engine](http://jinja.pocoo.org) written in [Crystal](https://crystal-lang.org/). Templates are parsed and evaluated at runtime (see [Background](#background)). It includes a script runtime for evaluation of dynamic python-like expressions used by the Jinja2 syntax.\n\n**[API Documentation](https://straight-shoota.github.io/crinja/api/latest/)** ·\n**[Github Repo](https://github.com/straight-shoota/crinja)** ·\n**[Template Syntax](https://github.com/straight-shoota/crinja/blob/master/TEMPLATE_SYNTAX.md)**\n\n## Features\n\nCrinja tries to stay close to the Jinja2 language design and implementation. It currently provides most features of the original template language, such as:\n\n* all basic language features like control structures and expressions\n* template inheritance\n* block scoping\n* custom tags, filters, functions, operators and tests\n* autoescape by default\n* template cache\n\nFrom Jinja2 all builtin [control structures (tags)](http://jinja.pocoo.org/docs/2.9/templates/#list-of-control-structures), [tests](http://jinja.pocoo.org/docs/2.9/templates/#list-of-builtin-tests), [global functions](http://jinja.pocoo.org/docs/2.9/templates/#list-of-global-functions), [operators](http://jinja.pocoo.org/docs/2.9/templates/#expressions) and [filters](http://jinja.pocoo.org/docs/2.9/templates/#list-of-builtin-filters) have been ported to Crinja. See `Crinja::Filter`, `Crinja::Test`, `Crinja::Function`, `Crinja::Tag`, `Crinja::Operator` for lists of builtin features.\n\nCurrently, template errors fail fast raising an exception. It is considered to change this behaviour to collect multiple errors, similar to what Jinjava does.\n\n## Installation\n\nAdd this to your application's `shard.yml`:\n\n```yaml\ndependencies:\n  crinja:\n    github: straight-shoota/crinja\n```\n\n## Usage\n\n### Simple string template\n```crystal\nrequire \"crinja\"\n\nCrinja.render(\"Hello, {{ name }}!\", {\"name\" =\u003e \"John\"}) # =\u003e \"Hello, John!\"\n```\n\n### File loader\n\nWith this template file:\n```html\n# views/index.html.j2\n\u003cp\u003eHello {{ name | default('World') }}\u003c/p\u003e\n```\n\nIt can be loaded with a `FileSystemLoader`:\n\n```crystal\nrequire \"crinja\"\n\nenv = Crinja.new\nenv.loader = Crinja::Loader::FileSystemLoader.new(\"views/\")\ntemplate = env.get_template(\"index.html.j2\")\ntemplate.render # =\u003e \"Hello, World!\"\ntemplate.render({ \"name\" =\u003e \"John\" }) # =\u003e \"Hello, John!\"\n```\n\n### Crystal Playground\n\nRun the **Crystal playground** inside this repostitory and the server is prepared with examples of using Crinja's API (check the `Workbooks` section).\n\n```shell\n$ crystal play\n```\n\nYou can also browse the examples and documentation online (without the interactive playground): [objects](https://straight-shoota.github.io/crinja/api/latest/playground/objects.html) \u0026 [features](https://straight-shoota.github.io/crinja/api/latest/playground/features.html)\n\n### Crinja Playground\n\nThe **Crinja Example Server** in [`examples/server`](https://github.com/straight-shoota/crinja/tree/master/examples/server) is an HTTP server which renders Crinja templates from `examples/server/pages`. It has also an interactive playground for Crinja template testing at `/play`.\n\n```shell\n$ cd examples/server \u0026\u0026 crystal server.cr\n```\n\nOther examples can be found in the [`examples` folder](https://github.com/straight-shoota/crinja/tree/master/examples).\n\n## Template Syntax\n\nThe following is a quick overview of the template language to get you started.\n\nMore details can be found in **[the template guide](https://github.com/straight-shoota/crinja/blob/master/TEMPLATE_SYNTAX.md)**.\nThe original [Jinja2 template reference](http://jinja.pocoo.org/docs/2.9/templates/) can also be helpful, Crinja templates are mostly similar.\n\n### Expressions\n\nIn a template, **expressions** inside double curly braces (`{{` ... `}}`) will be evaluated and printed to the template output.\n\nAssuming there is a variable `name` with value `\"World\"`, the following template renders `Hello, World!`.\n\n```html+jinja\nHello, {{ name }}!\n```\n\nProperties of an object can be accessed by dot (`.`) or square brackets (`[]`). Filters modify the value of an expression.\n\n```html+jinja\nHello, {{ current_user.name | default(\"World\") | titelize }}!\n```\n\nTests are similar to filters, but are used in the context of a boolean expression, for example as condition of an `if` tag.\n\n```html+jinja\n{% if current_user is logged_in %}\n  Hello, {{ current_user.name }}!\n{% else %}\n  Hey, stranger!\n{% end %}\n```\n\n### Tags\n\n**Tags** control the logic of the template. They are enclosed in `{%` and `%}`.\n\n```html+jinja\n{% if is_morning %}\n  Good Morning, {{ name }}!\n{% else %}\n  Hello, {{ name }}!\n{% end %}\n```\n\nThe `for` tag allows looping over a collection.\n\n```html+jinja\n{% for name in users %}\n  {{ user.name }}\n{% endfor %}\n```\n\nOther templates can be included using the `include` tag:\n\n```html+jinja\n{% include \"header.html\" %}\n\n\u003cmain\u003e\n  Content\n\u003c/main\u003e\n\n{% include \"footer.html\" %}\n```\n\n### Macros\n\nMacros are similar to functions in other programming languages.\n\n```html+jinja\n{% macro say_hello(name) %}Hello, {{ name | default(\"stranger\") }}!{% endmacro %}\n{{ say_hello('Peter') }}\n{{ say_hello('Paul') }}\n```\n\n### Template Inheritance\nTemplate inheritance enables the use of `block` tags in parent templates that can be overwritten by child templates. This is useful for implementating layouts:\n\n```html+jinja\n{# layout.html #}\n\n\u003ch1\u003e{% block page_title %}{% endblock %}\u003c/h1\u003e\n\n\u003cmain\u003e\n  {% block body %}\n    {# This block is typically overwritten by child templates #}\n  {% endblock %}\n\u003c/main\u003e\n\n{% block footer %}\n  {% include \"footer.html\" %}\n{% endblock %}\n```\n\n```html+jinja\n{# page.html #}\n{% extends \"layout.html\" %}\n\n{% block page_title %}Blog Index{% endblock %}\n{% block body %}\n  \u003cul\u003e\n    {% for article in articles if article.published %}\n    \u003cdiv class=\"article\"\u003e\n      \u003cli\u003e\n        \u003ca href=\"{{ article.href | escape }}\"\u003e{{ article.title | escape }}\u003c/a\u003e\n        written by \u003ca href=\"{{ article.user.href | escape}}\"\u003e{{ article.user.username | escape }}\u003c/a\u003e\n      \u003c/li\u003e\n    {%- endfor %}\n  \u003c/ul\u003e\n{% endblock %}\n```\n\n## Crystal API\n\nThe API tries to stick ot the original [Jinja2 API](http://jinja.pocoo.org/docs/2.9/api/) which is written in Python.\n\n**[API Documentation](https://straight-shoota.github.io/crinja/api/latest/)**\n\n### Configuration\n\nCurrently the following configuration options for `Config` are supported:\n\n\u003cdl\u003e\n  \u003cdt\u003eautoescape\u003c/dt\u003e\n  \u003cdd\u003e\n  \u003cp\u003eThis config allows the same settings as \u003ccode\u003e\u003ca href=\"http://jinja.pocoo.org/docs/2.9/api/#jinja2.select_autoescape\"\u003eselect_autoescape\u003c/a\u003e\u003c/code\u003e in Jinja 2.9.\u003c/p\u003e\n  \u003cp\u003eIt intelligently sets the initial value of autoescaping based on the filename of the template.\u003c/p\u003e\n  \u003cp\u003eWhen set to a boolean value, \u003ccode\u003efalse\u003c/code\u003e deactivates any autoescape and \u003ccode\u003etrue\u003c/code\u003e activates autoescape for any template.\n  It also allows more detailed configuration:\u003c/p\u003e\n  \u003cdl\u003e\n    \u003cdt\u003eenabled_extensions\u003c/dt\u003e\n    \u003cdd\u003eList of filename extensions that autoescape should be enabled for. Default: \u003ccode\u003e[\"html\", \"htm\", \"xml\"]\u003c/code\u003e\u003c/dd\u003e\n    \u003cdt\u003edisabled_extensions\u003c/dt\u003e\n    \u003cdd\u003eList of filename extensions that autoescape should be disabled for. Default: \u003ccode\u003e[] of String\u003c/code\u003e\u003c/dd\u003e\n    \u003cdt\u003edefault_for_string\u003c/dt\u003e\n    \u003cdd\u003eDetermines autoescape default value for templates loaded from a string (without a filename). Default: \u003ccode\u003efalse\u003c/code\u003e\u003c/dd\u003e\n    \u003cdt\u003edefault\u003c/dt\u003e\n    \u003cdd\u003eIf nothing matches, this will be the default autoescape value. Default: \u003ccode\u003efalse\u003c/code\u003e\u003c/dd\u003e\n  \u003c/dl\u003e\n  \u003cp\u003eNote: \u003cem\u003eThe default configuration of Crinja differs from that of Jinja 2.9, that autoescape is activated by default for HTML and XML files. This will most likely be changed by Jinja2 in the future, too.\u003c/em\u003e\u003c/p\u003e\n  \u003c/dd\u003e\n  \u003cdt\u003edisabled_filters\u003c/dt\u003e\n  \u003cdd\u003eA list of *disabled_filters* that will raise a `SecurityError` when invoked.\u003c/dd\u003e\n  \u003cdt\u003edisabled_functions\u003c/dt\u003e\n  \u003cdd\u003eA list of *disabled_functions* that will raise a `SecurityError` when invoked.\u003c/dd\u003e\n  \u003cdt\u003edisabled_operators\u003c/dt\u003e\n  \u003cdd\u003eA list of *disabled_operators* that will raise a `SecurityError` when invoked.\u003c/dd\u003e\n  \u003cdt\u003edisabled_tags\u003c/dt\u003e\n  \u003cdd\u003eA list of *disabled_tags* that will raise a `SecurityError` when invoked.\u003c/dd\u003e\n  \u003cdt\u003edisabled_tests\u003c/dt\u003e\n  \u003cdd\u003eA list of *disabled_tests* that will raise a `SecurityError` when invoked.\u003c/dd\u003e\n  \u003cdt\u003ekeep_trailing_newline\u003c/dt\u003e\n  \u003cdd\u003ePreserve the trailing newline when rendering templates. If set to `false`, a single newline, if present, will be stripped from the end of the template. Default: \u003ccode\u003efalse\u003c/code\u003e\u003c/dd\u003e\n  \u003cdt\u003etrim_blocks\u003c/dt\u003e\n  \u003cdd\u003eIf this is set to \u003ccode\u003etrue\u003c/code\u003e, the first newline after a block is removed. This only applies to blocks, not expression tags. Default: \u003ccode\u003efalse\u003c/code\u003e.\u003c/dd\u003e\n  \u003cdt\u003elstrip_blocks\u003c/dt\u003e\n  \u003cdd\u003eIf this is set to \u003ccode\u003etrue\u003c/code\u003e, leading spaces and tabs are stripped from the start of a line to a block. Default: \u003ccode\u003efalse\u003c/code\u003e.\u003c/dd\u003e\n  \u003ctd\u003eregister_defaults\u003c/td\u003e\n  \u003cdd\u003eIf \u003ccode\u003eregister_defaults\u003c/code\u003e is set to \u003ccode\u003etrue\u003c/code\u003e, all feature libraries will be populated with the defaults (Crinja standards and registered custom features).\n  Otherwise the libraries will be empty. They can be manually populated with \u003ccode\u003elibrary.register_defaults\u003c/code\u003e.\n  This setting needs to be set at the creation of an environment.\u003c/dd\u003e\n\u003c/dl\u003e\n\nSee also the original [Jinja2 API Documentation](http://jinja.pocoo.org/docs/2.9/api/).\n\n### Custom features\n\nYou can provide custom tags, filters, functions, operators and tests. Create an implementation using the macros `Crinja.filter`, `Crinja.function`, `Crinja.test`. They need to be passed a block which will be converted to a Proc. Optional arguments are a `Hash` or `NamedTuple` with default arguments and a name. If a name is provided, it will be added to the feature library defaults and available in every environment which uses the registered defaults.\n\nExample with macro `Crinja.filter`:\n\n```crystal\nenv = Crinja.new\n\nmyfilter = Crinja.filter({ attribute: nil }) do\n  \"#{target} is #{arguments[\"attribute\"]}!\"\nend\n\nenv.filters[\"customfilter\"] = myfilter\n\ntemplate = env.from_string(%({{ \"Hello World\" | customfilter(attribute=\"super\") }}))\ntemplate.render # =\u003e \"Hello World is super!\"\n```\n\nOr you can define a class for more complex features:\n```crystal\nclass Customfilter\n  include Crinja::Callable\n\n  getter name = \"customfilter\"\n\n  getter defaults = Crinja.variables({\n    \"attribute\" =\u003e \"great\"\n  })\n\n  def call(arguments)\n    \"#{arguments.target} is #{arguments[\"attribute\"]}!\"\n  end\nend\n\nenv = Crinja.new\nenv.filters \u003c\u003c Customfilter.new\n\ntemplate = env.from_string(%({{ \"Hello World\" | customfilter(attribute=\"super\") }}))\ntemplate.render # =\u003e \"Hello World is super!\"\n```\n\nCustom tags and operator can be implemented through subclassing `Crinja::Operator` and  `Crinja:Tag` and adding an instance to the feature library defaults (`Crinja::Operator::Library.defaults \u003c\u003c MyTag.new`) or to a specific environment (`env.tags \u003c\u003c MyTag.new`).\n\n## Differences from Jinja2\n\nThis is an incomplete list of **Differences to the original Jinja2**:\n\n* **Python expressions:** Because templates are evaluated inside a compiled Crystal program, it's not possible to use ordinary Python expressions in Crinja. But it might be considered to implement some of the Python stdlib like `Dict#iteritems()` which is often used to make dicts iterable.\n* **Line statements and line comments**: Are not supported, because their usecase is negligible.\n* **String representation:** Some objects will have slightly different representation as string or JSON. Crinja uses Crystal internals, while Jinja uses Python internals. For example, an array with strings like `{{ [\"foo\", \"bar\"] }}` will render as `[u'foo', u'bar']` in Jinja2 and as `['foo', 'bar']` in Crinja.\n* **Double escape:** `{{ '\u003chtml\u003e'|escape|escape }}` will render as `\u0026lt;html\u0026gt;` in Jinja2, but `\u0026amp;lt;html\u0026amp;gt;`. Should we change that behaviour?\n* **Complex numbers**: Complex numbers are not supported yet.\n* **Configurable syntax**: It is not possible to reconfigure the syntax symbols. This makes the parser less complex and faster.\n\nThe following features are not yet fully implemented, but on the [roadmap](ROADMAP.md):\n\n* Sandboxed execution.\n* Some in-depth features like extended macro reflection, reusable blocks.\n\n## Background\n\nCrystal is a great programming language with a clean syntax inspired by Ruby, but it is compiled and runs incredibly fast.\n\nThere are already some [template engines for crystal](https://github.com/veelenga/awesome-crystal#template-engine). But if you want control structures and dynamic expressions without some sort of Domain Specific Language, there is only [Embedded Crystal (ECR)](https://crystal-lang.org/api/0.21.1/ECR.html), which is a part of Crystal's standard library. It uses macros to convert templates to Crystal code and embed them into the source at compile time. So for every change in a template, you have to recompile the binary. This approach is certainly applicable for many projects and provides very fast template rendering. The downside is, you need a crystal build stack for template design. This makes it impossible to render dynamic, user defined templates, that can be changed at runtime.\n\nJinja2 is a powerful, mature template engine with a great syntax and proven language design. Its philosophy is:\n\n\u003e Application logic is for the controller, but don't make the template designer's life difficult by restricting functionality too much.\n\nJinja derived from the [Django Template Language](http://docs.djangoproject.com/en/dev/ref/templates/builtins/). While it comes from web development and is heavily used there ([Flask](http://flask.pocoo.org/))\n[Ansible](https://ansible.com/) and [Salt](http://www.saltstack.com/) use it for dynamic enhancements of configuration data. It has quite a number of implementations and adaptations in other languages:\n\n* [Jinjava](https://github.com/HubSpot/jinjava) - Jinja2 implementation in Java using [Unified Expression Language](https://uel.java.net/) (`javaex.el`) for expression resolving. It served as an inspiration for some parts of Crinja.\n* [Liquid](https://shopify.github.io/liquid/) - Jinja2-inspired template engine in Ruby\n* [Liquid.cr](https://github.com/TechMagister/liquid.cr) - Liquid implementation in Crystal\n* [Twig](https://twig.symfony.com/) - Jinja2-inspired template engine in PHP\n* [ginger](https://hackage.haskell.org/package/ginger) - Jinja2 implementation in Haskell\n* [Jinja-Js](https://github.com/sstur/jinja-js) - Jinja2-inspired template engin in Javascript\n* [jigo](https://github.com/jmoiron/jigo) - Jinja2 implementation in Go\n* [tera](https://github.com/Keats/tera) - Jinja2 implementation in Rust\n* [jingoo](https://github.com/tategakibunko/jingoo) - Jinja2 implementation in OCaml\n* [nunjucks](https://mozilla.github.io/nunjucks/) - Jinja2 inspired template engine in Javascript\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/straight-shoota/crinja/fork\u003e)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Contributors\n\n- [straight-shoota](https://github.com/straight-shoota) Johannes Müller - creator, maintainer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstraight-shoota%2Fcrinja","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstraight-shoota%2Fcrinja","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstraight-shoota%2Fcrinja/lists"}