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

Helper gets called twice if it returns "false" as a subexpression in with/if #435

Closed
ilya-zlobintsev opened this issue May 21, 2021 · 4 comments
Milestone

Comments

@ilya-zlobintsev
Copy link

When using a custom helper in a subexpression and it returns "false" (as interpreted by the with and if helpers, such as when the helper returns an empty object), it gets called twice.

Here's a reproducible example of this behaviour:

use handlebars::{Context, Handlebars, Helper, HelperDef, RenderContext, RenderError, ScopedJson};
use serde_json::json;

fn main() {
    let mut registry = Handlebars::new();

    registry.register_helper("myhelper", Box::new(MyHelper {}));

    println!("{}", registry
        .render_template("{{#if (myhelper a)}}something{{else}}nothing{{/if}}", &TemplateContext {}) // If returns true
        .unwrap());

    println!("{}", registry
        .render_template("{{#if (myhelper)}}something{{else}}nothing{{/if}}", &TemplateContext {}) // If returns false
        .unwrap());

}

struct MyHelper;

impl HelperDef for MyHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        _: &'reg Handlebars,
        _: &'rc Context,
        _: &mut RenderContext<'reg, 'rc>,
    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        println!("Called helper");
        if let Some(_) = h.param(0) {
            Ok(Some(ScopedJson::Derived(json!({
                "a": 1,
            }))))
        } else {
            Ok(None)
        }
    }
}

#[derive(serde::Serialize)]
struct TemplateContext;

In this scenario, the helper returns an object when a parameter is passed to it, and nothing otherwise.

When the helper returns an object (so it's understood as "true" by the if helper), everything works as expected. However, when the if check fails, the helper is called twice. The entire output of the program looks like:

Called helper
something
Called helper
Called helper
nothing

The same behaviour occurs with a with helper as well, and regardless of if there's an else block or not.

I know that this is not a problem in most cases (as helpers don't usually have demanding code), and my use case (making network requests from within helpers) is not typical, but I think fixing this would be good for performance in general use cases such as HTML templating as well.

@sunng87
Copy link
Owner

sunng87 commented May 22, 2021

This happens when call_inner returns Ok(None) we treated it as default implementation. To workaround this, you can return Ok(Some(json!(null).into())) for your case.

I will think about to fix this via API change or documentation.

@sunng87
Copy link
Owner

sunng87 commented May 23, 2021

@ilyazzz In #437 I will change the return type of call_inner and remove the Option to avoid misleading of None returns. I wish this is the latest breaking change before 4.0 release.

@ilya-zlobintsev
Copy link
Author

@sunng87 thanks. Could it be documented that it's possible to return a json!(null) value from the helper?

@sunng87
Copy link
Owner

sunng87 commented May 25, 2021

docstring has been updated on master

@sunng87 sunng87 closed this as completed May 25, 2021
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

2 participants