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

Context for global functions #299

Closed
max-frai opened this issue Jun 4, 2018 · 11 comments
Closed

Context for global functions #299

max-frai opened this issue Jun 4, 2018 · 11 comments
Labels

Comments

@max-frai
Copy link
Contributor

max-frai commented Jun 4, 2018

Hello, I use fluent-rs for internationalization.
For now tera supports to register only global static functions without context which help process incoming data, but what if I need some additional context data?
In template:
{{ global_lang(key_name) }}
In code:
context.add("key_name", "localization-key-name")

So I want to call global_lang with localization-key-name string as argument and inside access external object which stores all locale keys, etc and return result string data.

@max-frai
Copy link
Contributor Author

max-frai commented Jun 4, 2018

Seems like this is related #219

@Keats
Copy link
Owner

Keats commented Jun 6, 2018

I'd be happy to have a PR for #219, especially if it can fix your issues

@Keats Keats mentioned this issue Sep 6, 2018
16 tasks
@Keats Keats added the done label Jan 4, 2019
@Keats
Copy link
Owner

Keats commented Jan 12, 2019

An issue still: the filter/tester/functions with context requires a 'static lifetime in fn register_function<F: Function + 'static> (same for the others).
It is required but that means you cannot have pointers in the context really because of that. This means you need to clone whatever you wanted the context to be, which can be terrible perf wise like in Zola where the context for that function can get huge.

cc @andy128k

@andy128k
Copy link
Contributor

Arc to the rescue. This example shows how to deal with it. What do I miss here?

@Keats
Copy link
Owner

Keats commented Jan 12, 2019

Next doesn't contain a pointer. Here is an example of a Zola fn:

#[derive(Debug)]
pub struct GetPage<'a> {
    pages: Arc<HashMap<&'a String, SerializingPage<'a>>>,
}
impl<'a> GetPage<'a> {
    pub fn new(library: &'a Library) -> Self {
        let mut pages = HashMap::new();
        for page in library.pages_values() {
            pages.insert(
                &page.file.relative,
                library.get_page(&page.file.path).unwrap().to_serialized(library),
            );
        }

        Self {pages: Arc::new(pages)}
    }
}
impl<'a> Function for GetPage<'a> {
    fn call(&self, args: &HashMap<String, Value>) -> Result<Value> {
        let path = required_arg!(
            String,
            args.get("path"),
            "`get_page` requires a `path` argument with a string value"
        );
        match self.pages.get(&path) {
            Some(p) => Ok(to_value(p).unwrap()),
            None => Err(format!("Page `{}` not found.", path).into()),
        }
    }
}

Trying to register it gives a conflicting lifetime error because:

    pub fn register_tera_global_fns(&mut self) {
        self.tera.register_function("trans", global_fns::Trans::new(self.config.clone()));
        self.tera.register_function("get_page", global_fns::GetPage::new(&self.library));
}

The lifetime of &self.library will end and won't outlive the 'static. I don't know if there is a way to fix that tbh

@Keats
Copy link
Owner

Keats commented Jan 13, 2019

If we can tie the lifetime to the traits to the Tera instance instead of 'static, it might work?

@andy128k andy128k mentioned this issue Jan 13, 2019
@andy128k
Copy link
Contributor

Giving a lifetime to an instance of Tera could help. Have a look at my attempt here #378 . I don't know how it fits into Zola's code, but I'm afraid with references ergonomics could be worse.

@Keats Keats closed this as completed Dec 7, 2019
@technic
Copy link

technic commented Jul 31, 2020

Hi! Why do we need to use an external variable and the synchronization for this? Can't we pass reference to Context inside each function?

Synchronization will mean, that we can not render templates on web server in parallel. Because each client needs different value of the global variable. (for example different languages).

@Keats
Copy link
Owner

Keats commented Jul 31, 2020

Are you referring to the content of that issue or the current version?

@technic
Copy link

technic commented Jul 31, 2020

Sorry, I didn't understand your question. I mean tera::Context.

Basically I want something like this:

<p> translate("foobar") <p>
let mut ctx = Context::new();
ctx.insert("LANG", "en");
t.render(template_name, ctx)

so basically lambdas that we use in register_function in addition to HashMap with arguments also accepts &Context reference.
Then I can know value of "LANG" inside translate implementation.

Upd. Is there a better solution for this use case in your latest release?

@technic
Copy link

technic commented Aug 1, 2020

@Keats I created a new issue with more explanation #543.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants