From 57af8f473b54e19c1cdf6117d879a080e50e206a Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Sun, 6 Sep 2020 22:59:57 -0700 Subject: [PATCH 1/2] Make #each handle non-iterable values like the original JS library. Previously things like `{{#each "foo"}}` were render errors. Now non-iterable values are treated like empty iterables. This brings handlebars-rust's behavior closer in line with the original JavaScript library. Here are the two new test cases in the handlebars playground: 1. https://handlebarsjs.com/playground.html#format=1¤tExample=%7B%22template%22%3A%22%7B%7B%23each%20this%7D%7Deach%20block%7B%7Belse%7D%7Delse%20block%7B%7B%2Feach%7D%7D%22%2C%22partials%22%3A%5B%5D%2C%22input%22%3A%22%5C%22strings%20aren't%20iterable%5C%22%22%2C%22output%22%3A%22else%20block%22%2C%22preparationScript%22%3A%22%22%2C%22handlebarsVersion%22%3A%224.7.6%22%7D 2. https://handlebarsjs.com/playground.html#format=1¤tExample=%7B%22template%22%3A%22%7B%7B%23*inline%20%5C%22walk%5C%22%7D%7D(%7B%7B%23each%20this%7D%7D%7B%7B%23if%20%40key%7D%7D%7B%7B%40key%7D%7D%7B%7Belse%7D%7D%7B%7B%40index%7D%7D%7B%7B%2Fif%7D%7D%3A%20%7B%7Bthis%7D%7D%20%7B%7B%3E%20walk%20this%7D%7D%2C%20%7B%7B%2Feach%7D%7D)%7B%7B%2Finline%7D%7D%5Cn%7B%7B%3E%20walk%7D%7D%22%2C%22partials%22%3A%5B%5D%2C%22input%22%3A%22%7B%5Cn%20%20%20%20%5C%22array%5C%22%3A%20%5B42%2C%20%7B%5C%22wow%5C%22%3A%20%5C%22cool%5C%22%7D%2C%20%5B%5B%5D%5D%5D%2C%5Cn%20%20%20%20%5C%22object%5C%22%3A%20%7B%20%5C%22a%5C%22%3A%20%7B%20%5C%22b%5C%22%3A%20%5C%22c%5C%22%2C%20%5C%22d%5C%22%3A%20%5B%5C%22e%5C%22%5D%20%7D%20%7D%2C%5Cn%20%20%20%20%5C%22string%5C%22%3A%20%5C%22hi%5C%22%5Cn%7D%22%2C%22output%22%3A%22%5Cn(array%3A%2042%2C%5Bobject%20Object%5D%2C%20(0%3A%2042%20()%2C%201%3A%20%5Bobject%20Object%5D%20(wow%3A%20cool%20()%2C%20)%2C%202%3A%20%20(0%3A%20%20()%2C%20)%2C%20)%2C%20object%3A%20%5Bobject%20Object%5D%20(a%3A%20%5Bobject%20Object%5D%20(b%3A%20c%20()%2C%20d%3A%20e%20(0%3A%20e%20()%2C%20)%2C%20)%2C%20)%2C%20string%3A%20hi%20()%2C%20)%22%2C%22preparationScript%22%3A%22%22%2C%22handlebarsVersion%22%3A%224.7.6%22%7D --- src/helpers/helper_each.rs | 55 ++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/helpers/helper_each.rs b/src/helpers/helper_each.rs index 9958bbd93..081436022 100644 --- a/src/helpers/helper_each.rs +++ b/src/helpers/helper_each.rs @@ -136,16 +136,12 @@ impl HelperDef for EachHelper { rc.pop_block(); Ok(()) } - (false, _) => { + _ => { if let Some(else_template) = h.inverse() { else_template.render(r, ctx, rc, out)?; } Ok(()) } - _ => Err(RenderError::new(format!( - "Param type is not iterable: {:?}", - value.value() - ))), }, None => Ok(()), } @@ -479,4 +475,53 @@ mod test { let rendered = reg.render_template(template, &input).unwrap(); assert_eq!("01", rendered); } + + #[test] + fn test_non_iterable() { + let reg = Registry::new(); + let template = "{{#each this}}each block{{else}}else block{{/each}}"; + let input = json!("strings aren't iterable"); + let rendered = reg.render_template(template, &input).unwrap(); + assert_eq!("else block", rendered); + } + + #[test] + fn test_recursion() { + let mut reg = Registry::new(); + assert!(reg + .register_template_string( + "walk", + "(\ + {{#each this}}\ + {{#if @key}}{{@key}}{{else}}{{@index}}{{/if}}: \ + {{this}} \ + {{> walk this}}, \ + {{/each}}\ + )", + ) + .is_ok()); + + let input = json!({ + "array": [42, {"wow": "cool"}, [[]]], + "object": { "a": { "b": "c", "d": ["e"] } }, + "string": "hi" + }); + let expected_output = "(\ + array: [42, [object], [[], ], ] (\ + 0: 42 (), \ + 1: [object] (wow: cool (), ), \ + 2: [[], ] (0: [] (), ), \ + ), \ + object: [object] (\ + a: [object] (\ + b: c (), \ + d: [e, ] (0: e (), ), \ + ), \ + ), \ + string: hi (), \ + )"; + + let rendered = reg.render("walk", &input).unwrap(); + assert_eq!(expected_output, rendered); + } } From 5930fdd7132407047982557572887e6a61ea9717 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Tue, 8 Sep 2020 10:26:33 -0700 Subject: [PATCH 2/2] Readability improvement for #each. --- src/helpers/helper_each.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/helpers/helper_each.rs b/src/helpers/helper_each.rs index 081436022..48d7b15e2 100644 --- a/src/helpers/helper_each.rs +++ b/src/helpers/helper_each.rs @@ -5,7 +5,7 @@ use crate::block::{BlockContext, BlockParams}; use crate::context::Context; use crate::error::RenderError; use crate::helpers::{HelperDef, HelperResult}; -use crate::json::value::{to_json, JsonTruthy}; +use crate::json::value::to_json; use crate::output::Output; use crate::registry::Registry; use crate::render::{Helper, RenderContext, Renderable}; @@ -79,8 +79,8 @@ impl HelperDef for EachHelper { let template = h.template(); match template { - Some(t) => match (value.value().is_truthy(false), value.value()) { - (true, &Json::Array(ref list)) => { + Some(t) => match *value.value() { + Json::Array(ref list) if !list.is_empty() => { let block_context = create_block(&value)?; rc.push_block(block_context); @@ -108,7 +108,7 @@ impl HelperDef for EachHelper { rc.pop_block(); Ok(()) } - (true, &Json::Object(ref obj)) => { + Json::Object(ref obj) if !obj.is_empty() => { let block_context = create_block(&value)?; rc.push_block(block_context);