Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Templating Engine Support (as Middleware, ideally) #125

Closed
reem opened this issue Aug 8, 2014 · 33 comments
Closed

Templating Engine Support (as Middleware, ideally) #125

reem opened this issue Aug 8, 2014 · 33 comments

Comments

@reem
Copy link
Member

reem commented Aug 8, 2014

mustache is probably the only thing worth supporting right now

@brycefisher
Copy link
Contributor

rust-mustache is pretty decent.

I'm not sure about making this middleware. Do mean create a new piece of middleware that acts as the templating engine?

I'm familiar with how Express does this for NodeJs. The developer configures Express to pass through res.render() invocations to the templating engine. The developer also supplies template director(y|ies). Do you plan to create something similar?

@reem
Copy link
Member Author

reem commented Aug 10, 2014

This is tricky, because we'd like the method to be implemented on Response but access some form of app-global configuration - you'd set up the templating by adding a Middleware with some options on it like the templating engine and template directories, but I'm not sure how that information gets to res.render.

This are good thoughts to be having, but I'm not sure what the answer is.

@brycefisher
Copy link
Contributor

I just realized that Mixins would make Middleware an ideal way to provide a replaceable templating engine. Sorry I'm a little slow on the uptake.

Perhaps, we could establish a convention that a particular Alloy entry (maybe something like a Vec of Path objects) contains the directories for whatever template engine Middleware is used. There could be a Hashmap in Alloy for per-engine app-wide configuration settings.

A third part of the convention could be that a piece of middleware acting as the render engine would provide a res.render() method via Mixins.

How does that strike your fancy?

@reem
Copy link
Member Author

reem commented Aug 10, 2014

I think it's cleaner to just provide a Templating trait that any template engine could implement, then you provide an instance of that trait to the templating middleware and it uses that for res.render. My main concern is that res.render would have no access to the Alloy and therefore no access to any configuration.

This is solvable by placing a second Alloy in Response, but I'm not sure if that's the way to go. I think there is a better, more general way to do app-wide configuration than this, but I'm not sure how yet.

Maybe the best way would be to add a global-configuration Alloy on each Iron instance - then provide a &-reference to the app in both request and response? This is fast but also quite convenient, and could in fact be made memory efficient by storing the global Alloy behind an Arc, so it is not cloned for each request.

@brycefisher
Copy link
Contributor

Yes! That's essentially what express does with app.set(key, value) and app.get(key). Is no other way to provide config, other than in fn arguments?

It seems like there may be other app-wide configuration needs that may come along anyway -- there should a simple app-wide in memory data-store ready to go.

@reem
Copy link
Member Author

reem commented Aug 10, 2014

