straight-shoota / crinja Goto Github PK
View Code? Open in Web Editor NEWImplementation of Jinja2 template language in Crystal
Home Page: https://straight-shoota.github.io/crinja/
License: Other
Implementation of Jinja2 template language in Crystal
Home Page: https://straight-shoota.github.io/crinja/
License: Other
I've added crinja to my shard.yml
, ran crystal deps
and tried to build the app with a require "crinja"
, but it just produced errors like:
in lib/crinja/src/crinja/runtime/py_tuple.cr:1: while requiring "../value": can't find file '../value' relative to '/home/phortx/workspaces/benny/tesseract/lib/crinja/src/crinja/runtime'
require "../value"
and
in lib/crinja/src/crinja/lib/function/cycler.cr:9: undefined method 'getattr'
getattr :next, :rewind, :reset, :current
^~~~~~~
Compiler error when requiring "crinja":
1 | require "crinja"
^
Error: while requiring "crinja"
In lib/crinja/src/crinja.cr:63:1
63 | require "./lib/filter"
^
Error: while requiring "./lib/filter"
In lib/crinja/src/lib/filter.cr:68:1
68 | require "./filter/*"
^
Error: while requiring "./filter/*"
In lib/crinja/src/lib/filter/collections.cr:2:10
2 | Crinja.filter :list do
^-----
Error: expanding macro
In lib/crinja/src/lib/filter/collections.cr:2:3
2 | Crinja.filter :list do
^
Error: expanding macro
There was a problem expanding macro 'filter'
Called macro defined in lib/crinja/src/runtime/callable.cr:69:3
69 | macro filter(defaults = nil, name = nil, &block)
Which expanded to:
> 1 | Crinja.callable(Crinja::Filter, :list, nil) do
> 2 | target = arguments.target!
> 3 |
> 4 | begin value = target.raw
> 5 | case value
> 6 | when String
> 7 | value.chars
> 8 | when Array
> 9 | value
> 10 | when .responds_to?(:to_a)
> 11 | target.to_a
> 12 | else
> 13 | raise(TypeError.new("target for list filter cannot be converted to list"))
> 14 | end
> 15 | end
> 16 | end
> 17 |
Error: expanding macro
In lib/crinja/src/lib/filter/collections.cr:11:14
11 | target.to_a
^---
Error: instantiating 'Crinja::Value#to_a()'
In lib/crinja/src/runtime/value.cr:399:5
399 | each do |item|
^---
Error: instantiating 'each()'
In lib/crinja/src/runtime/value.cr:255:5
255 | raw_each do |raw|
^-------
Error: instantiating 'raw_each()'
In lib/crinja/src/runtime/value.cr:239:14
239 | object.each { |value| yield value.as(Value).raw }
^---
Error: instantiating '(Array(Crinja::Value) | Crinja::Function::Cycler | Crinja::Tuple | Iterator(Crinja::Value))#each()'
In /usr/share/crystal/src/iterator.cr:597:7
597 | yield value
^
Error: argument #1 of yield expected to be Crinja::Value, not (Array(Crinja::Value) | Bool | Crinja::Callable | Crinja::Object | Crinja::SafeString | Crinja::Undefined | Float64 | Hash(Crinja::Value, Crinja::Value) | Int32 | Int64 | Iterator(Crinja::Value) | Proc(Crinja::Arguments, Crinja::Value) | String | Time | Nil)
Hello
I send current user to index.html but can parse it. What is the correct way?
get "/" do |env|
user = User.find(2).as(User)
user.to_json unless user.nil?
template = cri.get_template("index.html")
template.render({"user" => user})
end
{{user}}
writes this:
{"id":2,"username":"a","email":"a","password":"s","role":"user","created_at":"2018-05-18T15:09:27+0300","updated_at":"2018-05-18T15:09:27+0300"}
Cant parse it with {{user.username}}
or {{user["username"]}}
Maybe simple question, but I am very new to Crystal :)
Thanks in advance.
I've read your docs and looked through the examples and I couldn't find an example of calling some method from the template. I'm using slang at the moment, which is obviously compile time. I'd like runtime templating if possible, but I can't work out how to do something.
We have a method called menu. It takes an int32 which is uses to look some stuff up in a database, then generate some html, then returns that html.
eg
<ul>
menu(1)
</ul>
would produce
<ul>
<li class="active"><a href="/">Home</a></li>
<li><a href="/about/">About</a></li>
<li><a href="/products/">Products</a></li>
</ul>
How would I make my menu method work from crinja? I'm guessing I have to wrap the method in something, can't get my head around what Functions and Tags are in Crinja, not familiar with Jinja.
Just found one of my templates running slow. It loops over 60 + items. If I remove a check for loop.first
the templates drops from a very slow 1.5 seconds, down to 800ms.
Not sure why it spends half it's time assessing loop.first. I've tried with loop.index == 0
and it's equally slow.
Also I've notice if I put it in the second part of an if .. and
expression, the bit after the and is evaluated even if the first one is false.
Hopefully those two issue are easy optimisations. Good work on this engine, really like the idea of run time templates. If I can work either out I'll do a pull request.
First off - this is seriously awesome! I want to embed the following Crystal macro in a template file but crinja keeps giving me errors. The macro:
VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }}
The error:
unhandled exception: Not implemented expression value ```
Looks like a lot of escaping is needed. Suggestions on possibly doing this?
Thank you.
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
.circleci/config.yml
.github/workflows/ci.yml
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
crystal-lang/install-crystal v1
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
crystal-lang/install-crystal v1
mig4/setup-bats v1@af9a00deb21b5d795cabfeaa8d9060410377686d
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
crystal-lang/install-crystal v1
In /home/warrior/projects/opensource/shivneri/src/providers/index.cr:2:1
2 | require "./crinja_view_engine"
^
Error: while requiring "./crinja_view_engine"
In /home/warrior/projects/opensource/shivneri/src/providers/crinja_view_engine.cr:1:1
1 | require "crinja"
^
Error: while requiring "crinja"
In lib/crinja/src/crinja.cr:61:1
61 | require "./lib/tag"
^
Error: while requiring "./lib/tag"
In lib/crinja/src/lib/tag.cr:150:1
150 | require "./tag/*"
^
Error: while requiring "./tag/*"
In lib/crinja/src/lib/tag/for.cr:129:1
129 | require "../util/for_loop"
^
Error: while requiring "../util/for_loop"
In lib/crinja/src/lib/util/for_loop.cr:1:1
1 | require "../function"
^
Error: while requiring "../function"
In lib/crinja/src/lib/function.cr:18:1
18 | require "./function/*"
^
Error: while requiring "./function/*"
In lib/crinja/src/lib/function/joiner.cr:1:8
1 | Crinja.function({sep: ", "}, :joiner) do
^-------
Error: expanding macro
In lib/crinja/src/lib/function/joiner.cr:1:1
1 | Crinja.function({sep: ", "}, :joiner) do
^
Error: expanding macro
There was a problem expanding macro 'function'
Called macro defined in lib/crinja/src/runtime/callable.cr:89:3
89 | macro function(defaults = nil, name = nil, &block)
Which expanded to:
> 1 | Crinja.callable(Crinja::Function, {sep: ", "}, :joiner) do
> 2 | called = false
> 3 | sep = arguments["sep"]
> 4 | ->(_args : Crinja::Arguments) do
> 5 | if called
> 6 | sep
> 7 | else
> 8 | called = true
> 9 | Crinja::Value.new("")
> 10 | end
> 11 | end
> 12 |
> 13 | end
> 14 |
Error: expanding macro
In lib/crinja/src/lib/function/joiner.cr:3:18
3 | sep = arguments["sep"]
^
Error: instantiating 'Crinja::Arguments#[](String)'
In lib/crinja/src/arguments.cr:30:28
30 | elsif index = defaults.key_index(name)
^--------
Error: undefined method 'key_index' for Hash(String, Crinja::Value)
Did you mean 'get_index'?
Crinja seems rather useful, but I'm not familiar with the Jinja2 syntax (only Liquid, Twig, etc.).
Since Crinja deviates from Jinja2 in some ways, it would be much more helpful IMO if the readme just had examples of all the things that you can do with Crinja. Going through the Jinja2 documentation is a bit annoying since it's written with the assumption that you use Python, and I'd much rather know the Crinja way of doing things.
Maybe include a "Getting Started" section with the most common features people will likely use? This should make it easier for people to understand how to use Crinja without a background in Python + Jinja2. Something like how Slang does it would be ideal, but with distinct examples separated into subsections instead.
Usually when working with static site generators, there are two common directories: one for includes and one for the layouts that use these includes. Example:
{% include "includes/header.html" %}
...
{% include "includes/footer.html" %}
Since this type of pattern is so common, I propose the following shorthand:
{% include header %}
...
{% include footer %}
How does it work? If no explicit path is given (i.e. no quotation marks), look inside the default includes directory (customizable by the user), then:
Crinja::TemplateNotFoundError
like usual.I already have a site using ecr engine. Now I want to migrate it to use crinja engine. Take an example for this code:
name = "Foo"
render "Hello <%= name %>" #=> Hello Foo
crinja.render "Hello {{ name }}", { "name" => name }
As far as I know, ecr embed the template string directly to program code. Is it possible to config crinja make it behave as ecr? I want to migrate as least as possible :)
Hello, what this error means? I have Array of MyObject class instances in template bindings and I want to access the Array of MyObject in template like:
{% for obj in objs %}
...
{{ obj.some_property }}
...
{% endblock %}
MyObject is my own class/struct with proper getters.
Thanks!
Build warning:
URI.escape depreciated. Changed to URI.encode on my local copy in lib directory and tested.
Thanks.
Following code will not work because the loop variable scope.
{% set dte = "-" %}
{% for blog in blogs %}
{% if (blog.created_at | date("%b %Y")) != dte %}
<h2>{{ blog.created_at | date("%b %Y") }}</h2>
{% set dte = blog.created_at | date("%b %Y") %}
{% endif %}
<p>{{ blog.title }}</p>
{% endfor %}
Jinja 2 provides namespace
using that this use case will work (Ref: https://jinja.palletsprojects.com/en/3.0.x/templates/#assignments)
This feature was introduced in Jinja version 2.10
{% set ns = namespace(dte="-") %}
{% for blog in blogs %}
{% if (blog.created_at | date("%b %Y")) != ns.dte %}
<h2>{{ blog.created_at | date("%b %Y") }}</h2>
{% set ns.dte = blog.created_at | date("%b %Y") %}
{% endif %}
<p>{{ blog.title }}</p>
{% endfor %}
Now I cannot build the master branch of Crinja with crystal-0.25.0 because of attached error.
When I was using jm/crystal-0.25.0
which is merged by this PR, it worked.
Could you kindly give any suggestion?
$ crystal -v
Crystal 0.25.0 [7fb783f7a] (2018-06-11)
LLVM: 4.0.0
Default target: x86_64-unknown-linux-gnu
$ shards build
Fetching https://github.com/MakeNowJust/crystal-diff.git
Fetching https://github.com/schovi/baked_file_system.git
Using diff (1.0.0 at master)
Installing baked_file_system (0.9.5 at 0.9.6)
Building: crinja
Error target crinja failed to compile:
Error in src/cli.cr:3: while requiring "./crinja"
require "./crinja"
^
in src/crinja.cr:63: while requiring "./lib/filter"
require "./lib/filter"
^
in src/lib/filter.cr:68: while requiring "./filter/*"
require "./filter/*"
^
in src/lib/filter/collections.cr:2: expanding macro
Crinja.filter :list do
^~~~~~
in src/lib/filter/collections.cr:2: expanding macro
Crinja.filter :list do
^
in macro 'filter' /home/shibata_h/Workspace/crinja/src/runtime/callable.cr:69, line 1:
> 1. Crinja.callable(Crinja::Filter, :list, nil) do
2. target = arguments.target!
3.
4. #<loc:push>begin #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",3,5>#<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",3,5>#<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",3,5>value = #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",3,13>#<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",3,13>target.raw
5. #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",5,5>case #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",5,10>value
6. #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",6,5>when #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",6,10>String
7. #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",7,7>#<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",7,7>value.chars
8. #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",8,5>when #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",8,10>Array
9. #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",9,7>value
10. #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",10,5>when .responds_to?(:to_a)
11. #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",11,7>#<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",11,7>target.to_a
12. else
13. #<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",13,7>raise(#<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",13,13>#<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",13,13>TypeError.new(#<loc:"/home/shibata_h/Workspace/crinja/src/lib/filter/collections.cr",13,27>"target for list filter cannot be converted to list"))
14. end
15. end#<loc:pop>
16. end
17.
expanding macro
in src/lib/filter/collections.cr:11: instantiating 'Crinja::Value#to_a()'
target.to_a
^~~~
in src/runtime/value.cr:394: instantiating 'each()'
each do |item|
^~~~
in src/runtime/value.cr:254: instantiating 'raw_each()'
raw_each do |raw|
^~~~~~~~
in src/runtime/value.cr:238: instantiating '(Array(Crinja::Value) | Crinja::Function::Cycler | Crinja::PyTuple | Iterator(Crinja::Value))#each()'
object.each { |value| yield value.as(Value).raw }
^~~~
in /usr/share/crystal/src/iterator.cr:390: instantiating 'Crinja::Tag::For::ConditionalIterator#next()'
value = self.next
^~~~
in src/lib/tag/for.cr:116: instantiating 'loop()'
loop do
^~~~
in src/lib/tag/for.cr:116: instantiating 'loop()'
loop do
^~~~
in src/lib/tag/for.cr:118: instantiating 'Crinja::Context#[]=(String, Crinja::StrictUndefined)'
@env.context[LOOP_VARIABLE] = StrictUndefined.new(LOOP_VARIABLE)
^
in src/runtime/context.cr:93: instantiating 'Crinja::Context#[]=(String, Crinja::Value)'
self[key] = Value.new(value)
^
in src/runtime/context.cr:88: wrong number of arguments for 'Crinja::Util::ScopeMap(String, Crinja::Value)#[]=' (given 2, expected 1)
Overloads are:
- Crinja::Util::ScopeMap(K, V)#[]=(arg)
super
^~~~~
Example:
...
{% set active = "active" %}
{% set cnt = 1 %}
{% for photo in photos %}
<div class="carousel-item {{ active }} {{ cnt }}">
<img src="{{ photo }}" class="d-block w-100">
</div>
{% set active = "" %}
{% set cnt = cnt + 1 %}
{% endfor %}
...
Variables "active" and "cnt" are still the same - what's wrong?
Generated output:
<div class="carousel-item active 1">
<img src="photos/camp/foto1.jpg" class="d-block w-100">
</div>
<div class="carousel-item active 1">
<img src="photos/camp/foto14.jpg" class="d-block w-100">
</div>
<div class="carousel-item active 1">
<img src="photos/camp/foto2.jpg" class="d-block w-100">
</div>
....
This error shows up using crinja (not with crystal 0.28.0). I can't find what broke by looking through the changelog.
Error in src/quikweb.cr:24: while requiring "crinja"
.
in macro 'filter' /vagrant/Projects/crystal/quikweb/lib/crinja/src/runtime/callable.cr:69, line 1:
.
.
expanding macro
in lib/crinja/src/lib/filter/number.cr:24:no overload matches 'String#to_f?' with type Int32
Overloads are:
- String#to_f?(whitespace : Bool = true, strict : Bool = true)
result = raw.to_f?(arguments["base"].to_i).try &.to_i
On the current version of crystal (0.27.0) its throwing many errors on compilation for us.
Last error is always:
in /usr/share/crystal/src/indexable.cr:188: abstract `def Indexable(T)#unsafe_fetch(index : Int)` must be implemented by Crinja::Tuple
yield unsafe_fetch(i)
Seems to be related to your last commit.
Take the following program:
require "crinja"
struct StructWithIterator
include Crinja::Object
# removing this `include` fixes
include ::Iterable(Crinja::Value)
@iter : -> ::Iterator(Crinja::Value)
def initialize
@iter = "invalid code to show it isn't being instanced directly?"
end
def each : ::Iterator(Crinja::Value)
# replacing the below line fixes:
# Array(Crinja::Value).new.each
@iter.call
end
# to satisfy Value#raw_each
def each(& : Crinja::Value ->)
yield Crinja.value(1)
end
# to satisfy Value#first
def first
1
end
end
template = Crinja::Template.new(<<-JINJA)
{% for k, v in table %}
{% endfor %}
JINJA
table = {0 => 0} # must be a hash?
template.render({table: table})
Take note that StructWithIter
is NOT used directly within any of this code.
If you compile this program once, and run it multiple times, it will have one of two outputs:
Unhandled exception: Missing value for unpack
template: <string>:1:1 .. 2:13
1 | {% for k, v in table %}
X | ^
2 | {% endfor %}
(Crinja::RuntimeError)
from lib/crinja/src/runtime/context.cr:133:25 in 'unpack'
from lib/crinja/src/lib/tag/for.cr:83:13 in 'run_loop'
from lib/crinja/src/lib/tag/for.cr:35:5 in 'interpret_output'
from lib/crinja/src/runtime/renderer.cr:90:5 in 'render'
from lib/crinja/src/runtime/renderer.cr:70:19 in 'render'
from lib/crinja/src/runtime/renderer.cr:64:5 in 'render'
from lib/crinja/src/runtime/renderer.cr:50:14 in 'render'
from lib/crinja/src/template.cr:69:5 in 'render'
from lib/crinja/src/template.cr:61:7 in 'render'
from lib/crinja/src/template.cr:54:7 in 'render'
from _example.cr:38:1 in '__crystal_main'
from /usr/lib/crystal/crystal/main.cr:129:5 in 'main_user_code'
from /usr/lib/crystal/crystal/main.cr:115:7 in 'main'
from /usr/lib/crystal/crystal/main.cr:141:3 in 'main'
from /usr/lib/libc.so.6 in '??'
from /usr/lib/libc.so.6 in '__libc_start_main'
from /home/lune/.cache/crystal/crystal-run-_example.tmp in '_start'
from ???
Unhandled exception: Index out of bounds (IndexError)
from /usr/lib/crystal/indexable.cr:89:20 in '[]'
from /usr/lib/crystal/indexable.cr:1324:17 in 'next'
from lib/crinja/src/runtime/value.cr:274:15 in 'next'
from lib/crinja/src/runtime/value.cr:302:15 in 'next'
from lib/crinja/src/runtime/context.cr:131:19 in 'unpack'
from lib/crinja/src/lib/tag/for.cr:83:13 in 'run_loop'
from lib/crinja/src/lib/tag/for.cr:35:5 in 'interpret_output'
from lib/crinja/src/runtime/renderer.cr:90:5 in 'render'
from lib/crinja/src/runtime/renderer.cr:70:19 in 'render'
from lib/crinja/src/runtime/renderer.cr:64:5 in 'render'
from lib/crinja/src/runtime/renderer.cr:50:14 in 'render'
from lib/crinja/src/template.cr:69:5 in 'render'
from lib/crinja/src/template.cr:61:7 in 'render'
from lib/crinja/src/template.cr:54:7 in 'render'
from _example.cr:38:1 in '__crystal_main'
from /usr/lib/crystal/crystal/main.cr:129:5 in 'main_user_code'
from /usr/lib/crystal/crystal/main.cr:115:7 in 'main'
from /usr/lib/crystal/crystal/main.cr:141:3 in 'main'
from /usr/lib/libc.so.6 in '??'
from /usr/lib/libc.so.6 in '__libc_start_main'
from /home/lune/.cache/crystal/crystal-run-_example.tmp in '_start'
from ???
If you compile it in release mode, it segfaults due to some kind of stack corruption:
Invalid memory access (signal 11) at address 0xa
[0x557acb3d6d29] ?? +93985884171561 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x557acb3d6cf8] ?? +93985884171512 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x7f5dee740710] ?? +140041409267472 in /usr/lib/libc.so.6
[0x557acb3f9b3c] ?? +93985884314428 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x557acb3f9925] ?? +93985884313893 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x557acb3f9c55] ?? +93985884314709 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x557acb40e062] ?? +93985884397666 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x557acb40a50c] ?? +93985884382476 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x557acb440040] ?? +93985884602432 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x557acb37eeb8] __crystal_main +34424 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x557acb3840f0] main +64 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x7f5dee729cd0] ?? +140041409174736 in /usr/lib/libc.so.6
[0x7f5dee729d8a] __libc_start_main +138 in /usr/lib/libc.so.6
[0x557acb376765] _start +37 in /home/lune/.cache/crystal/crystal-run-_example.tmp
[0x0] ???
I think this is clearly some kind of upstream compiler issue, but I'm opening here first to see if we can't workshop a more minimal repro that doesn't depend on Crinja, or maybe you can identify an existing upstream issue. If you would prefer me to open on the main Crystal repo, I can move it there.
The behavior is also slightly different if you pass table = [1, 2, 3]
for example; it will 50/50 between outputting nothing and index OOB, where a hash will throw 100% of the time (with 2 different errors).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.