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

Block params #101

Merged
merged 13 commits into from
Sep 10, 2016
4 changes: 2 additions & 2 deletions examples/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
<body>
<h1>CSL {{year}}</h1>
<ul>
{{#each teams~}}
{{#each teams as |t| ~}}
<li class="{{ranking_label @index ../teams}}">
{{~log @index~}}
<b>{{name}}</b>: {{format pts ~}}
<b>{{t.name}}</b>: {{format t.pts ~}}
</li>
{{/each~}}
</ul>
Expand Down
16 changes: 15 additions & 1 deletion src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub type Object = BTreeMap<String, Json>;

/// The context wrap data you render on your templates.
///
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Context {
data: Json,
}
Expand Down Expand Up @@ -178,6 +178,20 @@ impl JsonRender for Json {
}
}

#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
pub fn to_json<T>(src: &T) -> Json
where T: ToJson
{
src.to_json()
}

#[cfg(feature = "serde_type")]
pub fn to_json<T>(src: &T) -> Json
where T: Serialize
{
value::to_value(src)
}

impl JsonTruthy for Json {
fn is_truthy(&self) -> bool {
match *self {
Expand Down
19 changes: 16 additions & 3 deletions src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)* ~ [")"] }

Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
}
}
153 changes: 59 additions & 94 deletions src/helpers/helper_each.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -39,128 +39,64 @@ 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!("{}/{}.[{}]",
local_rc.get_path(),
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(())
}
(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(), 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(())
Expand All @@ -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
}
Expand Down Expand Up @@ -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());
}
}
Loading