diff --git a/examples/template.hbs b/examples/template.hbs
index 2476531c1..b6817adb1 100644
--- a/examples/template.hbs
+++ b/examples/template.hbs
@@ -5,10 +5,10 @@
CSL {{year}}
- {{#each teams~}}
+ {{#each teams as |t| ~}}
-
{{~log @index~}}
- {{name}}: {{format pts ~}}
+ {{t.name}}: {{format t.pts ~}}
{{/each~}}
diff --git a/src/context.rs b/src/context.rs
index 0528886ae..f9527fc68 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -19,7 +19,7 @@ pub type Object = BTreeMap;
/// The context wrap data you render on your templates.
///
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct Context {
data: Json,
}
@@ -178,6 +178,20 @@ impl JsonRender for Json {
}
}
+#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
+pub fn to_json(src: &T) -> Json
+ where T: ToJson
+{
+ src.to_json()
+}
+
+#[cfg(feature = "serde_type")]
+pub fn to_json(src: &T) -> Json
+ where T: Serialize
+{
+ value::to_value(src)
+}
+
impl JsonTruthy for Json {
fn is_truthy(&self) -> bool {
match *self {
diff --git a/src/grammar.rs b/src/grammar.rs
index 6d3803433..a5b2ff3d9 100644
--- a/src/grammar.rs
+++ b/src/grammar.rs
@@ -32,9 +32,10 @@ impl_rdp! {
reference = @{ identifier ~ (["["] ~ (string_literal|['0'..'9']+) ~ ["]"])* ~ ["-"]* ~ reference* }
name = _{ subexpression | reference }
- param = { literal | reference | subexpression }
+ param = { !["as"] ~ (literal | reference | subexpression) }
hash = { identifier ~ ["="] ~ param }
- exp_line = _{ identifier ~ (hash|param)* }
+ block_param = { ["as"] ~ ["|"] ~ identifier ~ identifier? ~ ["|"]}
+ exp_line = _{ identifier ~ (hash|param)* ~ block_param?}
subexpression = { ["("] ~ name ~ (hash|param)* ~ [")"] }
@@ -243,7 +244,9 @@ fn test_helper_start() {
"{{#if []}}",
"{{#if {}}}",
"{{#if}}",
- "{{~#if hello~}}"];
+ "{{~#if hello~}}",
+ "{{#each people as |person|}}",
+ "{{#each-obj obj as |key val|}}"];
for i in s.iter() {
let mut rdp = Rdp::new(StringInput::new(i));
assert!(rdp.helper_block_start());
@@ -287,3 +290,13 @@ fn test_raw_block() {
assert!(rdp.end());
}
}
+
+#[test]
+fn test_block_param() {
+ let s = vec!["as |person|", "as |key val|"];
+ for i in s.iter() {
+ let mut rdp = Rdp::new(StringInput::new(i));
+ assert!(rdp.block_param());
+ assert!(rdp.end());
+ }
+}
diff --git a/src/helpers/helper_each.rs b/src/helpers/helper_each.rs
index e5f2f9327..adb531efc 100644
--- a/src/helpers/helper_each.rs
+++ b/src/helpers/helper_each.rs
@@ -1,19 +1,19 @@
+use std::collections::BTreeMap;
+
#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
-use serialize::json::{Json, ToJson};
+use serialize::json::Json;
#[cfg(feature = "serde_type")]
-use serde_json::value::{self, Value as Json};
-
+use serde_json::value::Value as Json;
use helpers::HelperDef;
use registry::Registry;
-use context::{Context, JsonTruthy};
+use context::{Context, JsonTruthy, to_json};
use render::{Renderable, RenderContext, RenderError, Helper};
#[derive(Clone, Copy)]
pub struct EachHelper;
impl HelperDef for EachHelper {
- #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
fn call(&self,
c: &Context,
h: &Helper,
@@ -39,9 +39,9 @@ impl HelperDef for EachHelper {
let len = list.len();
for i in 0..len {
let mut local_rc = rc.derive();
- local_rc.set_local_var("@first".to_string(), (i == 0usize).to_json());
- local_rc.set_local_var("@last".to_string(), (i == len - 1).to_json());
- local_rc.set_local_var("@index".to_string(), i.to_json());
+ local_rc.set_local_var("@first".to_string(), to_json(&(i == 0usize)));
+ local_rc.set_local_var("@last".to_string(), to_json(&(i == len - 1)));
+ local_rc.set_local_var("@index".to_string(), to_json(&i));
if let Some(inner_path) = value.path() {
let new_path = format!("{}/{}.[{}]",
@@ -49,95 +49,20 @@ impl HelperDef for EachHelper {
inner_path,
i);
debug!("each path {:?}", new_path);
- local_rc.set_path(new_path);
+ local_rc.set_path(new_path.clone());
}
- try!(t.render(c, r, &mut local_rc));
- }
- Ok(())
- }
- (true, &Json::Object(ref obj)) => {
- let mut first: bool = true;
- for k in obj.keys() {
- let mut local_rc = rc.derive();
- local_rc.set_local_var("@first".to_string(), first.to_json());
- if first {
- first = false;
- }
-
- local_rc.set_local_var("@key".to_string(), k.to_json());
- if let Some(inner_path) = value.path() {
- let new_path = format!("{}/{}.[{}]",
- local_rc.get_path(),
- inner_path,
- k);
- local_rc.set_path(new_path);
+ if let Some(block_param) = h.block_param() {
+ let mut map = BTreeMap::new();
+ map.insert(block_param.to_string(), to_json(&list[i]));
+ local_rc.push_block_context(&map);
}
try!(t.render(c, r, &mut local_rc));
- }
-
- Ok(())
- }
- (false, _) => {
- if let Some(else_template) = h.inverse() {
- try!(else_template.render(c, r, rc));
- }
- Ok(())
- }
- _ => {
- Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
- }
- };
- rc.demote_local_vars();
- rendered
- }
- None => Ok(()),
- }
- }
-
- #[cfg(feature = "serde_type")]
- fn call(&self,
- c: &Context,
- h: &Helper,
- r: &Registry,
- rc: &mut RenderContext)
- -> Result<(), RenderError> {
- let value = try!(h.param(0)
- .ok_or_else(|| RenderError::new("Param not found for helper \"each\"")));
-
- let template = h.template();
-
- match template {
- Some(t) => {
- rc.promote_local_vars();
- if let Some(path_root) = value.path_root() {
- let local_path_root = format!("{}/{}", rc.get_path(), path_root);
- rc.set_local_path_root(local_path_root);
- }
-
- debug!("each value {:?}", value.value());
- let rendered = match (value.value().is_truthy(), value.value()) {
- (true, &Json::Array(ref list)) => {
- let len = list.len();
- for i in 0..len {
- let mut local_rc = rc.derive();
- local_rc.set_local_var("@first".to_string(),
- value::to_value(&(i == 0usize)));
- local_rc.set_local_var("@last".to_string(),
- value::to_value(&(i == len - 1)));
- local_rc.set_local_var("@index".to_string(), value::to_value(&i));
-
- if let Some(inner_path) = value.path() {
- let new_path = format!("{}/{}.[{}]",
- local_rc.get_path(),
- inner_path,
- i);
- debug!("each value {:?}", new_path);
- local_rc.set_path(new_path);
+ if h.block_param().is_some() {
+ local_rc.pop_block_context();
}
- try!(t.render(c, r, &mut local_rc));
}
Ok(())
}
@@ -145,22 +70,33 @@ impl HelperDef for EachHelper {
let mut first: bool = true;
for k in obj.keys() {
let mut local_rc = rc.derive();
- local_rc.set_local_var("@first".to_string(), value::to_value(&first));
+ local_rc.set_local_var("@first".to_string(), to_json(&first));
if first {
first = false;
}
- local_rc.set_local_var("@key".to_string(), value::to_value(&k));
+ local_rc.set_local_var("@key".to_string(), to_json(k));
+
if let Some(inner_path) = value.path() {
let new_path = format!("{}/{}.[{}]",
local_rc.get_path(),
inner_path,
k);
- debug!("each value {:?}", new_path);
local_rc.set_path(new_path);
}
+ if let Some((bp_key, bp_val)) = h.block_param_pair() {
+ let mut map = BTreeMap::new();
+ map.insert(bp_key.to_string(), to_json(k));
+ map.insert(bp_val.to_string(), to_json(obj.get(k).unwrap()));
+ local_rc.push_block_context(&map);
+ }
+
try!(t.render(c, r, &mut local_rc));
+
+ if h.block_param().is_some() {
+ local_rc.pop_block_context();
+ }
}
Ok(())
@@ -175,7 +111,7 @@ impl HelperDef for EachHelper {
Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
}
};
- // rc.set_path(path);
+
rc.demote_local_vars();
rendered
}
@@ -345,4 +281,33 @@ mod test {
assert_eq!(r1, "empty");
}
+ #[test]
+ fn test_block_param() {
+ let t0 = Template::compile("{{#each a as |i|}}{{i}}{{/each}}".to_owned()).unwrap();
+ let mut handlebars = Registry::new();
+ handlebars.register_template("t0", t0);
+ let m1 = btreemap! {
+ "a".to_string() => vec![1,2,3,4,5]
+ };
+ let r0 = handlebars.render("t0", &m1).unwrap();
+ println!("render: {}", r0);
+ assert_eq!(r0, "12345");
+ }
+
+ #[test]
+ fn test_each_object_block_param() {
+ let t0 = Template::compile("{{#each this as |k v|}}{{#with k as |inner_k|}}{{inner_k}}{{/with}}:{{v}}|{{/each}}".to_string())
+ .ok()
+ .unwrap();
+
+ let mut handlebars = Registry::new();
+ handlebars.register_template("t0", t0);
+
+ let m = btreemap!{
+ "ftp".to_string() => 21,
+ "http".to_string() => 80
+ };
+ let r0 = handlebars.render("t0", &m);
+ assert_eq!(r0.ok().unwrap(), "ftp:21|http:80|".to_string());
+ }
}
diff --git a/src/helpers/helper_with.rs b/src/helpers/helper_with.rs
index c2c6af885..36683cd5e 100644
--- a/src/helpers/helper_with.rs
+++ b/src/helpers/helper_with.rs
@@ -1,6 +1,8 @@
+use std::collections::BTreeMap;
+
use helpers::HelperDef;
use registry::Registry;
-use context::{Context, JsonTruthy};
+use context::{Context, JsonTruthy, to_json};
use render::{Renderable, RenderContext, RenderError, Helper};
#[derive(Clone, Copy)]
@@ -35,6 +37,12 @@ impl HelperDef for WithHelper {
let new_path = format!("{}/{}", path, inner_path);
rc.set_path(new_path);
}
+
+ if let Some(block_param) = h.block_param() {
+ let mut map = BTreeMap::new();
+ map.insert(block_param.to_string(), to_json(param.value()));
+ rc.push_block_context(&map);
+ }
}
let rendered = match template {
@@ -44,6 +52,9 @@ impl HelperDef for WithHelper {
rc.set_path(path);
rc.demote_local_vars();
+ if not_empty {
+ rc.pop_block_context();
+ }
rendered
}
}
@@ -129,7 +140,47 @@ mod test {
}
#[test]
- fn test_with_in_each(){
+ fn test_with_block_param() {
+ let addr = Address {
+ city: "Beijing".to_string(),
+ country: "China".to_string(),
+ };
+
+ let person = Person {
+ name: "Ning Sun".to_string(),
+ age: 27,
+ addr: addr,
+ titles: vec!["programmer".to_string(), "cartographier".to_string()],
+ };
+
+ let t0 = Template::compile("{{#with addr as |a|}}{{a.city}}{{/with}}".to_string())
+ .ok()
+ .unwrap();
+ let t1 = Template::compile("{{#with notfound as |c|}}hello{{else}}world{{/with}}"
+ .to_string())
+ .ok()
+ .unwrap();
+ let t2 = Template::compile("{{#with addr/country as |t|}}{{t}}{{/with}}".to_string())
+ .ok()
+ .unwrap();
+
+ let mut handlebars = Registry::new();
+ handlebars.register_template("t0", t0);
+ handlebars.register_template("t1", t1);
+ handlebars.register_template("t2", t2);
+
+ let r0 = handlebars.render("t0", &person);
+ assert_eq!(r0.ok().unwrap(), "Beijing".to_string());
+
+ let r1 = handlebars.render("t1", &person);
+ assert_eq!(r1.ok().unwrap(), "world".to_string());
+
+ let r2 = handlebars.render("t2", &person);
+ assert_eq!(r2.ok().unwrap(), "China".to_string());
+ }
+
+ #[test]
+ fn test_with_in_each() {
let addr = Address {
city: "Beijing".to_string(),
country: "China".to_string(),
@@ -156,9 +207,18 @@ mod test {
let people = vec![person, person2];
- let t0 = Template::compile("{{#each this}}{{#with addr}}{{city}}{{/with}}{{/each}}".to_string()).ok().unwrap();
- let t1 = Template::compile("{{#each this}}{{#with addr}}{{../age}}{{/with}}{{/each}}".to_string()).ok().unwrap();
- let t2 = Template::compile("{{#each this}}{{#with addr}}{{@../index}}{{/with}}{{/each}}".to_string()).ok().unwrap();
+ let t0 = Template::compile("{{#each this}}{{#with addr}}{{city}}{{/with}}{{/each}}"
+ .to_string())
+ .ok()
+ .unwrap();
+ let t1 = Template::compile("{{#each this}}{{#with addr}}{{../age}}{{/with}}{{/each}}"
+ .to_string())
+ .ok()
+ .unwrap();
+ let t2 = Template::compile("{{#each this}}{{#with addr}}{{@../index}}{{/with}}{{/each}}"
+ .to_string())
+ .ok()
+ .unwrap();
let mut handlebars = Registry::new();
handlebars.register_template("t0", t0);
diff --git a/src/render.rs b/src/render.rs
index 63d111a0d..5431c824d 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -1,15 +1,17 @@
-use std::collections::{HashMap, BTreeMap};
+use std::collections::{HashMap, BTreeMap, VecDeque};
use std::error;
use std::fmt;
use std::io::Write;
use std::io::Error as IOError;
#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
-use serialize::json::Json;
+use serialize::json::{ToJson, Json};
#[cfg(feature = "serde_type")]
use serde_json::value::Value as Json;
+#[cfg(feature = "serde_type")]
+use serde::ser::Serialize as ToJson;
-use template::{Template, TemplateElement, Parameter, HelperTemplate, TemplateMapping};
+use template::{Template, TemplateElement, Parameter, HelperTemplate, TemplateMapping, BlockParam};
use template::TemplateElement::{RawString, Expression, Comment, HelperBlock, HTMLExpression,
HelperExpression};
use registry::Registry;
@@ -75,6 +77,7 @@ pub struct RenderContext<'a> {
local_path_root: Option,
local_variables: HashMap,
default_var: Json,
+ block_context: VecDeque,
/// the `Write` where page is generated
pub writer: &'a mut Write,
/// current template name
@@ -93,6 +96,7 @@ impl<'a> RenderContext<'a> {
local_path_root: None,
local_variables: HashMap::new(),
default_var: Json::Null,
+ block_context: VecDeque::new(),
writer: w,
current_template: None,
root_template: None,
@@ -108,6 +112,7 @@ impl<'a> RenderContext<'a> {
local_path_root: self.local_path_root.clone(),
local_variables: self.local_variables.clone(),
default_var: self.default_var.clone(),
+ block_context: self.block_context.clone(),
writer: w,
current_template: self.current_template.clone(),
root_template: self.root_template.clone(),
@@ -122,6 +127,7 @@ impl<'a> RenderContext<'a> {
local_path_root: self.local_path_root.clone(),
local_variables: self.local_variables.clone(),
default_var: self.default_var.clone(),
+ block_context: self.block_context.clone(),
writer: self.writer,
current_template: self.current_template.clone(),
root_template: self.root_template.clone(),
@@ -192,16 +198,34 @@ impl<'a> RenderContext<'a> {
self.local_variables = new_map;
}
- pub fn get_local_var(&self, name: &String) -> &Json {
- match self.local_variables.get(name) {
- Some(j) => j,
- None => &self.default_var,
- }
+ pub fn get_local_var(&self, name: &String) -> Option<&Json> {
+ self.local_variables.get(name)
}
pub fn writer(&mut self) -> &mut Write {
self.writer
}
+
+ pub fn push_block_context(&mut self, ctx: &T)
+ where T: ToJson
+ {
+ self.block_context.push_front(Context::wraps(ctx));
+ }
+
+ pub fn pop_block_context(&mut self) {
+ self.block_context.pop_front();
+ }
+
+ pub fn evaluate_in_block_context(&self, local_path: &str) -> Option<&Json> {
+ for bc in self.block_context.iter() {
+ let v = bc.navigate(".", local_path);
+ if !v.is_null() {
+ return Some(v);
+ }
+ }
+
+ None
+ }
}
impl<'a> fmt::Debug for RenderContext<'a> {
@@ -249,6 +273,7 @@ pub struct Helper<'a> {
name: &'a str,
params: Vec,
hash: BTreeMap,
+ block_param: &'a Option,
template: &'a Option,
inverse: &'a Option,
block: bool,
@@ -276,6 +301,7 @@ impl<'a, 'b> Helper<'a> {
name: &ht.name,
params: evaluated_params,
hash: evaluated_hash,
+ block_param: &ht.block_param,
template: &ht.template,
inverse: &ht.inverse,
block: ht.block,
@@ -309,24 +335,37 @@ impl<'a, 'b> Helper<'a> {
/// Returns the default inner template if any
pub fn template(&self) -> Option<&Template> {
- match *self.template {
- Some(ref t) => Some(t),
- None => None,
- }
+ (*self.template).as_ref().map(|t| t)
}
/// Returns the template of `else` branch if any
pub fn inverse(&self) -> Option<&Template> {
- match *self.inverse {
- Some(ref t) => Some(t),
- None => None,
- }
+ (*self.inverse).as_ref().map(|t| t)
}
/// Returns if the helper is a block one `{{#helper}}{{/helper}}` or not `{{helper 123}}`
pub fn is_block(&self) -> bool {
self.block
}
+
+ /// Returns block param if any
+ pub fn block_param(&self) -> Option<&str> {
+ if let Some(BlockParam::Single(Parameter::Name(ref s))) = *self.block_param {
+ Some(s)
+ } else {
+ None
+ }
+ }
+
+ /// Return block param pair (for example |key, val|) if any
+ pub fn block_param_pair(&self) -> Option<(&str, &str)> {
+ if let Some(BlockParam::Pair((Parameter::Name(ref s1), Parameter::Name(ref s2)))) =
+ *self.block_param {
+ Some((s1, s2))
+ } else {
+ None
+ }
+ }
}
pub trait Renderable {
@@ -346,22 +385,24 @@ impl Parameter {
-> Result {
match self {
&Parameter::Name(ref name) => {
- if name.starts_with("@") {
- Ok(ContextJson {
- path: None,
- value: rc.get_local_var(&name).clone(),
- })
- } else {
- let path = if name.starts_with("../") {
- rc.get_local_path_root()
- } else {
- rc.get_path()
- };
- Ok(ContextJson {
- path: Some(name.to_owned()),
- value: ctx.navigate(path, name).clone(),
- })
- }
+ Ok(rc.get_local_var(&name).map_or_else(|| {
+ let path = if name.starts_with("../") {
+ rc.get_local_path_root()
+ } else {
+ rc.get_path()
+ };
+ ContextJson {
+ path: Some(name.to_owned()),
+ value: rc.evaluate_in_block_context(name).map_or_else(|| {ctx.navigate(path, name).clone()}, |v| v.clone()),
+ }
+
+ },
+ |v| {
+ ContextJson {
+ path: None,
+ value: v.clone(),
+ }
+ }))
}
&Parameter::Literal(ref j) => {
Ok(ContextJson {
@@ -579,12 +620,12 @@ fn test_render_context_promotion_and_demotion() {
render_context.promote_local_vars();
- assert_eq!(render_context.get_local_var(&"@../index".to_string()),
+ assert_eq!(render_context.get_local_var(&"@../index".to_string()).unwrap(),
&0usize.to_json());
render_context.demote_local_vars();
- assert_eq!(render_context.get_local_var(&"@index".to_string()),
+ assert_eq!(render_context.get_local_var(&"@index".to_string()).unwrap(),
&0usize.to_json());
}
diff --git a/src/template.rs b/src/template.rs
index 94417fc4e..c83b1769c 100644
--- a/src/template.rs
+++ b/src/template.rs
@@ -53,11 +53,18 @@ impl Subexpression {
}
}
+#[derive(PartialEq, Clone, Debug)]
+pub enum BlockParam {
+ Single(Parameter),
+ Pair((Parameter, Parameter)),
+}
+
#[derive(PartialEq, Clone, Debug)]
pub struct ExpressionSpec {
pub name: Parameter,
pub params: Vec,
pub hash: BTreeMap,
+ pub block_param: Option,
pub omit_pre_ws: bool,
pub omit_pro_ws: bool,
}
@@ -74,6 +81,7 @@ pub struct HelperTemplate {
pub name: String,
pub params: Vec,
pub hash: BTreeMap,
+ pub block_param: Option,
pub template: Option,
pub inverse: Option,
pub block: bool,
@@ -85,6 +93,7 @@ impl<'a> From<&'a Subexpression> for HelperTemplate {
name: s.name.clone(),
params: s.params.clone(),
hash: s.hash.clone(),
+ block_param: None,
template: None,
inverse: None,
block: false,
@@ -224,6 +233,31 @@ impl Template {
Ok((key, value))
}
+ #[inline]
+ fn parse_block_param<'a>(source: &'a str,
+ it: &mut Peekable>>,
+ limit: usize)
+ -> Result {
+ let p1_name = it.next().unwrap();
+ // identifier
+ let p1 = source[p1_name.start..p1_name.end].to_owned();
+
+ let p2 = it.peek().and_then(|p2_name| {
+ if p2_name.end <= limit {
+ Some(source[p2_name.start..p2_name.end].to_owned())
+ } else {
+ None
+ }
+ });
+
+ if p2.is_some() {
+ it.next();
+ Ok(BlockParam::Pair((Parameter::Name(p1), Parameter::Name(p2.unwrap()))))
+ } else {
+ Ok(BlockParam::Single(Parameter::Name(p1)))
+ }
+ }
+
#[inline]
fn parse_expression<'a>(source: &'a str,
it: &mut Peekable>>,
@@ -233,6 +267,7 @@ impl Template {
let mut hashes: BTreeMap = BTreeMap::new();
let mut omit_pre_ws = false;
let mut omit_pro_ws = false;
+ let mut block_param = None;
if it.peek().unwrap().rule == Rule::pre_whitespace_omitter {
omit_pre_ws = true;
@@ -265,6 +300,9 @@ impl Template {
let (key, value) = try!(Template::parse_hash(source, it.by_ref(), end));
hashes.insert(key, value);
}
+ Rule::block_param => {
+ block_param = Some(try!(Template::parse_block_param(source, it.by_ref(), end)));
+ }
Rule::pro_whitespace_omitter => {
omit_pro_ws = true;
}
@@ -275,6 +313,7 @@ impl Template {
name: name,
params: params,
hash: hashes,
+ block_param: block_param,
omit_pre_ws: omit_pre_ws,
omit_pro_ws: omit_pro_ws,
})
@@ -339,6 +378,7 @@ impl Template {
name: exp.name.as_name().unwrap(),
params: exp.params,
hash: exp.hash,
+ block_param: exp.block_param,
block: true,
template: None,
inverse: None,
@@ -467,6 +507,7 @@ impl Template {
name: exp.name.as_name().unwrap(),
params: exp.params,
hash: exp.hash,
+ block_param: exp.block_param,
block: false,
template: None,
inverse: None,
@@ -761,9 +802,43 @@ fn test_template_mapping() {
}
#[test]
-
fn test_whitespace_elements() {
let c = Template::compile(" {{elem}}\n\t{{#if true}} \
{{/if}}\n{{{{raw}}}} {{{{/raw}}}}\n{{{{raw}}}}{{{{/raw}}}}\n");
assert_eq!(c.ok().unwrap().elements.len(), 9);
}
+
+#[test]
+fn test_block_param() {
+ match Template::compile("{{#each people as |person|}}{{person}}{{/each}}") {
+ Ok(t) => {
+ if let HelperBlock(ref ht) = t.elements[0] {
+ if let Some(BlockParam::Single(Parameter::Name(ref n))) = ht.block_param {
+ assert_eq!(n, "person");
+ } else {
+ panic!("block param expected.")
+ }
+ } else {
+ panic!("Helper block expected");
+ }
+ }
+ Err(e) => panic!("{}", e),
+ }
+
+ match Template::compile("{{#each people as |key val|}}{{person}}{{/each}}") {
+ Ok(t) => {
+ if let HelperBlock(ref ht) = t.elements[0] {
+ if let Some(BlockParam::Pair((Parameter::Name(ref n1), Parameter::Name(ref n2)))) =
+ ht.block_param {
+ assert_eq!(n1, "key");
+ assert_eq!(n2, "val");
+ } else {
+ panic!("helper block param expected.");
+ }
+ } else {
+ panic!("Helper block expected");
+ }
+ }
+ Err(e) => panic!("{}", e),
+ }
+}