I'm only concerned that adding a reference to the app in Request and Response makes testing trickier - right now I've managed to create a small mocking library for Request and Response that does not have to resort to unsafe anywhere (https://github.com/reem/iron-test) but an app reference may make that trickier.

@brycefisher
Copy link
Contributor

Ugh -- that would suck. Hmmm, maybe we could check for an environmental variable (IRON_VIEWS) and fallback to a default directory for templates (./views). I'm not sure if I'm in love with this idea, but it should prevent any unsafe could or references back to Iron in res or req...

I saw that repo. I'm super excited to play with that.

@reem
Copy link
Member Author

reem commented Aug 10, 2014

There's way too much of std::mem::unitialized in our tests right now, so that would be great to fix.

I'd really rather not fall back to an environment variable. Maybe, instead of a global persistent reference to Iron, we could just have a global persistent reference to some sort of global preferences object that only coincidentally happens to be stored on Iron.

I think that we'll find, however, that there may be a need for a general-purpose Alloy-like structure on Response.

@brycefisher
Copy link
Contributor

Yeah, relying only on environment variables is wrong.

@reem
Copy link
Member Author

reem commented Aug 14, 2014

I think there needs to be a dedicated way to immutably access shared global configuration. More thoughts to come.

@reem reem added this to the V-1.0 milestone Aug 15, 2014
@reem
Copy link
Member Author

reem commented Aug 16, 2014

I think the solution is to include a config: Arc<AnyMap> in both Request and Response. This allows easy global configuration and is pretty easy to mock in tests.

@brycefisher
Copy link
Contributor

I think this is the most ergonomic solution. Would it make any difference if the config is accessed in Response or Request? It feels wrong to provide the same information in two places, but I can't think of any particular problems with this idea.

@reem reem added the ready label Aug 19, 2014
@reem
Copy link
Member Author

reem commented Aug 19, 2014

I think providing access to config in both places is perfectly fine.

@reem
Copy link
Member Author

reem commented Aug 28, 2014

The configuration problem is mostly solved through the Read variant in Persistent and the interface it exposes.

@Valve
Copy link
Contributor

Valve commented Sep 8, 2014

I don't know if this question is relevant, just curious if it's possible to compile a view as part of the build process. For example there is play! framework for Scala, where views are normal scala files, statically checked and compiled by the scala compiler (https://playframework.com/documentation/2.3.x/ScalaTemplates). I wonder if it's possible to do the same in Rust.

@reem
Copy link
Member Author

reem commented Sep 8, 2014

You can definitely do this with a syntax extension, or even without one if you are willing to lose a bit of expressiveness. However, I think that's probably the domain of a templating engine, not this middleware.

@aochagavia
Copy link

it could be interesting to look at the way Nickel has implemented mustache support

@reem
Copy link
Member Author

reem commented Sep 20, 2014

@aochagavia They have done much of the work that we'd like to do, but I'd much rather be templating engine agnostic and support a richer interface - notably nickels .render only allows HashMap<&'static str, &'static str> which means that you can't render a template with dynamic data, like from a database.

@allan-simon
Copy link

as someone was noting for scala, in cppcms , a webframework in C++ (so in the same segment of "compiled language") the use template file , that get compiles into C++

basically the idea is that your template file is converted into a C++ class + a name , which is then added to a hashmap, then in your "controller" methods you just do

 render(content_structure,  "name_of_the_template")

content_structure being a plain struct, with the data you want to render in your template.

The advantage I see to this approach is

  • possibility to add dynamic data
  • compile time check, (the same as people of the posgresql rust library have created a compile time check of the SQL statement)
  • performance (as it's only compiled once)
  • still possible for corner case to inject "raw" rust code
    If that seems interesting, I can provide more details on how it is implemented as I've been working with this templating system quite extensively (and would interested to re implement it in Iron)

http://cppcms.com/wikipp/en/page/cppcms_1x_templates_gen
http://cppcms.com/wikipp/en/page/cppcms_1x_templates_comm

@brycefisher
Copy link
Contributor

@allan-simon sounds like this would really put us ahead of the node / ruby / python community by really leveraging all the best parts of rustc.

@reem
Copy link
Member Author

reem commented Oct 6, 2014

Agreed. There could be some really cool work to be done in making a crazy syntax extensions for creating templates.

@allan-simon
Copy link

I've finished a side project of mine these last days, and I've started today to take a look at syntax extensions, I will soon create a "toy" repository on my github to keep track on my progress on comprehension of "how to create complex syntax extensions" , so that at the end I could use to have a tutorial on how the machinery work, so that we don’t end up with an unmaintainable extension.

I will keep you updated on my progress for this, though I think it's going to take me at least a week or two before getting a "beginning of proof of concept", but things will get faster after a good grasp on this.

@reem
Copy link
Member Author

reem commented Oct 9, 2014

@allan-simon I also just recently started learning about syntax extensions. If you want an example of a (hopefully) well written one: https://github.com/reem/stainless

@allan-simon
Copy link

@reem Perfect it will help a lot.

@allan-simon
Copy link

Hello, just to keeps you updated, using your stainless extension and brain_fuck macro, I've started a toy html template extension and now i'm able to generate function to output html and to mix rust code in it

https://bpaste.net/show/62c46adedcb2

right now I have 2 tags

  • <% template name() %> which generate a function "name() -> String"
  • <% rust xxxxxx %> that permits to "inject" rust code inside a template, I've first implemented this one because it was IMHO the most complex one (the others will follow the same logic as <% template %>, and it will permit to have "workarounds" waiting for me to implement more specific tags (like <% if %> etc.

other things are treated as html and added to the output buffer returned by string

Right now I'm still making myself clear about what i want, and I will tell you when I got something that reflect my idea of the templating engine I want, so that we can start discussing about making change on it

@reem
Copy link
Member Author

reem commented Oct 19, 2014

@allan-simon I think it would probably be best to adopt an existing templating standard and possibly just make changes to it, rather than starting from scratch.

The most ideal situation is that I can create static or const templates which are aware of the types they must be passed to be rendered, then I can get nice type errors if I try to pass incompatible things as the pieces of the template.

@allan-simon
Copy link

actually as said, I'm basing the syntax/concept fully on the one of the "cppcms" framework, (which I think was also based on an existing one, as vim was able to put a nice syntaxic coloration) as I think it's closer to what I'm doing (i.e template transformed into native code at compilation time)

and yup as you said, the goal is to have the same garanty in the template as we have in rust, i.e if it compiles then you're sure it works (i.e syntax checking / type checking etc.)

@sunng87
Copy link
Contributor

sunng87 commented Jan 11, 2015

Hi, I have talked with @reem in IRC but also want to update here. I have been working on a handlebars template engine for rust and I'm going to create an iron middleware for integration.

By far, the handlebars-rust project is almost finished and should support most of handlebars features, helper customization and template inheritance. I'm now working on the middleware. Since it's still difficult to compile iron against rust nightly, I may still need several days for it.

As discussed with @reem, we will create a new Template struct contains template name and data you want to render. Template will implement Modifier for Response, so users can call response.set(template). The information will be stored in extensions and processed by middleware. I'm also going to keep it easy to test. So users can test their handlers by retrieving data from extensions (in test function).

@sunng87
Copy link
Contributor

sunng87 commented Jan 11, 2015

To answer some questions above, handlebars is a widely used template syntax created by Yokuda Hatz (yes, it's who made cargo also) originally in javascript. In handlebars-rust, there is a tricky point that you have to make the data implements ToJson so we can render it. Because in handlebars, there's if/else, each and some other custom flow controls, data are not only for rendering, but also to control rendering. So I can't just use a "Show" trait or String/&str type for it. Lucky enough, most common data structure implements ToJson, so we can use it without (too much) pain.

@reem
Copy link
Member Author

reem commented Jan 11, 2015

Just a note, but its Yehuda Katz, aka wycats. :)

@sunng87
Copy link
Contributor

sunng87 commented Jan 12, 2015

Oh, sorry for wycats.

@sunng87
Copy link
Contributor

sunng87 commented Jan 19, 2015

The middleware handlebars-iron is done and available from crates.io

@untitaker
Copy link
Member

I think this can be closed since handlebars-iron seems to work fairly well.

@reem reem removed the ready label Oct 18, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants