www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Temple: Compile time, embedded D templates

reply "Dylan Knutson" <tcdknutson gmail.com> writes:
Hello,

A few months ago I had posted a project of mine, templ-d. It was 
an experiment in writing a template engine for embedding D code 
in arbitrary text files, a-la Vibe.d's Diet templates, but 
without the requirement of generating HTML.

So, I've revamped templ-d, and written Temple in its place. It 
supports all the neat stuff that a template engine should, 
including (but not limited to!)

  - Nested templates (`render` templates within templates)
  - Layouts that can `yield` to partials
  - Compile time generation of the template functions, for zero 
overhead rendering
  - Writes to an OutputStream (a type of OutputRange), making it 
compatible with Vibe.d
  - Easy to work with template contexts, for passing runtime 
variables to templates.

The syntax is based off of eRuby, the templating engine for Ruby 
on Rails, so it should be very recognizable to the RoR devs here, 
and very intuitive to use for those who haven't used eRuby before.

There's a much more in depth rundown, as well as a plethora of 
examples, in the README on the project's page, here:

https://github.com/dymk/temple

dub package: http://code.dlang.org/packages/temple

Thanks, and please let me know what you think!
Dec 30 2013
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-12-31 07:05, Dylan Knutson wrote:
 Hello,

 A few months ago I had posted a project of mine, templ-d. It was an
 experiment in writing a template engine for embedding D code in
 arbitrary text files, a-la Vibe.d's Diet templates, but without the
 requirement of generating HTML.

 So, I've revamped templ-d, and written Temple in its place. It supports
 all the neat stuff that a template engine should, including (but not
 limited to!)
