tera icon indicating copy to clipboard operation
tera copied to clipboard

Dynamic Template Loading

Open poupryc opened this issue 4 years ago • 14 comments

Hi

I'm trying to build a collaborative wiki and I'd like to use Tera's template system. However some problems arise and I wonder if Tera can be used in this kind of use case.

The system is quite simple: a user can write a page that will be registered on a database (we are doing this because we are going to have a lot of pages). What I'm trying to build is the rendering server, which will fetch the template from the database and send the result of the rendering.

The first problem is that I don't have control over the template cache system: I'd like to implement a custom LFU strategy. This is currently impossible because even if Template is exposed, Renderer is not.

I'm imagining something that would look like this:

use tera::{Context, Renderer, Template, Tera};

fn main() {
  // I use Tera for everything that is static and won't (probably) change like macro, base template...
  let tera = Tera::new("./template/**/*.html").unwrap();
  let name = "my-custom-page-slug-to-render";
  // database
  let raw = fetch(&name);

  let result = {
    let template = get_cached_template_or_generate_a_new_one(&name, &raw);
    Renderer::new(template, &tera, Context::default())
      .render()
      .unwrap()
  };

  send(result);
}

Is that possible? Other problems appear in a more advanced implementation (module definition by users) but I want to discuss custom rendering first: I know that Template is only exported for Benchmarks etc, but isn't the use legitimate here?

Thanks,

poupryc avatar Jan 14 '20 21:01 poupryc

Isn't that https://tera.netlify.com/docs/#loading-templates-from-strings ?

Keats avatar Jan 15 '20 14:01 Keats

I have seen this but the problem is that I already know that we are going to have tens of thousands of pages in the database written by our users (this is already the case in the current system).

UPDATE: I updated the code in my previous comment, maybe it can be helpfull.

poupryc avatar Jan 15 '20 15:01 poupryc

If I understand correctly, you have some custom templates, a main Tera instance and you want to be able to:

  • add a raw template: already doable
  • remove templates: not doable currently

In get_cached_template_or_generate_a_new_one, where/what would the cache be? Templates have to be added to a Tera instance to check for inheritance so your code sample won't work. If the user templates don't use inheritance or anything like that, do they use anything from the Tera instance outside of the default filters/tests/functions?

Keats avatar Jan 17 '20 19:01 Keats

Hi,

I don't know if I understood your message correctly. I will try to answer as accurately as possible. The purpose of using Tera in my case is to let the user write dynamic pages with Tera syntax. These pages are stored in the database and when a user will request a page from the system, the system will create the template and render it.

So from your comment I suppose this might be possible.

use tera::{Context, Renderer, Template, Tera};

fn main() {
    // I use Tera for everything that is static and won't (probably) change like macro, base template...
    let tera = Tera::new("./template/**/*.html").unwrap();
    let name = "my-custom-page-slug-to-render";
    // database
    let raw = fetch(&name);

    let result = {
        let template = get_cached_template_or_generate_a_new_one(&name, &raw, &tera);
        Renderer::new(template, &tera, &Context::default())
            .render()
            .unwrap()
    };

    send(result);
}

fn get_cached_template_or_generate_a_new_one(name: &str, raw: &str, tera: &Tera) -> Template {
    // check cache w/ name
    // if cached template is found: return it

    // else create new Template w/ `tera` as Tera instance
    // save it into the cache
    // return it
}

the user will only be able to use include for static templates already compiled via Tera::new Have I answered your questions?

Thanks,

poupryc avatar Jan 20 '20 18:01 poupryc

The thing I don't understand is what is the cache there? Are you caching the output of Tera::render or the template itself? If it's just a LRU on the template themselves, I think adding a remove_template fn would allow you to build your own cache right?

Keats avatar Jan 28 '20 05:01 Keats

It might work, but I think we'll test a basic LRU first and then go for a custom eviction strategy like a LFU that might require exposing the Renderer. Using the basic map hash might be too simplistic I think.

poupryc avatar Jan 30 '20 17:01 poupryc

@Keats Hello

Following the last updates, I found a workaround with render_str, but this implies that Tera compiles the same template several times sometimes. Isn't it still possible to expose Renderer? It would make a good addition in conjunction with render_str.

poupryc avatar Mar 27 '20 18:03 poupryc

I don't think the internals will be exposed as that would mean I can't do any breaking changes to it anymore.

Keats avatar Mar 27 '20 19:03 Keats

Why wouldn't Render have the same statue as Template, which is exposed? I have to seem insistent, but for me it's all that Tera needs to be performant server side with dynamic data.

poupryc avatar Mar 27 '20 19:03 poupryc

Template is public but considered private api (hidden from docs), Renderer could be made the same but that means that it could have breaking changes in any releases, without even being mentioned in the changelog.

Keats avatar Mar 30 '20 07:03 Keats

I'm okay with that. Since no one before me had asked for this API, I think leaving it undocumented and unversioned is a good compromise.

poupryc avatar Mar 30 '20 08:03 poupryc

Hi, during lockdown I'd like to do some work on Tera's server rendering w/ template in database. Is it still possible to publish Render?

poupryc avatar May 09 '20 15:05 poupryc

I would take a PR for it

Keats avatar May 11 '20 08:05 Keats

I also struct this problem. After I've got a template value, e.g.: let template: &Template = tera.get_template(format!("index.{lang}.html").as_str()) .unwrap_or( tera.get_template("index.html")? );

How can I render it?

shestero avatar Oct 25 '23 09:10 shestero