Looks quite nice. Since you support setting variables in a context using opDispatch, why no support opDispatch to get the variables as well? Something like this: Hello, <%= var.name %> And to convert to a specific type: % if(var.should_bort!bool) { -- /Jacob Carlborg
Dec 31 2013
parent reply "Dylan Knutson" <tcdknutson gmail.com> writes:
On Tuesday, 31 December 2013 at 13:10:53 UTC, Jacob Carlborg 
wrote:
 On 2013-12-31 07:05, Dylan Knutson wrote:
 Hello,

 A few months ago I had posted a project of mine, templ-d. It 
 was an
 experiment in writing a template engine for embedding D code in
 arbitrary text files, a-la Vibe.d's Diet templates, but 
 without the
 requirement of generating HTML.

 So, I've revamped templ-d, and written Temple in its place. It 
 supports
 all the neat stuff that a template engine should, including 
 (but not
 limited to!)
Looks quite nice. Since you support setting variables in a context using opDispatch, why no support opDispatch to get the variables as well? Something like this: Hello, <%= var.name %> And to convert to a specific type: % if(var.should_bort!bool) {
Ah yeah, I quite like that. I tried to implement the `var.should_bort!bool` syntax, but I can't pass an additional type parameter to opDispatch, other than the string of the method it's dispatching to. Is there a way to do that? Something like this: struct Params { private: Variant[string] vars; public: T opDispatch(string op, T)() property { return vars[op].get!T; } }
Dec 31 2013
next sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
Added a goodie: Nestable capture blocks (like pseudo-templates 
inside your templates)

This template:
```
		<% auto outer = capture(() { %>
			Outer, first
			<% auto inner = capture(() { %>
				Inner, first
			<% }); %>
			Outer, second

			<%= inner %>
		<% }); %>

		<%= outer %>
```

Evaluates to this:
```
		Outer, first
		Outer, second
			Inner, first
```

a-la Rail's capture helper. It was slightly strange to implement, 
and means tighter coupling between the template function and a 
template context, but I think at this point that's alright, given 
the two are yin and yang anyways.

Right now they don't lazily evaluate; would it be desirable for 
them to do so?
Jan 01 2014
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-01 04:24, Dylan Knutson wrote:

 Ah yeah, I quite like that. I tried to implement the
 `var.should_bort!bool` syntax, but I can't pass an additional type
 parameter to opDispatch, other than the string of the method it's
 dispatching to. Is there a way to do that? Something like this:

 struct Params
 {
 private:
      Variant[string] vars;

 public:
      T opDispatch(string op, T)()  property
      {
          return vars[op].get!T;
      }
 }
I was pretty sure that was possible, but apparently it doesn't compile. -- /Jacob Carlborg
Jan 01 2014
parent Jacob Carlborg <doob me.com> writes:
On 2014-01-01 12:53, Jacob Carlborg wrote:

 I was pretty sure that was possible, but apparently it doesn't compile.
Reported as: https://d.puremagic.com/issues/show_bug.cgi?id=11855 -- /Jacob Carlborg
Jan 01 2014
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-12-31 07:05, Dylan Knutson wrote:
 Hello,

 A few months ago I had posted a project of mine, templ-d. It was an
 experiment in writing a template engine for embedding D code in
 arbitrary text files, a-la Vibe.d's Diet templates, but without the
 requirement of generating HTML.

 So, I've revamped templ-d, and written Temple in its place. It supports
 all the neat stuff that a template engine should, including (but not
 limited to!)
Does it support any kind of helpers, like Rails do? -- /Jacob Carlborg
Jan 01 2014
parent reply "Dylan Knutson" <tcdknutson gmail.com> writes:
On Wednesday, 1 January 2014 at 13:04:27 UTC, Jacob Carlborg 
wrote:
 On 2013-12-31 07:05, Dylan Knutson wrote:
 Hello,

 A few months ago I had posted a project of mine, templ-d. It 
 was an
 experiment in writing a template engine for embedding D code in
 arbitrary text files, a-la Vibe.d's Diet templates, but 
 without the
 requirement of generating HTML.

 So, I've revamped templ-d, and written Temple in its place. It 
 supports
 all the neat stuff that a template engine should, including 
 (but not
 limited to!)
Does it support any kind of helpers, like Rails do?
It didn't before, because of how the semantics of eRuby syntax works, but now it does! It seemed like an important thing to support... Here's an example mimicking a subset of Rails' `form_for` helper: ```d <% import std.string; struct FormHelper { string model_name; auto field_for(string field_name, string type="text") { if(model_name != "") { field_name = "%s[%s]".format(model_name, field_name); } return `<input type="%s" name="%s" />`.format(type, field_name); } auto submit(string value = "Submit") { return `<input type="button" value="%s" />`.format(value); } } auto form_for( string action, string name, void delegate(FormHelper) block) { auto form_body = capture(block, FormHelper(name)); return ` <form action="%s" method="POST"> %s </form>`.format(action, form_body); } %> <%= form_for("/shorten", "", (f) { %> Shorten a URL: <%= f.field_for("url") %> <%= f.submit("Shorten URL") %> <% }); %> <%= form_for("/person", "person", (f) { %> Name: <%= f.field_for("name") %> Age: <%= f.field_for("age") %> DOB: <%= f.field_for("date_of_birth", "date") %> <%= f.submit %> <% }); %> ``` Renders: ``` <form action="/shorten" method="POST"> Shorten a URL: <input type="text" name="url" /> <input type="button" value="Shorten URL" /> </form> <form action="/person" method="POST"> Name: <input type="text" name="person[name]" /> Age: <input type="text" name="person[age]" /> DOB: <input type="date" name="person[date_of_birth]" /> <input type="button" value="Submit" /> </form> ``` This change is present in the latest release of Temple
Jan 01 2014
next sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
I've made a post on Reddit, if anyone that found the library 
nifty would like to upvote:

http://www.reddit.com/r/programming/comments/1u71sr/temple_compile_time_embedded_templating_engine/
Jan 01 2014
prev sibling next sibling parent reply "yazd" <yazan.dabain gmail.com> writes:
On Thursday, 2 January 2014 at 01:12:24 UTC, Dylan Knutson wrote:
 On Wednesday, 1 January 2014 at 13:04:27 UTC, Jacob Carlborg 
 wrote:
 On 2013-12-31 07:05, Dylan Knutson wrote:
 Hello,

 A few months ago I had posted a project of mine, templ-d. It 
 was an
 experiment in writing a template engine for embedding D code 
 in
 arbitrary text files, a-la Vibe.d's Diet templates, but 
 without the
 requirement of generating HTML.

 So, I've revamped templ-d, and written Temple in its place. 
 It supports
 all the neat stuff that a template engine should, including 
 (but not
 limited to!)
Does it support any kind of helpers, like Rails do?
It didn't before, because of how the semantics of eRuby syntax works, but now it does! It seemed like an important thing to support... Here's an example mimicking a subset of Rails' `form_for` helper: ```d <% import std.string; struct FormHelper { string model_name; auto field_for(string field_name, string type="text") { if(model_name != "") { field_name = "%s[%s]".format(model_name, field_name); } return `<input type="%s" name="%s" />`.format(type, field_name); } auto submit(string value = "Submit") { return `<input type="button" value="%s" />`.format(value); } } auto form_for( string action, string name, void delegate(FormHelper) block) { auto form_body = capture(block, FormHelper(name)); return ` <form action="%s" method="POST"> %s </form>`.format(action, form_body); } %> <%= form_for("/shorten", "", (f) { %> Shorten a URL: <%= f.field_for("url") %> <%= f.submit("Shorten URL") %> <% }); %> <%= form_for("/person", "person", (f) { %> Name: <%= f.field_for("name") %> Age: <%= f.field_for("age") %> DOB: <%= f.field_for("date_of_birth", "date") %> <%= f.submit %> <% }); %> ``` Renders: ``` <form action="/shorten" method="POST"> Shorten a URL: <input type="text" name="url" /> <input type="button" value="Shorten URL" /> </form> <form action="/person" method="POST"> Name: <input type="text" name="person[name]" /> Age: <input type="text" name="person[age]" /> DOB: <input type="date" name="person[date_of_birth]" /> <input type="button" value="Submit" /> </form> ``` This change is present in the latest release of Temple
How much of this is done at compile-time? I would guess that the code is compiled but it is evaluated on each render. Is that correct? Is there a way to force compile-time evaluation of at least part of the template which does not depend on a runtime value? Or is it completely dependant on an optimizing compiler to do this work?
Jan 01 2014
parent reply "Dylan Knutson" <tcdknutson gmail.com> writes:
On Thursday, 2 January 2014 at 06:59:04 UTC, yazd wrote:
 How much of this is done at compile-time?
Strictly speaking, just the generation of the code that writes the template to the output buffer. E.g, the code above simulating the Rails form helper would be lowered to code approximately equivalent to this (but with more fluff to add line numbers, and expose some needed hooks for the context): ``` void Temple(OutputStream __buff, TempleContext __context = null) { //... //struct FormHelper, etc //... // The actual rendering into a buffer code auto __buff_tmp_0 = form_for("/shorten", "", (f) { __buff.put("Shorten a URL: "); __buff.put(to!string( f.field_for("url") )); __buff.put(to!string( f.submit("Shorten URL") )); }); __buff.put(__buff_tmp_0); auto __buff_tmp_1 = form_for("/person", "person", (f) { __buff.put("Name: "); __buff.put(to!string( f.field_for("name") )); __buff.put("Age: "); __buff.put(to!string( f.field_for("age") )); __buff.put("DOB: "); __buff.put(to!string( f.field_for("date_of_birth", "date") )); __buff.put(to!string( f.submit )); }); __buff.put(__buff_tmp_1); } ```
I would guess that
 the code is compiled but it is evaluated on each render. Is 
 that correct? Is there a way to force compile-time evaluation 
 of at least part of the template which does not depend on a 
 runtime value?
Yes, generated templates themselves can be executed with CTFE (just had to make a small modification to ArrayOutputAppender, which has been pushed), as long as they don't take a context. The reason for this is that std.variant.Variant isn't CTFEable, because it uses memcpy in opAssign. I'd consider that a Phobos bug; perhaps there is a way to make std.variant CTFE compatible? That'd allow for a much wider (and more useful) range of templates to be evaluated at compile time. Anyways, here's a working compile time evaluated, compile time generated template: ``` string templeToString(TempleFuncType* func, TempleContext context = null) { auto accum = new AppenderOutputStream; (*func)(accum, context); return accum.data; } unittest { alias render = Temple!q{ <% if(true) { %> Bort <% } else { %> No bort! <% } %> <% auto a = capture(() { %> inside a capture block <% }); %> Before capture <%= a %> After capture }; const result = templeToString(&render); static assert(isSameRender(result, ` Bort Before capture inside a capture block After capture `)); } ```
Jan 02 2014
parent reply "Nicolas Sicard" <dransic gmail.com> writes:
On Thursday, 2 January 2014 at 08:36:24 UTC, Dylan Knutson wrote:
 The reason for this is that std.variant.Variant isn't CTFEable, 
 because it uses memcpy in opAssign. I'd consider that a Phobos 
 bug; perhaps there is a way to make std.variant CTFE 
 compatible? That'd allow for a much wider (and more useful) 
 range of templates to be evaluated at compile time.
I wish Variant worked at compile time myself. Did you file a bug/enhancement request? (I couldn't find one in bugzilla).
Jan 03 2014
parent "Dylan Knutson" <tcdknutson gmail.com> writes:
On Friday, 3 January 2014 at 17:50:22 UTC, Nicolas Sicard wrote:
 I wish Variant worked at compile time myself. Did you file a 
 bug/enhancement request? (I couldn't find one in bugzilla).
https://d.puremagic.com/issues/show_bug.cgi?id=11864 Doing some more tests, it seems like just about any template can be evaluated at compile time, if they don't set variables on the context (via var.name, or var("name")). I was surprised to find even filters work for CTFE'd templates!
Jan 03 2014
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-02 02:12, Dylan Knutson wrote:

 It didn't before, because of how the semantics of eRuby syntax works,
 but now it does! It seemed like an important thing to support...

 Here's an example mimicking a subset of Rails' `form_for` helper:
 [Snip]
Nice. Do you have any concept of safe vs unsafe strings? -- /Jacob Carlborg
Jan 02 2014
next sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
On Thursday, 2 January 2014 at 10:59:27 UTC, Jacob Carlborg wrote:
 On 2014-01-02 02:12, Dylan Knutson wrote:

 It didn't before, because of how the semantics of eRuby syntax 
 works,
 but now it does! It seemed like an important thing to 
 support...

 Here's an example mimicking a subset of Rails' `form_for` 
 helper:
 [Snip]
Nice. Do you have any concept of safe vs unsafe strings?
No, but that's been on the to-do list. I feel like safe vs. unsafe strings are tied heavily with the escaping of unsafe strings, which is specific to a language, so I think the best way to go about this is to provide: - A way to define custom "string" types (like a struct wrapping a string tracking if it's safe or not) - Callbacks for processing all of the string-ey things written to the output buffer, to decide if something should be escaped or not.
Jan 02 2014
prev sibling parent reply "Dylan Knutson" <tcdknutson gmail.com> writes:
On Thursday, 2 January 2014 at 10:59:27 UTC, Jacob Carlborg wrote:
 Nice. Do you have any concept of safe vs unsafe strings?
That's a really good idea... [the day passes by] Support implemented in v0.5.0: https://github.com/dymk/temple#filter-policies FilterPolicies allow the developer to insert hooks for when an expression is appended to the buffer. This can be overloaded for different types of expressions, letting concepts like Tainted strings (e.g. your safe vs. unsafe strings) to be implemented. I added an example to the README showing a simple example of this.
Jan 03 2014
parent Jacob Carlborg <doob me.com> writes:
On 2014-01-03 09:39, Dylan Knutson wrote:

 That's a really good idea...
 [the day passes by]

 Support implemented in v0.5.0:
 https://github.com/dymk/temple#filter-policies

 FilterPolicies allow the developer to insert hooks for when an
 expression is appended to the buffer. This can be overloaded for
 different types of expressions, letting concepts like Tainted strings
 (e.g. your safe vs. unsafe strings) to be implemented. I added an
 example to the README showing a simple example of this.
Cool :) -- /Jacob Carlborg
Jan 03 2014