Repository: sunng87/handlebars-rust
Pull request #201 (2018-02-03 02:31:52)

1.0 development
This branch is for new features targeted for handlebars-rust 1.0

The goals of 1.0 is:

- Try best to align with Rust API guidelines
  - [x] doc root attribute
  - [x] `?` in examples
- Pickup some breaking changes, like #200 Output API
- Improve performance, while keeping current architecture #224 
- Run `rustfmt` against latest `rustfmt-preview` before release



Patch:
diff --git a/Cargo.toml b/Cargo.toml
index feb2957..662f4a6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,8 +1,8 @@
 [package]
 
 name = "handlebars"
-version = "0.32.4"
-authors = ["Ning Sun <sunng@about.me>"]
+version = "1.0.0-beta.2"
+authors = ["Ning Sun <sunng@pm.me>"]
 description = "Handlebars templating implemented in Rust."
 license = "MIT"
 keywords = ["handlebars", "templating", "web"]
@@ -30,11 +30,10 @@ lazy_static = "^1.0.0"
 walkdir = "^2.1.4"
 
 [dev-dependencies]
-env_logger = "^0.4.0"
+env_logger = "^0.5.0"
 maplit = "^1.0.0"
 serde_derive = "^1.0.0"
 tempfile = "^3.0.0"
 
 [badges]
 maintenance = { status = "actively-developed" }
-
diff --git a/benches/bench.rs b/benches/bench.rs
index 7a88e49..69a0627 100644
--- a/benches/bench.rs
+++ b/benches/bench.rs
@@ -8,8 +8,8 @@ extern crate test;
 
 use std::collections::BTreeMap;
 
-use serde_json::value::Value as Json;
 use handlebars::{to_json, Handlebars, Template};
+use serde_json::value::Value as Json;
 
 static SOURCE: &'static str = "<html>
   <head>
diff --git a/examples/decorator.rs b/examples/decorator.rs
index 8857d1c..2b20e89 100644
--- a/examples/decorator.rs
+++ b/examples/decorator.rs
@@ -3,69 +3,90 @@ extern crate handlebars;
 #[macro_use]
 extern crate serde_derive;
 extern crate serde_json;
+use std::error::Error;
+
 use serde_json::value::{Map, Value as Json};
 
-use handlebars::{to_json, Decorator, Handlebars, Helper, JsonRender, RenderContext, RenderError};
+use handlebars::{
+    to_json, Context, Decorator, Handlebars, Helper, JsonRender, Output, RenderContext, RenderError,
+};
 
 // default format helper
-fn format_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+fn format_helper(
+    h: &Helper,
+    _: &Handlebars,
+    _: &Context,
+    _: &mut RenderContext,
+    out: &mut Output,
+) -> Result<(), RenderError> {
     // get parameter from helper or throw an error
-    let param = try!(
-        h.param(0,)
-            .ok_or(RenderError::new("Param 0 is required for format helper.",),)
-    );
+    let param = h
+        .param(0)
+        .ok_or(RenderError::new("Param 0 is required for format helper."))?;
     let rendered = format!("{} pts", param.value().render());
-    try!(rc.writer.write(rendered.into_bytes().as_ref()));
+    out.write(rendered.as_ref())?;
     Ok(())
 }
 
 fn format_decorator(
     d: &Decorator,
     _: &Handlebars,
+    _: &Context,
     rc: &mut RenderContext,
 ) -> Result<(), RenderError> {
-    let suffix = d.param(0)
+    let suffix = d
+        .param(0)
         .map(|v| v.value().render())
         .unwrap_or("".to_owned());
     rc.register_local_helper(
         "format",
-        Box::new(move |h: &Helper, _: &Handlebars, rc: &mut RenderContext| {
-            // get parameter from helper or throw an error
-            let param = try!(
-                h.param(0,)
-                    .ok_or(RenderError::new("Param 0 is required for format helper.",),)
-            );
-            let rendered = format!("{} {}", param.value().render(), suffix);
-            try!(rc.writer.write(rendered.into_bytes().as_ref()));
-            Ok(())
-        }),
+        Box::new(
+            move |h: &Helper,
+                  _: &Handlebars,
+                  _: &Context,
+                  _: &mut RenderContext,
+                  out: &mut Output| {
+                // get parameter from helper or throw an error
+                let param = h
+                    .param(0)
+                    .ok_or(RenderError::new("Param 0 is required for format helper."))?;
+                let rendered = format!("{} {}", param.value().render(), suffix);
+                out.write(rendered.as_ref())?;
+                Ok(())
+            },
+        ),
     );
     Ok(())
 }
 
 // another custom helper
-fn rank_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
-    let rank = try!(
-        h.param(0,)
-            .and_then(|v| v.value().as_u64(),)
-            .ok_or(RenderError::new(
-                "Param 0 with u64 type is required for rank helper."
-            ),)
-    ) as usize;
-    let teams = try!(
-        h.param(1,)
-            .and_then(|v| v.value().as_array(),)
-            .ok_or(RenderError::new(
-                "Param 1 with array type is required for rank helper"
-            ),)
-    );
-    let total = teams.len();
+fn rank_helper(
+    h: &Helper,
+    _: &Handlebars,
+    _: &Context,
+    _: &mut RenderContext,
+    out: &mut Output,
+) -> Result<(), RenderError> {
+    let rank = h
+        .param(0)
+        .and_then(|v| v.value().as_u64())
+        .ok_or(RenderError::new(
+            "Param 0 with u64 type is required for rank helper.",
+        ))? as usize;
+    let total = h
+        .param(1)
+        .as_ref()
+        .and_then(|v| v.value().as_array())
+        .map(|arr| arr.len())
+        .ok_or(RenderError::new(
+            "Param 1 with array type is required for rank helper",
+        ))?;
     if rank == 0 {
-        try!(rc.writer.write("champion".as_bytes()));
+        out.write("champion")?;
     } else if rank >= total - 2 {
-        try!(rc.writer.write("relegation".as_bytes()));
+        out.write("relegation")?;
     } else if rank <= 2 {
-        try!(rc.writer.write("acl".as_bytes()));
+        out.write("acl")?;
     }
     Ok(())
 }
@@ -125,17 +146,14 @@ pub fn make_data() -> Map<String, Json> {
     data
 }
 
-fn main() {
-    env_logger::init().unwrap();
+fn main() -> Result<(), Box<Error>> {
+    env_logger::init();
     // create the handlebars registry
     let mut handlebars = Handlebars::new();
 
     // register template from a file and assign a name to it
     // deal with errors
-    if let Err(e) = handlebars.register_template_file("table", "./examples/decorator/template.hbs")
-    {
-        panic!("{}", e);
-    }
+    handlebars.register_template_file("table", "./examples/decorator/template.hbs")?;
 
     // register some custom helpers
     handlebars.register_helper("format", Box::new(format_helper));
@@ -144,10 +162,6 @@ fn main() {
 
     // make data and render it
     let data = make_data();
-    println!(
-        "{}",
-        handlebars
-            .render("table", &data,)
-            .unwrap_or_else(|e| format!("{}", e),)
-    );
+    println!("{}", handlebars.render("table", &data)?);
+    Ok(())
 }
diff --git a/examples/error.rs b/examples/error.rs
index a662467..7c353a4 100644
--- a/examples/error.rs
+++ b/examples/error.rs
@@ -1,131 +1,24 @@
 extern crate env_logger;
 extern crate handlebars;
-#[macro_use]
-extern crate serde_derive;
 extern crate serde_json;
 
-use serde_json::value::{Map, Value as Json};
+use std::error::Error;
 
-use handlebars::{to_json, Handlebars, Helper, JsonRender, RenderContext, RenderError};
+use handlebars::Handlebars;
 
-fn format_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
-    let param = try!(
-        h.param(0,)
-            .ok_or(RenderError::new("Param 0 is required for format helper.",),)
-    );
-    let rendered = format!("{} pts", param.value().render());
-    try!(rc.writer.write(rendered.into_bytes().as_ref()));
-    Ok(())
-}
-
-fn rank_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
-    let rank = try!(
-        h.param(0,)
-            .and_then(|v| v.value().as_u64(),)
-            .ok_or(RenderError::new(
-                "Param 0 with u64 type is required for rank helper."
-            ),)
-    ) as usize;
-    let teams = try!(
-        h.param(1,)
-            .and_then(|v| v.value().as_array(),)
-            .ok_or(RenderError::new(
-                "Param 1 with array type is required for rank helper"
-            ),)
-    );
-    let total = teams.len();
-    if rank == 0 {
-        try!(rc.writer.write("champion".as_bytes()));
-    } else if rank >= total - 2 {
-        try!(rc.writer.write("relegation".as_bytes()));
-    } else if rank <= 2 {
-        try!(rc.writer.write("acl".as_bytes()));
-    }
-    Ok(())
-}
-
-static TYPES: &'static str = "serde_json";
-
-#[derive(Serialize)]
-pub struct Team {
-    name: String,
-    pts: u16,
-}
-
-// produce some data
-pub fn make_data() -> Map<String, Json> {
-    let mut data = Map::new();
-
-    data.insert("year".to_string(), to_json(&"2015".to_owned()));
-
-    let teams = vec![
-        Team {
-            name: "Jiangsu Suning".to_string(),
-            pts: 43u16,
-        },
-        Team {
-            name: "Shanghai SIPG".to_string(),
-            pts: 39u16,
-        },
-        Team {
-            name: "Hebei CFFC".to_string(),
-            pts: 27u16,
-        },
-        Team {
-            name: "Guangzhou Evergrand".to_string(),
-            pts: 22u16,
-        },
-        Team {
-            name: "Shandong Luneng".to_string(),
-            pts: 12u16,
-        },
-        Team {
-            name: "Beijing Guoan".to_string(),
-            pts: 7u16,
-        },
-        Team {
-            name: "Hangzhou Greentown".to_string(),
-            pts: 7u16,
-        },
-        Team {
-            name: "Shanghai Shenhua".to_string(),
-            pts: 4u16,
-        },
-    ];
-
-    data.insert("teams".to_string(), to_json(&teams));
-    data.insert("engine".to_string(), to_json(&TYPES.to_owned()));
-    data
-}
-
-fn main() {
-    env_logger::init().unwrap();
+fn main() -> Result<(), Box<Error>> {
+    env_logger::init();
     let mut handlebars = Handlebars::new();
 
     // template not found
-    if let Err(e) = handlebars.register_template_file("notfound", "./examples/error/notfound.hbs") {
-        println!("{}", e);
-    }
+    handlebars
+        .register_template_file("notfound", "./examples/error/notfound.hbs")
+        .unwrap_or_else(|e| println!("{}", e));
 
     // an invalid template
-    if let Err(e) = handlebars.register_template_file("error", "./examples/error/error.hbs") {
-        println!("{}", e);
-    }
-
     handlebars
-        .register_template_file("table", "./examples/error/template.hbs")
-        .ok()
-        .unwrap();
+        .register_template_file("error", "./examples/error/error.hbs")
+        .unwrap_or_else(|e| println!("{}", e));
 
-    handlebars.register_helper("format", Box::new(format_helper));
-    handlebars.register_helper("ranking_label", Box::new(rank_helper));
-    // handlebars.register_helper("format", Box::new(FORMAT_HELPER));
-
-    let data = make_data();
-    println!(
-        "{}",
-        handlebars
-            .render("table", &data,)
-            .unwrap_or_else(|e| format!("{}", e),)
-    );
+    Ok(())
 }
diff --git a/examples/partials.rs b/examples/partials.rs
index 37460c9..fa55880 100644
--- a/examples/partials.rs
+++ b/examples/partials.rs
@@ -3,26 +3,17 @@ extern crate handlebars;
 #[macro_use]
 extern crate maplit;
 
-use std::path::Path;
 use handlebars::Handlebars;
+use std::error::Error;
 
-fn main() {
-    env_logger::init().unwrap();
+fn main() -> Result<(), Box<Error>> {
+    env_logger::init();
     let mut handlebars = Handlebars::new();
 
-    handlebars
-        .register_template_file("template", &Path::new("./examples/partials/template2.hbs"))
-        .ok()
-        .unwrap();
+    handlebars.register_template_file("template", "./examples/partials/template2.hbs")?;
 
-    handlebars
-        .register_template_file("base0", &Path::new("./examples/partials/base0.hbs"))
-        .ok()
-        .unwrap();
-    handlebars
-        .register_template_file("base1", &Path::new("./examples/partials/base1.hbs"))
-        .ok()
-        .unwrap();
+    handlebars.register_template_file("base0", "./examples/partials/base0.hbs")?;
+    handlebars.register_template_file("base1", "./examples/partials/base1.hbs")?;
 
     let data0 = btreemap! {
         "title".to_string() => "example 0".to_string(),
@@ -34,19 +25,11 @@ fn main() {
     };
 
     println!("Page 0");
-    println!(
-        "{}",
-        handlebars
-            .render("template", &data0,)
-            .unwrap_or_else(|e| format!("{}", e),)
-    );
+    println!("{}", handlebars.render("template", &data0)?);
     println!("=======================================================");
 
     println!("Page 1");
-    println!(
-        "{}",
-        handlebars
-            .render("template", &data1,)
-            .unwrap_or_else(|e| format!("{}", e),)
-    );
+    println!("{}", handlebars.render("template", &data1)?);
+
+    Ok(())
 }
diff --git a/examples/quick.rs b/examples/quick.rs
index 9fd5c24..9ffe3e5 100644
--- a/examples/quick.rs
+++ b/examples/quick.rs
@@ -3,18 +3,19 @@ extern crate handlebars;
 extern crate serde_json;
 
 use handlebars::Handlebars;
+use std::error::Error;
 
-fn main() {
+// wait RFC1937 in
+fn main() -> Result<(), Box<Error>> {
     let mut reg = Handlebars::new();
     // render without register
     println!(
         "{}",
-        reg.render_template("Hello {{name}}", &json!({"name": "foo"}))
-            .unwrap()
+        reg.render_template("Hello {{name}}", &json!({"name": "foo"}))?
     );
 
     // register template using given name
-    reg.register_template_string("tpl_1", "Good afternoon, {{name}}")
-        .unwrap();
-    println!("{}", reg.render("tpl_1", &json!({"name": "foo"})).unwrap());
+    reg.register_template_string("tpl_1", "Good afternoon, {{name}}")?;
+    println!("{}", reg.render("tpl_1", &json!({"name": "foo"}))?);
+    Ok(())
 }
diff --git a/examples/render-cli.rs b/examples/render-cli.rs
index 405e0e1..2a6e5d9 100644
--- a/examples/render-cli.rs
+++ b/examples/render-cli.rs
@@ -2,9 +2,9 @@ extern crate env_logger;
 extern crate handlebars;
 extern crate serde_json;
 
+use std::env;
 use std::io::{self, Write};
 use std::process;
-use std::env;
 use std::str::FromStr;
 
 use serde_json::value::Value as Json;
@@ -28,7 +28,7 @@ fn parse_json(text: &str) -> Json {
 }
 
 fn main() {
-    env_logger::init().unwrap();
+    env_logger::init();
 
     let mut args = env::args();
     args.next(); // skip own filename
@@ -54,6 +54,3 @@ fn main() {
         }
     }
 }
-
-#[cfg(feature = "serde_type")]
-fn main() {}
diff --git a/examples/render.rs b/examples/render.rs
index 2c08e83..8bc5685 100644
--- a/examples/render.rs
+++ b/examples/render.rs
@@ -6,44 +6,57 @@ extern crate serde_derive;
 extern crate serde_json;
 
 use serde_json::value::{Map, Value as Json};
+use std::error::Error;
 
-use handlebars::{to_json, Handlebars, Helper, JsonRender, RenderContext, RenderError};
+use handlebars::{
+    to_json, Context, Handlebars, Helper, JsonRender, Output, RenderContext, RenderError,
+};
 
 // define a custom helper
-fn format_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
+fn format_helper(
+    h: &Helper,
+    _: &Handlebars,
+    _: &Context,
+    _: &mut RenderContext,
+    out: &mut Output,
+) -> Result<(), RenderError> {
     // get parameter from helper or throw an error
-    let param = try!(
-        h.param(0,)
-            .ok_or(RenderError::new("Param 0 is required for format helper.",),)
-    );
+    let param = h
+        .param(0)
+        .ok_or(RenderError::new("Param 0 is required for format helper."))?;
     let rendered = format!("{} pts", param.value().render());
-    try!(rc.writer.write(rendered.into_bytes().as_ref()));
+    out.write(rendered.as_ref())?;
     Ok(())
 }
 
 // another custom helper
-fn rank_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
-    let rank = try!(
-        h.param(0,)
-            .and_then(|v| v.value().as_u64(),)
-            .ok_or(RenderError::new(
-                "Param 0 with u64 type is required for rank helper."
-            ),)
-    ) as usize;
-    let teams = try!(
-        h.param(1,)
-            .and_then(|v| v.value().as_array(),)
-            .ok_or(RenderError::new(
-                "Param 1 with array type is required for rank helper"
-            ),)
-    );
-    let total = teams.len();
+fn rank_helper(
+    h: &Helper,
+    _: &Handlebars,
+    _: &Context,
+    _: &mut RenderContext,
+    out: &mut Output,
+) -> Result<(), RenderError> {
+    let rank = h
+        .param(0)
+        .and_then(|v| v.value().as_u64())
+        .ok_or(RenderError::new(
+            "Param 0 with u64 type is required for rank helper.",
+        ))? as usize;
+    let total = h
+        .param(1)
+        .as_ref()
+        .and_then(|v| v.value().as_array())
+        .map(|arr| arr.len())
+        .ok_or(RenderError::new(
+            "Param 1 with array type is required for rank helper",
+        ))?;
     if rank == 0 {
-        try!(rc.writer.write("champion".as_bytes()));
+        out.write("champion")?;
     } else if rank >= total - 2 {
-        try!(rc.writer.write("relegation".as_bytes()));
+        out.write("relegation")?;
     } else if rank <= 2 {
-        try!(rc.writer.write("acl".as_bytes()));
+        out.write("acl")?;
     }
     Ok(())
 }
@@ -103,16 +116,13 @@ pub fn make_data() -> Map<String, Json> {
     data
 }
 
-fn main() {
-    env_logger::init().unwrap();
+fn main() -> Result<(), Box<Error>> {
+    env_logger::init();
     // create the handlebars registry
     let mut handlebars = Handlebars::new();
 
     // register template from a file and assign a name to it
-    // deal with errors
-    if let Err(e) = handlebars.register_template_file("table", "./examples/render/template.hbs") {
-        panic!("{}", e);
-    }
+    handlebars.register_template_file("table", "./examples/render/template.hbs")?;
 
     // register some custom helpers
     handlebars.register_helper("format", Box::new(format_helper));
@@ -120,10 +130,6 @@ fn main() {
 
     // make data and render it
     let data = make_data();
-    println!(
-        "{}",
-        handlebars
-            .render("table", &data,)
-            .unwrap_or_else(|e| format!("{}", e),)
-    );
+    println!("{}", handlebars.render("table", &data)?);
+    Ok(())
 }
diff --git a/examples/render_file.rs b/examples/render_file.rs
index d398269..bf35e81 100644
--- a/examples/render_file.rs
+++ b/examples/render_file.rs
@@ -8,45 +8,58 @@ extern crate serde_json;
 use serde::Serialize;
 use serde_json::value::{self, Map, Value as Json};
 
-use std::io::{Read, Write};
+use std::error::Error;
 use std::fs::File;
+use std::io::{Read, Write};
 
-use handlebars::{to_json, Context, Handlebars, Helper, JsonRender, RenderContext, RenderError};
+use handlebars::{
+    to_json, Context, Handlebars, Helper, JsonRender, Output, RenderContext, RenderError,
+};
 
 // define a custom helper
-fn format_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
-    let param = try!(
-        h.param(0,)
-            .ok_or(RenderError::new("Param 0 is required for format helper.",),)
-    );
+fn format_helper(
+    h: &Helper,
+    _: &Handlebars,
+    _: &Context,
+    _: &mut RenderContext,
+    out: &mut Output,
+) -> Result<(), RenderError> {
+    let param = h
+        .param(0)
+        .ok_or(RenderError::new("Param 0 is required for format helper."))?;
     let rendered = format!("{} pts", param.value().render());
-    try!(rc.writer.write(rendered.into_bytes().as_ref()));
+    out.write(rendered.as_ref())?;
     Ok(())
 }
 
 // another custom helper
-fn rank_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
-    let rank = try!(
-        h.param(0,)
-            .and_then(|v| v.value().as_u64(),)
-            .ok_or(RenderError::new(
-                "Param 0 with u64 type is required for rank helper."
-            ),)
-    ) as usize;
-    let teams = try!(
-        h.param(1,)
-            .and_then(|v| v.value().as_array(),)
-            .ok_or(RenderError::new(
-                "Param 1 with array type is required for rank helper"
-            ),)
-    );
-    let total = teams.len();
+fn rank_helper(
+    h: &Helper,
+    _: &Handlebars,
+    _: &Context,
+    _: &mut RenderContext,
+    out: &mut Output,
+) -> Result<(), RenderError> {
+    let rank = h
+        .param(0)
+        .and_then(|ref v| v.value().as_u64())
+        .ok_or(RenderError::new(
+            "Param 0 with u64 type is required for rank helper.",
+        ))? as usize;
+    let total = h
+        .param(1)
+        .as_ref()
+        .and_then(|v| v.value().as_array())
+        .map(|arr| arr.len())
+        .ok_or(RenderError::new(
+            "Param 1 with array type is required for rank helper",
+        ))?;
     if rank == 0 {
-        try!(rc.writer.write("champion".as_bytes()));
+        out.write("champion")?;
     } else if rank >= total - 2 {
-        try!(rc.writer.write("relegation".as_bytes()));
+        out.write("relegation")?;
     } else if rank <= 2 {
-        try!(rc.writer.write("acl".as_bytes()));
+        out.write("acl")?;
     }
     Ok(())
 }
@@ -106,8 +119,8 @@ pub fn make_data() -> Map<String, Json> {
     data
 }
 
-fn main() {
-    env_logger::init().unwrap();
+fn main() -> Result<(), Box<Error>> {
+    env_logger::init();
     let mut handlebars = Handlebars::new();
 
     handlebars.register_helper("format", Box::new(format_helper));
@@ -116,15 +129,9 @@ fn main() {
 
     let data = make_data();
 
-    // I'm using unwrap directly here to demostration.
-    // Never use this style in your real-world projects.
-    let mut source_template = File::open(&"./examples/render_file/template.hbs").unwrap();
-    let mut output_file = File::create("target/table.html").unwrap();
-    if let Err(e) =
-        handlebars.render_template_source_to_write(&mut source_template, &data, &mut output_file)
-    {
-        println!("Failed to generate target/table.html: {}", e);
-    } else {
-        println!("target/table.html generated");
-    };
+    let mut source_template = File::open(&"./examples/render_file/template.hbs")?;
+    let mut output_file = File::create("target/table.html")?;
+    handlebars.render_template_source_to_write(&mut source_template, &data, &mut output_file)?;
+    println!("target/table.html generated");
+    Ok(())
 }
diff --git a/src/context.rs b/src/context.rs
index 3adce0b..7cc30eb 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -1,14 +1,12 @@
-use std::sync::Arc;
-
 use serde::Serialize;
 use serde_json::value::{to_value, Map, Value as Json};
 
 use std::collections::{BTreeMap, VecDeque};
 
-use pest::Parser;
-use pest::iterators::Pair;
-use grammar::{HandlebarsParser, Rule};
 use error::RenderError;
+use grammar::{HandlebarsParser, Rule};
+use pest::iterators::Pair;
+use pest::Parser;
 
 pub type Object = BTreeMap<String, Json>;
 
@@ -16,7 +14,7 @@ pub type Object = BTreeMap<String, Json>;
 ///
 #[derive(Debug, Clone)]
 pub struct Context {
-    data: Arc<Json>,
+    data: Json,
 }
 
 #[inline]
@@ -115,16 +113,14 @@ pub fn merge_json(base: &Json, addition: &Object) -> Json {
 impl Context {
     /// Create a context with null data
     pub fn null() -> Context {
-        Context {
-            data: Arc::new(Json::Null),
-        }
+        Context { data: Json::Null }
     }
 
     /// Create a context with given data
     pub fn wraps<T: Serialize>(e: &T) -> Result<Context, RenderError> {
         to_value(e)
             .map_err(RenderError::from)
-            .map(|d| Context { data: Arc::new(d) })
+            .map(|d| Context { data: d })
     }
 
     /// Navigate the context with base path and relative path
@@ -142,13 +138,14 @@ impl Context {
         parse_json_visitor(&mut path_stack, base_path, path_context, relative_path)?;
 
         let paths: Vec<&str> = path_stack.iter().map(|x| *x).collect();
-        let mut data: Option<&Json> = Some(self.data.as_ref());
+        let mut data: Option<&Json> = Some(&self.data);
         for p in paths.iter() {
             if *p == "this" {
                 continue;
             }
             data = match data {
-                Some(&Json::Array(ref l)) => p.parse::<usize>()
+                Some(&Json::Array(ref l)) => p
+                    .parse::<usize>()
                     .map_err(|e| RenderError::with(e))
                     .map(|idx_u| l.get(idx_u))?,
                 Some(&Json::Object(ref m)) => m.get(*p),
@@ -160,82 +157,20 @@ impl Context {
     }
 
     pub fn data(&self) -> &Json {
-        self.data.as_ref()
+        &self.data
     }
 
     pub fn data_mut(&mut self) -> &mut Json {
-        Arc::make_mut(&mut self.data)
-    }
-}
-
-/// Render Json data with default format
-pub trait JsonRender {
-    fn render(&self) -> String;
-}
-
-pub trait JsonTruthy {
-    fn is_truthy(&self) -> bool;
-}
-
-impl JsonRender for Json {
-    fn render(&self) -> String {
-        match *self {
-            Json::String(ref s) => s.to_string(),
-            Json::Bool(i) => i.to_string(),
-            Json::Number(ref n) => n.to_string(),
-            Json::Null => "".to_owned(),
-            Json::Array(ref a) => {
-                let mut buf = String::new();
-                buf.push('[');
-                for i in a.iter() {
-                    buf.push_str(i.render().as_ref());
-                    buf.push_str(", ");
-                }
-                buf.push(']');
-                buf
-            }
-            Json::Object(_) => "[object]".to_owned(),
-        }
-    }
-}
-
-pub fn to_json<T>(src: &T) -> Json
-where
-    T: Serialize,
-{
-    to_value(src).unwrap_or_default()
-}
-
-pub fn as_string(src: &Json) -> Option<&str> {
-    src.as_str()
-}
-
-impl JsonTruthy for Json {
-    fn is_truthy(&self) -> bool {
-        match *self {
-            Json::Bool(ref i) => *i,
-            Json::Number(ref n) => n.as_f64().map(|f| f.is_normal()).unwrap_or(false),
-            Json::Null => false,
-            Json::String(ref i) => i.len() > 0,
-            Json::Array(ref i) => i.len() > 0,
-            Json::Object(ref i) => i.len() > 0,
-        }
+        &mut self.data
     }
 }
 
 #[cfg(test)]
 mod test {
-    use context::{self, Context, JsonRender};
+    use context::{self, Context};
+    use serde_json::value::Map;
     use std::collections::VecDeque;
-    use serde_json::value::{Map, Value as Json};
-
-    #[test]
-    fn test_json_render() {
-        let raw = "<p>Hello world</p>\n<p thing=\"hello\"</p>";
-        let thing = Json::String(raw.to_string());
-
-        assert_eq!(raw, thing.render());
-    }
+    use value::{self, JsonRender};
 
     #[derive(Serialize)]
     struct Address {
@@ -331,12 +266,12 @@ mod test {
     #[test]
     fn test_this() {
         let mut map_with_this = Map::new();
-        map_with_this.insert("this".to_string(), context::to_json(&"hello"));
-        map_with_this.insert("age".to_string(), context::to_json(&5usize));
+        map_with_this.insert("this".to_string(), value::to_json(&"hello"));
+        map_with_this.insert("age".to_string(), value::to_json(&5usize));
         let ctx1 = Context::wraps(&map_with_this).unwrap();
 
         let mut map_without_this = Map::new();
-        map_without_this.insert("age".to_string(), context::to_json(&4usize));
+        map_without_this.insert("age".to_string(), value::to_json(&4usize));
         let ctx2 = Context::wraps(&map_without_this).unwrap();
 
         assert_eq!(
@@ -360,7 +295,7 @@ mod test {
         let map = json!({ "age": 4 });
         let s = "hello".to_owned();
         let hash = btreemap!{
-            "tag".to_owned() => context::to_json(&"h1")
+            "tag".to_owned() => value::to_json(&"h1")
         };
 
         let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap();
@@ -381,7 +316,7 @@ mod test {
             "h1".to_owned()
         );
 
-        let ctx_a2 = Context::wraps(&context::merge_json(&context::to_json(&s), &hash)).unwrap();
+        let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(&s), &hash)).unwrap();
         assert_eq!(
             ctx_a2
                 .navigate(".", &VecDeque::new(), "this")
@@ -415,8 +350,8 @@ mod test {
         );
     }
 
-    use serde::{Serialize, Serializer};
     use serde::ser::Error as SerdeError;
+    use serde::{Serialize, Serializer};
 
     struct UnserializableType {}
 
diff --git a/src/directives/inline.rs b/src/directives/inline.rs
index d27d243..1bb3fa2 100644
--- a/src/directives/inline.rs
+++ b/src/directives/inline.rs
@@ -1,14 +1,15 @@
 use std::rc::Rc;
 
-use directives::DirectiveDef;
+use context::Context;
+use directives::{DirectiveDef, DirectiveResult};
+use error::RenderError;
 use registry::Registry;
 use render::{Directive, RenderContext};
-use error::RenderError;
 
 #[derive(Clone, Copy)]
 pub struct InlineDirective;
 
-fn get_name<'a>(d: &'a Directive) -> Result<&'a str, RenderError> {
+fn get_name<'reg: 'rc, 'rc>(d: &'rc Directive<'reg, 'rc>) -> Result<&'rc str, RenderError> {
     d.param(0)
         .ok_or_else(|| RenderError::new("Param required for directive \"inline\""))
         .and_then(|v| {
@@ -19,13 +20,18 @@ fn get_name<'a>(d: &'a Directive) -> Result<&'a str, RenderError> {
 }
 
 impl DirectiveDef for InlineDirective {
-    fn call(&self, d: &Directive, _: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
-        let name = try!(get_name(d));
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        d: &Directive<'reg, 'rc>,
+        _: &'reg Registry,
+        _: &'rc Context,
+        rc: &mut RenderContext,
+    ) -> DirectiveResult {
+        let name = get_name(d)?;
 
-        let template = try!(
-            d.template()
-                .ok_or_else(|| RenderError::new("inline should have a block"))
-        );
+        let template = d
+            .template()
+            .ok_or_else(|| RenderError::new("inline should have a block"))?;
 
         rc.set_partial(name.to_owned(), Rc::new(template.clone()));
         Ok(())
@@ -36,12 +42,10 @@ pub static INLINE_DIRECTIVE: InlineDirective = InlineDirective;
 
 #[cfg(test)]
 mod test {
-    use template::Template;
-    use registry::Registry;
     use context::Context;
+    use registry::Registry;
     use render::{Evaluable, RenderContext};
-    use support::str::StringWriter;
-    use std::collections::HashMap;
+    use template::Template;
 
     #[test]
     fn test_inline() {
@@ -52,12 +56,9 @@ mod test {
 
         let hbs = Registry::new();
 
-        let mut sw = StringWriter::new();
         let ctx = Context::null();
-        let mut hlps = HashMap::new();
-
-        let mut rc = RenderContext::new(ctx, &mut hlps, &mut sw);
-        t0.elements[0].eval(&hbs, &mut rc).unwrap();
+        let mut rc = RenderContext::new(None);
+        t0.elements[0].eval(&hbs, &ctx, &mut rc).unwrap();
 
         assert!(rc.get_partial(&"hello".to_owned()).is_some());
     }
diff --git a/src/directives/mod.rs b/src/directives/mod.rs
index a19a1ee..dfef010 100644
--- a/src/directives/mod.rs
+++ b/src/directives/mod.rs
@@ -1,9 +1,12 @@
-use render::{Directive, RenderContext};
-use registry::Registry;
+use context::Context;
 use error::RenderError;
+use registry::Registry;
+use render::{Directive, RenderContext};
 
 pub use self::inline::INLINE_DIRECTIVE;
 
+pub type DirectiveResult = Result<(), RenderError>;
+
 /// Decorator Definition
 ///
 /// Implement this trait to define your own decorators or directives. Currently
@@ -18,13 +21,17 @@ pub use self::inline::INLINE_DIRECTIVE;
 /// ```
 /// use handlebars::*;
 ///
-/// fn update_data(_: &Decorator, _: &Handlebars, rc: &mut RenderContext)
+/// fn update_data<'reg: 'rc, 'rc>(_: &Decorator, _: &Handlebars, ctx: &Context, rc: &mut RenderContext)
 ///         -> Result<(), RenderError> {
 ///     // modify json object
-///     let mut data = rc.context_mut().data_mut();
-///     if let Some(ref mut m) = data.as_object_mut() {
-///         m.insert("hello".to_string(), to_json(&"world".to_owned()));
+///     let mut new_ctx = ctx.clone();
+///     {
+///         let mut data = new_ctx.data_mut();
+///         if let Some(ref mut m) = data.as_object_mut() {
+///             m.insert("hello".to_string(), to_json(&"world".to_owned()));
+///         }
 ///     }
+///     rc.set_context(new_ctx);
 ///     Ok(())
 /// }
 ///
@@ -37,9 +44,9 @@ pub use self::inline::INLINE_DIRECTIVE;
 /// ```
 /// use handlebars::*;
 ///
-/// fn override_helper(_: &Decorator, _: &Handlebars, rc: &mut RenderContext)
+/// fn override_helper(_: &Decorator, _: &Handlebars, _: &Context, rc: &mut RenderContext)
 ///         -> Result<(), RenderError> {
-///     let new_helper = |h: &Helper, _: &Handlebars, rc: &mut RenderContext|
+///     let new_helper = |h: &Helper, _: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut Output|
 ///             -> Result<(), RenderError> {
 ///         // your helper logic
 ///         Ok(())
@@ -50,19 +57,31 @@ pub use self::inline::INLINE_DIRECTIVE;
 /// ```
 ///
 pub trait DirectiveDef: Send + Sync {
-    fn call(&self, d: &Directive, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>;
+    fn call<'reg: 'rc, 'rc>(
+        &'reg self,
+        d: &Directive<'reg, 'rc>,
+        r: &'reg Registry,
+        ctx: &'rc Context,
+        rc: &mut RenderContext,
+    ) -> DirectiveResult;
 }
 
 /// implement DirectiveDef for bare function so we can use function as directive
 impl<
-    F: Send
-        + Sync
-        + for<'b, 'c, 'd, 'e> Fn(&'b Directive, &'c Registry, &'d mut RenderContext)
-        -> Result<(), RenderError>,
-> DirectiveDef for F
+        F: Send
+            + Sync
+            + for<'reg, 'rc> Fn(&Directive<'reg, 'rc>, &'reg Registry, &'rc Context, &mut RenderContext)
+                -> DirectiveResult,
+    > DirectiveDef for F
 {
-    fn call(&self, d: &Directive, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
-        (*self)(d, r, rc)
+    fn call<'reg: 'rc, 'rc>(
+        &'reg self,
+        d: &Directive<'reg, 'rc>,
+        reg: &'reg Registry,
+        ctx: &'rc Context,
+        rc: &mut RenderContext,
+    ) -> DirectiveResult {
+        (*self)(d, reg, ctx, rc)
     }
 }
 
@@ -70,10 +89,12 @@ mod inline;
 
 #[cfg(test)]
 mod test {
+    use context::Context;
+    use error::RenderError;
+    use output::Output;
     use registry::Registry;
-    use context::{self, as_string, Context};
     use render::{Directive, Helper, RenderContext};
-    use error::RenderError;
+    use value::{as_string, to_json};
 
     #[test]
     fn test_register_decorator() {
@@ -91,14 +112,17 @@ mod test {
         handlebars.register_decorator(
             "foo",
             Box::new(
-                |_: &Directive, _: &Registry, _: &mut RenderContext| -> Result<(), RenderError> {
-                    Ok(())
-                },
+                |_: &Directive,
+                 _: &Registry,
+                 _: &Context,
+                 _: &mut RenderContext|
+                 -> Result<(), RenderError> { Ok(()) },
             ),
         );
         assert_eq!(handlebars.render("t0", &data).ok().unwrap(), "".to_string());
     }
 
+    // updating context data disabled for now
     #[test]
     fn test_update_data_with_decorator() {
         let mut handlebars = Registry::new();
@@ -113,15 +137,20 @@ mod test {
         handlebars.register_decorator(
             "foo",
             Box::new(
-                |_: &Directive, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
+                |_: &Directive,
+                 _: &Registry,
+                 ctx: &Context,
+                 rc: &mut RenderContext|
+                 -> Result<(), RenderError> {
                     // modify json object
-                    let ctx_ref = rc.context_mut();
-                    let data = ctx_ref.data_mut();
-
-                    if let Some(ref mut m) = data.as_object_mut().as_mut() {
-                        m.insert("hello".to_string(), context::to_json(&"war".to_owned()));
+                    let mut new_ctx = ctx.clone();
+                    {
+                        let data = new_ctx.data_mut();
+                        if let Some(ref mut m) = data.as_object_mut().as_mut() {
+                            m.insert("hello".to_string(), to_json(&"war".to_owned()));
+                        }
                     }
-
+                    rc.set_context(new_ctx);
                     Ok(())
                 },
             ),
@@ -136,12 +165,17 @@ mod test {
         handlebars.register_decorator(
             "bar",
             Box::new(
-                |d: &Directive, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
+                |d: &Directive,
+                 _: &Registry,
+                 _: &Context,
+                 rc: &mut RenderContext|
+                 -> Result<(), RenderError> {
                     // modify value
-                    let v = d.param(0)
+                    let v = d
+                        .param(0)
                         .and_then(|v| Context::wraps(v.value()).ok())
                         .unwrap_or(Context::null());
-                    *rc.context_mut() = v;
+                    rc.set_context(v);
                     Ok(())
                 },
             ),
@@ -188,14 +222,20 @@ mod test {
         handlebars.register_helper(
             "distance",
             Box::new(
-                |h: &Helper, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
+                |h: &Helper,
+                 _: &Registry,
+                 _: &Context,
+                 _: &mut RenderContext,
+                 out: &mut Output|
+                 -> Result<(), RenderError> {
                     let s = format!(
                         "{}m",
-                        h.param(0,)
-                            .map(|v| v.value(),)
-                            .unwrap_or(&context::to_json(&0),)
+                        h.param(0)
+                            .as_ref()
+                            .map(|v| v.value())
+                            .unwrap_or(&to_json(&0))
                     );
-                    try!(rc.writer().write(s.into_bytes().as_ref()));
+                    out.write(s.as_ref())?;
                     Ok(())
                 },
             ),
@@ -203,23 +243,32 @@ mod test {
         handlebars.register_decorator(
             "foo",
             Box::new(
-                |d: &Directive, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
-                    let new_unit = d.param(0)
+                |d: &Directive,
+                 _: &Registry,
+                 _: &Context,
+                 rc: &mut RenderContext|
+                 -> Result<(), RenderError> {
+                    let new_unit = d
+                        .param(0)
+                        .as_ref()
                         .and_then(|v| as_string(v.value()))
                         .unwrap_or("")
                         .to_owned();
                     let new_helper = move |h: &Helper,
                                            _: &Registry,
-                                           rc: &mut RenderContext|
+                                           _: &Context,
+                                           _: &mut RenderContext,
+                                           out: &mut Output|
                           -> Result<(), RenderError> {
                         let s = format!(
                             "{}{}",
-                            h.param(0,)
-                                .map(|v| v.value(),)
-                                .unwrap_or(&context::to_json(&0),),
+                            h.param(0)
+                                .as_ref()
+                                .map(|v| v.value())
+                                .unwrap_or(&to_json(&0)),
                             new_unit
                         );
-                        try!(rc.writer().write(s.into_bytes().as_ref()));
+                        out.write(s.as_ref())?;
                         Ok(())
                     };
 
@@ -231,7 +280,11 @@ mod test {
         handlebars.register_decorator(
             "bar",
             Box::new(
-                |_: &Directive, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
+                |_: &Directive,
+                 _: &Registry,
+                 _: &Context,
+                 rc: &mut RenderContext|
+                 -> Result<(), RenderError> {
                     rc.unregister_local_helper("distance");
                     Ok(())
                 },
diff --git a/src/error.rs b/src/error.rs
index fca81c0..824a7e5 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,6 +1,7 @@
-use std::io::Error as IOError;
 use std::error::Error;
 use std::fmt;
+use std::io::Error as IOError;
+use std::string::FromUtf8Error;
 
 use serde_json::error::Error as SerdeError;
 use walkdir::Error as WalkdirError;
@@ -57,6 +58,12 @@ impl From<SerdeError> for RenderError {
     }
 }
 
+impl From<FromUtf8Error> for RenderError {
+    fn from(e: FromUtf8Error) -> RenderError {
+        RenderError::with(e)
+    }
+}
+
 impl RenderError {
     pub fn new<T: AsRef<str>>(desc: T) -> RenderError {
         RenderError {
@@ -213,7 +220,10 @@ quick_error! {
 
 impl From<WalkdirError> for TemplateFileError {
     fn from(error: WalkdirError) -> TemplateFileError {
-        let path_string: String = error.path().map(|p| p.to_string_lossy().to_string()).unwrap_or_default();
+        let path_string: String = error
+            .path()
+            .map(|p| p.to_string_lossy().to_string())
+            .unwrap_or_default();
         TemplateFileError::IOError(IOError::from(error), path_string)
     }
 }
@@ -250,4 +260,3 @@ impl TemplateRenderError {
         }
     }
 }
-
diff --git a/src/helpers/helper_each.rs b/src/helpers/helper_each.rs
index ecb2323..059a7f0 100644
--- a/src/helpers/helper_each.rs
+++ b/src/helpers/helper_each.rs
@@ -2,21 +2,29 @@ use std::collections::BTreeMap;
 
 use serde_json::value::Value as Json;
 
+use context::Context;
+use error::RenderError;
 use helpers::{HelperDef, HelperResult};
+use output::Output;
 use registry::Registry;
-use context::{to_json, JsonTruthy};
 use render::{Helper, RenderContext, Renderable};
-use error::RenderError;
+use value::{to_json, JsonTruthy};
 
 #[derive(Clone, Copy)]
 pub struct EachHelper;
 
 impl HelperDef for EachHelper {
-    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> HelperResult {
-        let value = try!(
-            h.param(0)
-                .ok_or_else(|| RenderError::new("Param not found for helper \"each\""))
-        );
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        h: &Helper,
+        r: &Registry,
+        ctx: &Context,
+        rc: &mut RenderContext,
+        out: &mut Output,
+    ) -> HelperResult {
+        let value = h
+            .param(0)
+            .ok_or_else(|| RenderError::new("Param not found for helper \"each\""))?;
 
         let template = h.template();
 
@@ -45,7 +53,7 @@ impl HelperDef for EachHelper {
                                 let new_path =
                                     format!("{}/{}/[{}]", local_rc.get_path(), inner_path, i);
                                 debug!("each path {:?}", new_path);
-                                local_rc.set_path(new_path.clone());
+                                local_rc.set_path(new_path);
                             }
 
                             if let Some(block_param) = h.block_param() {
@@ -54,7 +62,7 @@ impl HelperDef for EachHelper {
                                 local_rc.push_block_context(&map)?;
                             }
 
-                            try!(t.render(r, &mut local_rc));
+                            t.render(r, ctx, &mut local_rc, out)?;
 
                             if h.block_param().is_some() {
                                 local_rc.pop_block_context();
@@ -70,6 +78,7 @@ impl HelperDef for EachHelper {
                         let mut first: bool = true;
                         for k in obj.keys() {
                             let mut local_rc = rc.derive();
+
                             if let Some(ref p) = local_path_root {
                                 local_rc.push_local_path_root(p.clone());
                             }
@@ -93,7 +102,7 @@ impl HelperDef for EachHelper {
                                 local_rc.push_block_context(&map)?;
                             }
 
-                            try!(t.render(r, &mut local_rc));
+                            t.render(r, ctx, &mut local_rc, out)?;
 
                             if h.block_param().is_some() {
                                 local_rc.pop_block_context();
@@ -108,13 +117,13 @@ impl HelperDef for EachHelper {
                     }
                     (false, _) => {
                         if let Some(else_template) = h.inverse() {
-                            try!(else_template.render(r, rc));
+                            else_template.render(r, ctx, rc, out)?;
                         }
                         Ok(())
                     }
                     _ => Err(RenderError::new(format!(
                         "Param type is not iterable: {:?}",
-                        template
+                        value.value()
                     ))),
                 };
 
@@ -131,11 +140,11 @@ pub static EACH_HELPER: EachHelper = EachHelper;
 #[cfg(test)]
 mod test {
     use registry::Registry;
-    use context::to_json;
+    use value::to_json;
 
+    use serde_json::value::Value as Json;
     use std::collections::BTreeMap;
     use std::str::FromStr;
-    use serde_json::value::Value as Json;
 
     #[test]
     fn test_each() {
diff --git a/src/helpers/helper_if.rs b/src/helpers/helper_if.rs
index 9eee1d5..b72ee90 100644
--- a/src/helpers/helper_if.rs
+++ b/src/helpers/helper_if.rs
@@ -1,8 +1,10 @@
+use context::Context;
+use error::RenderError;
 use helpers::{HelperDef, HelperResult};
+use output::Output;
 use registry::Registry;
-use context::JsonTruthy;
 use render::{Helper, RenderContext, Renderable};
-use error::RenderError;
+use value::JsonTruthy;
 
 #[derive(Clone, Copy)]
 pub struct IfHelper {
@@ -10,11 +12,17 @@ pub struct IfHelper {
 }
 
 impl HelperDef for IfHelper {
-    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> HelperResult {
-        let param = try!(
-            h.param(0)
-                .ok_or_else(|| RenderError::new("Param not found for helper \"if\""))
-        );
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        h: &Helper,
+        r: &Registry,
+        ctx: &Context,
+        rc: &mut RenderContext,
+        out: &mut Output,
+    ) -> HelperResult {
+        let param = h
+            .param(0)
+            .ok_or_else(|| RenderError::new("Param not found for helper \"if\""))?;
 
         let mut value = param.value().is_truthy();
 
@@ -25,7 +33,7 @@ impl HelperDef for IfHelper {
         let tmpl =
             if value { h.template() } else { h.inverse() };
         match tmpl {
-            Some(ref t) => t.render(r, rc),
+            Some(ref t) => t.render(r, ctx, rc, out),
             None => Ok(()),
         }
     }
@@ -36,10 +44,10 @@ pub static UNLESS_HELPER: IfHelper = IfHelper { positive: false };
 
 #[cfg(test)]
 mod test {
+    use helpers::WITH_HELPER;
     use registry::Registry;
-    use std::str::FromStr;
     use serde_json::value::Value as Json;
-    use helpers::WITH_HELPER;
+    use std::str::FromStr;
 
     #[test]
     fn test_if() {
diff --git a/src/helpers/helper_log.rs b/src/helpers/helper_log.rs
index 298ef06..15f40ba 100644
--- a/src/helpers/helper_log.rs
+++ b/src/helpers/helper_log.rs
@@ -1,18 +1,26 @@
+use context::Context;
+use error::RenderError;
 use helpers::{HelperDef, HelperResult};
+use output::Output;
 use registry::Registry;
-use context::JsonRender;
 use render::{Helper, RenderContext};
-use error::RenderError;
+use value::JsonRender;
 
 #[derive(Clone, Copy)]
 pub struct LogHelper;
 
 impl HelperDef for LogHelper {
-    fn call(&self, h: &Helper, _: &Registry, _: &mut RenderContext) -> HelperResult {
-        let param = try!(
-            h.param(0)
-                .ok_or_else(|| RenderError::new("Param not found for helper \"log\""))
-        );
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        h: &Helper,
+        _: &Registry,
+        _: &Context,
+        _: &mut RenderContext,
+        _: &mut Output,
+    ) -> HelperResult {
+        let param = h
+            .param(0)
+            .ok_or_else(|| RenderError::new("Param not found for helper \"log\""))?;
 
         info!(
             "{}: {}",
diff --git a/src/helpers/helper_lookup.rs b/src/helpers/helper_lookup.rs
index d805f8b..9597374 100644
--- a/src/helpers/helper_lookup.rs
+++ b/src/helpers/helper_lookup.rs
@@ -1,24 +1,31 @@
 use serde_json::value::Value as Json;
 
+use context::Context;
+use error::RenderError;
 use helpers::{HelperDef, HelperResult};
+use output::Output;
 use registry::Registry;
-use context::JsonRender;
 use render::{Helper, RenderContext};
-use error::RenderError;
+use value::JsonRender;
 
 #[derive(Clone, Copy)]
 pub struct LookupHelper;
 
 impl HelperDef for LookupHelper {
-    fn call(&self, h: &Helper, _: &Registry, rc: &mut RenderContext) -> HelperResult {
-        let collection_value = try!(
-            h.param(0)
-                .ok_or_else(|| RenderError::new("Param not found for helper \"lookup\""))
-        );
-        let index = try!(
-            h.param(1)
-                .ok_or_else(|| RenderError::new("Insufficient params for helper \"lookup\""))
-        );
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        h: &Helper,
+        _: &Registry,
+        _: &Context,
+        _: &mut RenderContext,
+        out: &mut Output,
+    ) -> HelperResult {
+        let collection_value = h
+            .param(0)
+            .ok_or_else(|| RenderError::new("Param not found for helper \"lookup\""))?;
+        let index = h
+            .param(1)
+            .ok_or_else(|| RenderError::new("Insufficient params for helper \"lookup\""))?;
 
         let null = Json::Null;
         let value = match collection_value.value() {
@@ -36,7 +43,7 @@ impl HelperDef for LookupHelper {
             _ => &null,
         };
         let r = value.render();
-        try!(rc.writer.write(r.into_bytes().as_ref()));
+        out.write(r.as_ref())?;
         Ok(())
     }
 }
diff --git a/src/helpers/helper_raw.rs b/src/helpers/helper_raw.rs
index 20ad54f..1bba94c 100644
--- a/src/helpers/helper_raw.rs
+++ b/src/helpers/helper_raw.rs
@@ -1,4 +1,6 @@
+use context::Context;
 use helpers::{HelperDef, HelperResult};
+use output::Output;
 use registry::Registry;
 use render::{Helper, RenderContext, Renderable};
 
@@ -6,10 +8,17 @@ use render::{Helper, RenderContext, Renderable};
 pub struct RawHelper;
 
 impl HelperDef for RawHelper {
-    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> HelperResult {
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        h: &Helper,
+        r: &Registry,
+        ctx: &Context,
+        rc: &mut RenderContext,
+        out: &mut Output,
+    ) -> HelperResult {
         let tpl = h.template();
         if let Some(t) = tpl {
-            t.render(r, rc)
+            t.render(r, ctx, rc, out)
         } else {
             Ok(())
         }
diff --git a/src/helpers/helper_with.rs b/src/helpers/helper_with.rs
index 3446383..0e1a67c 100644
--- a/src/helpers/helper_with.rs
+++ b/src/helpers/helper_with.rs
@@ -1,20 +1,28 @@
 use std::collections::BTreeMap;
 
+use context::Context;
+use error::RenderError;
 use helpers::{HelperDef, HelperResult};
+use output::Output;
 use registry::Registry;
-use context::{to_json, JsonTruthy};
 use render::{Helper, RenderContext, Renderable};
-use error::RenderError;
+use value::{to_json, JsonTruthy};
 
 #[derive(Clone, Copy)]
 pub struct WithHelper;
 
 impl HelperDef for WithHelper {
-    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> HelperResult {
-        let param = try!(
-            h.param(0)
-                .ok_or_else(|| RenderError::new("Param not found for helper \"with\""))
-        );
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        h: &Helper,
+        r: &Registry,
+        ctx: &Context,
+        rc: &mut RenderContext,
+        out: &mut Output,
+    ) -> HelperResult {
+        let param = h
+            .param(0)
+            .ok_or_else(|| RenderError::new("Param not found for helper \"with\""))?;
 
         rc.promote_local_vars();
 
@@ -46,7 +54,7 @@ impl HelperDef for WithHelper {
             }
 
             let result = match template {
-                Some(t) => t.render(r, &mut local_rc),
+                Some(t) => t.render(r, ctx, &mut local_rc, out),
                 None => Ok(()),
             };
 
@@ -71,7 +79,7 @@ pub static WITH_HELPER: WithHelper = WithHelper;
 #[cfg(test)]
 mod test {
     use registry::Registry;
-    use context::to_json;
+    use value::to_json;
 
     #[derive(Serialize)]
     struct Address {
diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs
index 9bee5b7..751b4dd 100644
--- a/src/helpers/mod.rs
+++ b/src/helpers/mod.rs
@@ -1,15 +1,18 @@
-use render::{Helper, RenderContext};
-use context::JsonRender;
-use registry::Registry;
+use context::Context;
 use error::RenderError;
-use serde_json::Value as Json;
+use output::Output;
+use registry::Registry;
+use render::{Helper, RenderContext};
+use value::ScopedJson;
 
-pub use self::helper_if::{IF_HELPER, UNLESS_HELPER};
 pub use self::helper_each::EACH_HELPER;
-pub use self::helper_with::WITH_HELPER;
+pub use self::helper_if::{IF_HELPER, UNLESS_HELPER};
+pub use self::helper_log::LOG_HELPER;
 pub use self::helper_lookup::LOOKUP_HELPER;
 pub use self::helper_raw::RAW_HELPER;
-pub use self::helper_log::LOG_HELPER;
+pub use self::helper_with::WITH_HELPER;
+
+pub type HelperResult = Result<(), RenderError>;
 
 /// Helper Definition
 ///
@@ -23,13 +26,14 @@ pub use self::helper_log::LOG_HELPER;
 ///
 /// ## Define an inline helper
 ///
-/// ```ignore
+/// ```
 /// use handlebars::*;
 ///
-/// fn upper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> HelperResult {
+/// fn upper(h: &Helper, _: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut Output)
+///     -> HelperResult {
 ///    // get parameter from helper or throw an error
-///    let param = h.param(0).and_then(|v| v.value().as_string()).unwrap_or("");
-///    try!(rc.writer.write(param.to_uppercase().into_bytes().as_ref()));
+///    let param = h.param(0).and_then(|v| v.value().as_str()).unwrap_or("");
+///    out.write(param.to_uppercase().as_ref())?;
 ///    Ok(())
 /// }
 /// ```
@@ -41,28 +45,34 @@ pub use self::helper_log::LOG_HELPER;
 /// ```
 /// use handlebars::*;
 ///
-/// fn dummy_block(h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> HelperResult {
-///     h.template().map(|t| t.render(r, rc)).unwrap_or(Ok(()))
+/// fn dummy_block(h: &Helper, r: &Handlebars, ctx: &Context, rc: &mut RenderContext, out: &mut Output) -> HelperResult {
+///     h.template().map(|t| t.render(r, ctx, rc, out)).unwrap_or(Ok(()))
 /// }
 /// ```
 ///
 ///
 
-pub type HelperResult = Result<(), RenderError>;
-
 pub trait HelperDef: Send + Sync {
-    fn call_inner(
+    fn call_inner<'reg: 'rc, 'rc>(
         &self,
-        _: &Helper,
-        _: &Registry,
+        _: &Helper<'reg, 'rc>,
+        _: &'reg Registry,
+        _: &'rc Context,
         _: &mut RenderContext,
-    ) -> Result<Option<Json>, RenderError> {
+    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
         Ok(None)
     }
 
-    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> HelperResult {
-        if let Some(result) = self.call_inner(h, r, rc)? {
-            rc.writer.write(result.render().into_bytes().as_ref())?;
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        h: &Helper<'reg, 'rc>,
+        r: &'reg Registry,
+        ctx: &'rc Context,
+        rc: &mut RenderContext,
+        out: &mut Output,
+    ) -> HelperResult {
+        if let Some(result) = self.call_inner(h, r, ctx, rc)? {
+            out.write(result.render().as_ref())?;
         }
 
         Ok(())
@@ -71,22 +81,35 @@ pub trait HelperDef: Send + Sync {
 
 /// implement HelperDef for bare function so we can use function as helper
 impl<
-    F: Send
-        + Sync
-        + for<'b, 'c, 'd> Fn(&'b Helper, &'c Registry, &'d mut RenderContext) -> HelperResult,
-> HelperDef for F
+        F: Send
+            + Sync
+            + for<'reg, 'rc> Fn(
+                &Helper<'reg, 'rc>,
+                &'reg Registry,
+                &'rc Context,
+                &mut RenderContext,
+                &mut Output,
+            ) -> HelperResult,
+    > HelperDef for F
 {
-    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> HelperResult {
-        (*self)(h, r, rc)
+    fn call<'reg: 'rc, 'rc>(
+        &self,
+        h: &Helper<'reg, 'rc>,
+        r: &'reg Registry,
+        ctx: &'rc Context,
+        rc: &mut RenderContext,
+        out: &mut Output,
+    ) -> HelperResult {
+        (*self)(h, r, ctx, rc, out)
     }
 }
 
-mod helper_if;
 mod helper_each;
-mod helper_with;
+mod helper_if;
+mod helper_log;
 mod helper_lookup;
 mod helper_raw;
-mod helper_log;
+mod helper_with;
 
 // pub type HelperDef = for <'a, 'b, 'c> Fn<(&'a Context, &'b Helper, &'b Registry, &'c mut RenderContext), Result<String, RenderError>>;
 //
@@ -99,32 +122,36 @@ mod helper_log;
 mod test {
     use std::collections::BTreeMap;
 
-    use context::JsonRender;
+    use context::Context;
+    use error::RenderError;
     use helpers::HelperDef;
+    use output::Output;
     use registry::Registry;
     use render::{Helper, RenderContext, Renderable};
-    use error::RenderError;
+    use value::JsonRender;
 
     #[derive(Clone, Copy)]
     struct MetaHelper;
 
     impl HelperDef for MetaHelper {
-        fn call(
+        fn call<'reg: 'rc, 'rc>(
             &self,
             h: &Helper,
             r: &Registry,
+            ctx: &Context,
             rc: &mut RenderContext,
+            out: &mut Output,
         ) -> Result<(), RenderError> {
             let v = h.param(0).unwrap();
 
             if !h.is_block() {
                 let output = format!("{}:{}", h.name(), v.value().render());
-                try!(rc.writer.write(output.into_bytes().as_ref()));
+                out.write(output.as_ref())?;
             } else {
                 let output = format!("{}:{}", h.name(), v.value().render());
-                try!(rc.writer.write(output.into_bytes().as_ref()));
-                try!(rc.writer.write("->".as_bytes()));
-                try!(h.template().unwrap().render(r, rc));
+                out.write(output.as_ref())?;
+                out.write("->")?;
+                h.template().unwrap().render(r, ctx, rc, out)?;
             };
             Ok(())
         }
@@ -167,9 +194,14 @@ mod test {
         handlebars.register_helper(
             "helperMissing",
             Box::new(
-                |h: &Helper, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
+                |h: &Helper,
+                 _: &Registry,
+                 _: &Context,
+                 _: &mut RenderContext,
+                 out: &mut Output|
+                 -> Result<(), RenderError> {
                     let output = format!("{}{}", h.name(), h.param(0).unwrap().value());
-                    try!(rc.writer.write(output.into_bytes().as_ref()));
+                    out.write(output.as_ref())?;
                     Ok(())
                 },
             ),
@@ -177,9 +209,14 @@ mod test {
         handlebars.register_helper(
             "foo",
             Box::new(
-                |h: &Helper, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
+                |h: &Helper,
+                 _: &Registry,
+                 _: &Context,
+                 _: &mut RenderContext,
+                 out: &mut Output|
+                 -> Result<(), RenderError> {
                     let output = format!("{}", h.hash_get("value").unwrap().value().render());
-                    try!(rc.writer.write(output.into_bytes().as_ref()));
+                    out.write(output.as_ref())?;
                     Ok(())
                 },
             ),
diff --git a/src/lib.rs b/src/lib.rs
index b5ca489..5ec08d0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,4 @@
+#![doc(html_root_url = "https://docs.rs/crate/handlebars/")]
 //! # Handlebars
 //!
 //! [Handlebars](http://handlebarsjs.com/) is a modern and extensible templating solution originally created in the JavaScript world. It's used by many popular frameworks like [Ember.js](http://emberjs.com) and Chaplin. It's also ported to some other platforms such as [Java](https://github.com/jknack/handlebars.java).
@@ -120,16 +121,16 @@
 //! Templates are created from String and registered to `Handlebars` with a name.
 //!
 //! ```
-//! extern crate handlebars;
+//! # extern crate handlebars;
 //!
 //! use handlebars::Handlebars;
 //!
-//! fn main() {
+//! # fn main() {
 //!   let mut handlebars = Handlebars::new();
 //!   let source = "hello {{world}}";
 //!
 //!   assert!(handlebars.register_template_string("t1", source).is_ok())
-//! }
+//! # }
 //! ```
 //!
 //! On registeration, the template is parsed, compiled and cached in the registry. So further
@@ -141,17 +142,22 @@
 //! without registration.
 //!
 //! ```
+//! # use std::error::Error;
 //! use handlebars::Handlebars;
 //! use std::collections::BTreeMap;
 //!
-//! fn main() {
+//! # fn _main() -> Result<(), Box<Error>> {
 //!   let mut handlebars = Handlebars::new();
 //!   let source = "hello {{world}}";
 //!
 //!   let mut data = BTreeMap::new();
 //!   data.insert("world".to_string(), "世界!".to_string());
-//!   assert_eq!(handlebars.render_template(source, &data).unwrap(),"hello 世界!".to_owned());
-//! }
+//!   assert_eq!(handlebars.render_template(source, &data)?, "hello 世界!".to_owned());
+//! # Ok(())
+//! # }
+//! # fn main() {
+//! #   _main().unwrap();
+//! # }
 //! ```
 //!
 //! ### Rendering Something
@@ -162,8 +168,11 @@
 //!
 //! You can use default `render` function to render a template into `String`. From 0.9, there's `renderw` to render text into anything of `std::io::Write`.
 //!
-//! ```ignore
-//! use std::collections::BTreeMap;
+//! ```
+//! # use std::error::Error;
+//! # #[macro_use]
+//! # extern crate serde_derive;
+//! # extern crate handlebars;
 //!
 //! use handlebars::Handlebars;
 //!
@@ -173,7 +182,7 @@
 //!   age: i16,
 //! }
 //!
-//! fn main() {
+//! # fn _main() -> Result<(), Box<Error>> {
 //!   let source = "Hello, {{name}}";
 //!
 //!   let mut handlebars = Handlebars::new();
@@ -184,15 +193,29 @@
 //!       name: "Ning Sun".to_string(),
 //!       age: 27
 //!   };
-//!   assert_eq!(handlebars.render("hello", &data).unwrap(), "Hello, Ning Sun".to_owned());
-//! }
+//!   assert_eq!(handlebars.render("hello", &data)?, "Hello, Ning Sun".to_owned());
+//! # Ok(())
+//! # }
+//! #
+//! # fn main() { _main().unwrap(); }
 //! ```
 //!
 //! Or if you don't need the template to be cached or referenced by other ones, you can
 //! simply render it without registering.
 //!
-//! ```ignore
-//! fn main() {
+//! ```
+//! # use std::error::Error;
+//! # #[macro_use]
+//! # extern crate serde_derive;
+//! # extern crate handlebars;
+//! use handlebars::Handlebars;
+//! # #[derive(Serialize)]
+//! # struct Person {
+//! #  name: String,
+//! #  age: i16,
+//! # }
+//!
+//! # fn _main() -> Result<(), Box<Error>> {
 //!   let source = "Hello, {{name}}";
 //!
 //!   let mut handlebars = Handlebars::new();
@@ -201,9 +224,11 @@
 //!       name: "Ning Sun".to_string(),
 //!       age: 27
 //!   };
-//!   assert_eq!(handlebars.render_template("Hello, {{name}}", &data).unwrap(),
+//!   assert_eq!(handlebars.render_template("Hello, {{name}}", &data)?,
 //!       "Hello, Ning Sun".to_owned());
-//! }
+//! # Ok(())
+//! # }
+//! # fn main() { _main().unwrap(); }
 //! ```
 //!
 //! #### Escaping
@@ -216,50 +241,54 @@
 //!
 //! ```
 //! use std::io::Write;
-//! use handlebars::{Handlebars, HelperDef, RenderContext, Helper, Context, JsonRender, HelperResult};
+//! # use std::error::Error;
+//! use handlebars::{Handlebars, HelperDef, RenderContext, Helper, Context, JsonRender, HelperResult, Output, RenderError};
 //!
 //! // implement by a structure impls HelperDef
 //! #[derive(Clone, Copy)]
 //! struct SimpleHelper;
 //!
 //! impl HelperDef for SimpleHelper {
-//!   fn call(&self, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> HelperResult {
+//!   fn call<'reg: 'rc, 'rc>(&self, h: &Helper, _: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut Output) -> HelperResult {
 //!     let param = h.param(0).unwrap();
 //!
-//!     try!(rc.writer.write("1st helper: ".as_bytes()));
-//!     try!(rc.writer.write(param.value().render().into_bytes().as_ref()));
+//!     out.write("1st helper: ")?;
+//!     out.write(param.value().render().as_ref())?;
 //!     Ok(())
 //!   }
 //! }
 //!
 //! // implement via bare function
-//! fn another_simple_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> HelperResult {
+//! fn another_simple_helper (h: &Helper, _: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut Output) -> HelperResult {
 //!     let param = h.param(0).unwrap();
 //!
-//!     try!(rc.writer.write("2nd helper: ".as_bytes()));
-//!     try!(rc.writer.write(param.value().render().into_bytes().as_ref()));
+//!     out.write("2nd helper: ")?;
+//!     out.write(param.value().render().as_ref())?;
 //!     Ok(())
 //! }
 //!
 //!
-//! fn main() {
+//! # fn _main() -> Result<(), Box<Error>> {
 //!   let mut handlebars = Handlebars::new();
 //!   handlebars.register_helper("simple-helper", Box::new(SimpleHelper));
 //!   handlebars.register_helper("another-simple-helper", Box::new(another_simple_helper));
 //!   // via closure
 //!   handlebars.register_helper("closure-helper",
-//!       Box::new(|h: &Helper, r: &Handlebars, rc: &mut RenderContext| -> HelperResult {
-//!           let param = h.param(0).unwrap();
+//!       Box::new(|h: &Helper, r: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut Output| -> HelperResult {
+//!           let param = h.param(0).ok_or(RenderError::new("param not found"))?;
 //!
-//!           try!(rc.writer.write("3rd helper: ".as_bytes()));
-//!           try!(rc.writer.write(param.value().render().into_bytes().as_ref()));
+//!           out.write("3rd helper: ")?;
+//!           out.write(param.value().render().as_ref())?;
 //!           Ok(())
 //!       }));
 //!
 //!   let tpl = "{{simple-helper 1}}\n{{another-simple-helper 2}}\n{{closure-helper 3}}";
-//!   assert_eq!(handlebars.render_template(tpl, &()).unwrap(),
+//!   assert_eq!(handlebars.render_template(tpl, &())?,
 //!       "1st helper: 1\n2nd helper: 2\n3rd helper: 3".to_owned());
-//! }
+//! # Ok(())
+//! # }
+//!
+//! # fn main() { _main().unwrap(); }
 //! ```
 //! Data available to helper can be found in [Helper](struct.Helper.html). And there are more
 //! examples in [HelperDef](trait.HelperDef.html) page.
@@ -312,23 +341,26 @@ extern crate serde;
 extern crate serde_json;
 extern crate walkdir;
 
-pub use self::template::Template;
+pub use self::context::Context;
+pub use self::directives::DirectiveDef as DecoratorDef;
 pub use self::error::{RenderError, TemplateError, TemplateFileError, TemplateRenderError};
-pub use self::registry::{html_escape, no_escape, EscapeFn, Registry as Handlebars};
-pub use self::render::{ContextJson, Directive as Decorator, Evaluable, Helper, RenderContext,
-                       Renderable};
 pub use self::helpers::{HelperDef, HelperResult};
-pub use self::directives::DirectiveDef as DecoratorDef;
-pub use self::context::{to_json, Context, JsonRender};
+pub use self::output::Output;
+pub use self::registry::{html_escape, no_escape, EscapeFn, Registry as Handlebars};
+pub use self::render::{Directive as Decorator, Evaluable, Helper, RenderContext, Renderable};
 pub use self::support::str::StringWriter;
+pub use self::template::Template;
+pub use self::value::{to_json, JsonRender, PathAndJson, ScopedJson};
 
-mod grammar;
-mod template;
+mod context;
+mod directives;
 mod error;
+mod grammar;
+mod helpers;
+mod output;
+mod partial;
 mod registry;
 mod render;
-mod helpers;
-mod context;
 mod support;
-mod directives;
-mod partial;
+mod template;
+mod value;
diff --git a/src/output.rs b/src/output.rs
new file mode 100644
index 0000000..2d8d757
--- /dev/null
+++ b/src/output.rs
@@ -0,0 +1,47 @@
+use std::io::{Error as IOError, Write};
+use std::string::FromUtf8Error;
+
+pub trait Output {
+    fn write(&mut self, seg: &str) -> Result<(), IOError>;
+}
+
+pub struct WriteOutput<W: Write> {
+    write: W,
+}
+
+impl<W: Write> Output for WriteOutput<W> {
+    fn write(&mut self, seg: &str) -> Result<(), IOError> {
+        self.write.write_all(seg.as_bytes())
+    }
+}
+
+impl<W: Write> WriteOutput<W> {
+    pub fn new(write: W) -> WriteOutput<W> {
+        WriteOutput { write }
+    }
+}
+
+pub struct StringOutput {
+    buf: Vec<u8>,
+}
+
+impl Output for StringOutput {
+    fn write(&mut self, seg: &str) -> Result<(), IOError> {
+        for b in seg.as_bytes() {
+            self.buf.push(*b);
+        }
+        Ok(())
+    }
+}
+
+impl StringOutput {
+    pub fn new() -> StringOutput {
+        StringOutput {
+            buf: Vec::with_capacity(8 * 1024),
+        }
+    }
+
+    pub fn to_string(self) -> Result<String, FromUtf8Error> {
+        String::from_utf8(self.buf)
+    }
+}
diff --git a/src/partial.rs b/src/partial.rs
index 481fb59..9839f2e 100644
--- a/src/partial.rs
+++ b/src/partial.rs
@@ -1,74 +1,81 @@
+use std::borrow::Borrow;
 use std::collections::BTreeMap;
 use std::iter::FromIterator;
-use std::borrow::Borrow;
 use std::rc::Rc;
 
-use template::Template;
-use registry::Registry;
 use context::{merge_json, Context};
-use render::{Directive, Evaluable, RenderContext, Renderable};
 use error::RenderError;
+use output::Output;
+use registry::Registry;
+use render::{Directive, Evaluable, RenderContext, Renderable};
+use template::Template;
 
-fn render_partial(
-    t: &Template,
-    d: &Directive,
-    r: &Registry,
+fn render_partial<'reg: 'rc, 'rc>(
+    t: &'reg Template,
+    d: &Directive<'reg, 'rc>,
+    r: &'reg Registry,
+    ctx: &'rc Context,
     local_rc: &mut RenderContext,
+    out: &mut Output,
 ) -> Result<(), RenderError> {
-    let context_param = d.params().get(0).and_then(|p| p.path());
-    if let Some(p) = context_param {
-        let old_path = local_rc.get_path().clone();
-        local_rc.promote_local_vars();
-        let new_path = format!("{}/{}", old_path, p);
-        local_rc.set_path(new_path);
+    if let Some(ref p) = d.param(0) {
+        if let Some(ref param_path) = p.path() {
+            let old_path = local_rc.get_path().clone();
+            local_rc.promote_local_vars();
+            let new_path = format!("{}/{}", old_path, param_path);
+            local_rc.set_path(new_path);
+        }
     };
 
     // @partial-block
     if let Some(t) = d.template() {
+        // FIXME: avoid clone here possibly
         local_rc.set_partial("@partial-block".to_string(), Rc::new(t.clone()));
     }
 
-    let hash = d.hash();
-    if hash.is_empty() {
-        t.render(r, local_rc)
+    if d.hash().is_empty() {
+        t.render(r, ctx, local_rc, out)
     } else {
         let hash_ctx =
             BTreeMap::from_iter(d.hash().iter().map(|(k, v)| (k.clone(), v.value().clone())));
-        let partial_context = merge_json(local_rc.evaluate(".", r.strict_mode())?, &hash_ctx);
-        let mut partial_rc = local_rc.with_context(Context::wraps(&partial_context)?);
-        t.render(r, &mut partial_rc)
+        let partial_context = merge_json(local_rc.evaluate(ctx, ".", r.strict_mode())?, &hash_ctx);
+        let ctx = Context::wraps(&partial_context)?;
+        let mut partial_rc = local_rc.new_for_block();
+        t.render(r, &ctx, &mut partial_rc, out)
     }
 }
 
-pub fn expand_partial(
-    d: &Directive,
-    r: &Registry,
+pub fn expand_partial<'reg: 'rc, 'rc>(
+    d: &Directive<'reg, 'rc>,
+    r: &'reg Registry,
+    ctx: &'rc Context,
     rc: &mut RenderContext,
+    out: &mut Output,
 ) -> Result<(), RenderError> {
     // try eval inline partials first
     if let Some(t) = d.template() {
-        try!(t.eval(r, rc));
+        t.eval(r, ctx, rc)?;
     }
 
-    if rc.is_current_template(d.name()) {
+    let tname = d.name();
+    if rc.is_current_template(tname) {
         return Err(RenderError::new("Cannot include self in >"));
     }
 
-    let tname = d.name();
     let partial = rc.get_partial(tname);
 
     match partial {
         Some(t) => {
             let mut local_rc = rc.derive();
-            render_partial(t.borrow(), d, r, &mut local_rc)
+            render_partial(t.borrow(), d, r, ctx, &mut local_rc, out)?;
         }
-        None => if let Some(t) = r.get_template(tname).or(d.template()) {
+        None => if let Some(t) = r.get_template(tname.as_ref()).or(d.template()) {
             let mut local_rc = rc.derive();
-            render_partial(t, d, r, &mut local_rc)
-        } else {
-            Ok(())
+            render_partial(t, d, r, ctx, &mut local_rc, out)?;
         },
     }
+
+    Ok(())
 }
 
 #[cfg(test)]
diff --git a/src/registry.rs b/src/registry.rs
index ea95fd1..3ed0ab0 100644
--- a/src/registry.rs
+++ b/src/registry.rs
@@ -1,25 +1,25 @@
 use std::collections::HashMap;
-use std::io::prelude::*;
-use std::io;
+use std::fmt::{self, Debug, Formatter};
 use std::fs::File;
+use std::io::prelude::*;
 use std::path::Path;
-use std::fmt::{self, Debug, Formatter};
 
 use serde::Serialize;
 
 use regex::{Captures, Regex};
 
-use template::Template;
-use render::{RenderContext, Renderable};
 use context::Context;
-use helpers::{self, HelperDef};
 use directives::{self, DirectiveDef};
-use support::str::StringWriter;
 use error::{RenderError, TemplateError, TemplateFileError, TemplateRenderError};
+use helpers::{self, HelperDef};
+use output::{Output, StringOutput, WriteOutput};
+use render::{RenderContext, Renderable};
+use support::str::StringWriter;
+use template::Template;
 
 use walkdir::WalkDir;
 
-lazy_static!{
+lazy_static! {
     static ref DEFAULT_REPLACE: Regex = Regex::new(">|<|\"|&").unwrap();
 }
 
@@ -145,10 +145,8 @@ impl Registry {
     where
         S: AsRef<str>,
     {
-        try!(
-            Template::compile_with_name(tpl_str, name.to_owned(), self.source_map)
-                .and_then(|t| Ok(self.templates.insert(name.to_string(), t)))
-        );
+        Template::compile_with_name(tpl_str, name.to_owned(), self.source_map)
+            .and_then(|t| Ok(self.templates.insert(name.to_string(), t)))?;
         Ok(())
     }
 
@@ -173,7 +171,7 @@ impl Registry {
         P: AsRef<Path>,
     {
         let mut file =
-            try!(File::open(tpl_path).map_err(|e| TemplateFileError::IOError(e, name.to_owned())));
+            File::open(tpl_path).map_err(|e| TemplateFileError::IOError(e, name.to_owned()))?;
         self.register_template_source(name, &mut file)
     }
 
@@ -203,7 +201,8 @@ impl Registry {
             let tpl_path = entry.path();
             let tpl_file_path = entry.path().to_string_lossy();
             if entry.metadata()?.is_file() && tpl_file_path.ends_with(tpl_extension) {
-                let tpl_name = &tpl_file_path[prefix_len..tpl_file_path.len() - tpl_extension.len()];
+                let tpl_name =
+                    &tpl_file_path[prefix_len..tpl_file_path.len() - tpl_extension.len()];
                 let tpl_canonical_name = tpl_name.replace("\\", "/");
                 self.register_template_file(&tpl_canonical_name, &tpl_path)?;
             }
@@ -219,13 +218,10 @@ impl Registry {
         tpl_source: &mut Read,
     ) -> Result<(), TemplateFileError> {
         let mut buf = String::new();
-        try!(
-            tpl_source
-                .read_to_string(&mut buf)
-                .map_err(|e| TemplateFileError::IOError(e, name.to_owned()))
-        );
-        try!(self.register_template_string(name, buf));
-
+        tpl_source
+            .read_to_string(&mut buf)
+            .map_err(|e| TemplateFileError::IOError(e, name.to_owned()))?;
+        self.register_template_string(name, buf)?;
         Ok(())
     }
 
@@ -295,6 +291,25 @@ impl Registry {
         self.templates.clear();
     }
 
+    fn render_to_output<T>(
+        &self,
+        name: &str,
+        data: &T,
+        output: &mut Output,
+    ) -> Result<(), RenderError>
+    where
+        T: Serialize,
+    {
+        self.get_template(&name.to_string())
+            .ok_or(RenderError::new(format!("Template not found: {}", name)))
+            .and_then(|t| {
+                let ctx = Context::wraps(data)?;
+                let mut render_context = RenderContext::new(t.name.clone());
+                t.render(self, &ctx, &mut render_context, output)
+            })
+            .map(|_| ())
+    }
+
     /// Render a registered template with some data into a string
     ///
     /// * `name` is the template name you registred previously
@@ -305,32 +320,19 @@ impl Registry {
     where
         T: Serialize,
     {
-        let mut writer = StringWriter::new();
-        {
-            try!(self.render_to_write(name, data, &mut writer));
-        }
-        Ok(writer.to_string())
+        let mut output = StringOutput::new();
+        self.render_to_output(name, data, &mut output)?;
+        output.to_string().map_err(RenderError::from)
     }
 
     /// Render a registered template and write some data to the `std::io::Write`
-    pub fn render_to_write<T>(
-        &self,
-        name: &str,
-        data: &T,
-        writer: &mut Write,
-    ) -> Result<(), RenderError>
+    pub fn render_to_write<T, W>(&self, name: &str, data: &T, writer: W) -> Result<(), RenderError>
     where
         T: Serialize,
+        W: Write,
     {
-        self.get_template(&name.to_string())
-            .ok_or(RenderError::new(format!("Template not found: {}", name)))
-            .and_then(|t| {
-                let ctx = try!(Context::wraps(data));
-                let mut local_helpers = HashMap::new();
-                let mut render_context = RenderContext::new(ctx, &mut local_helpers, writer);
-                render_context.root_template = t.name.clone();
-                t.render(self, &mut render_context)
-            })
+        let mut output = WriteOutput::new(writer);
+        self.render_to_output(name, data, &mut output)
     }
 
     /// render a template string using current registry without register it
@@ -343,119 +345,75 @@ impl Registry {
         T: Serialize,
     {
         let mut writer = StringWriter::new();
-        {
-            try!(self.render_template_to_write(template_string, data, &mut writer));
-        }
+        self.render_template_to_write(template_string, data, &mut writer)?;
         Ok(writer.to_string())
     }
 
     /// render a template string using current registry without register it
-    pub fn render_template_to_write<T>(
+    pub fn render_template_to_write<T, W>(
         &self,
         template_string: &str,
         data: &T,
-        writer: &mut Write,
+        writer: W,
     ) -> Result<(), TemplateRenderError>
     where
         T: Serialize,
+        W: Write,
     {
-        let tpl = try!(Template::compile2(template_string, self.source_map));
-        let ctx = try!(Context::wraps(data));
-        let mut local_helpers = HashMap::new();
-        let mut render_context = RenderContext::new(ctx, &mut local_helpers, writer);
-        tpl.render(self, &mut render_context)
+        let tpl = Template::compile2(template_string, self.source_map)?;
+        let ctx = Context::wraps(data)?;
+        let mut render_context = RenderContext::new(None);
+        let mut out = WriteOutput::new(writer);
+        tpl.render(self, &ctx, &mut render_context, &mut out)
+            .map(|_| ())
             .map_err(TemplateRenderError::from)
     }
 
     /// render a template source using current registry without register it
-    pub fn render_template_source_to_write<T>(
+    pub fn render_template_source_to_write<T, W>(
         &self,
         template_source: &mut Read,
         data: &T,
-        writer: &mut Write,
+        writer: W,
     ) -> Result<(), TemplateRenderError>
     where
         T: Serialize,
+        W: Write,
     {
         let mut tpl_str = String::new();
-        try!(
-            template_source
-                .read_to_string(&mut tpl_str)
-                .map_err(|e| TemplateRenderError::IOError(e, "Unamed template source".to_owned()))
-        );
+        template_source
+            .read_to_string(&mut tpl_str)
+            .map_err(|e| TemplateRenderError::IOError(e, "Unamed template source".to_owned()))?;
         self.render_template_to_write(&tpl_str, data, writer)
     }
-
-    #[deprecated(since = "0.30.0", note = "Please use render_to_write instead.")]
-    pub fn renderw<T>(&self, name: &str, data: &T, writer: &mut Write) -> Result<(), RenderError>
-    where
-        T: Serialize,
-    {
-        self.render_to_write(name, data, writer)
-    }
-
-    #[deprecated(since = "0.30.0", note = "Please use render_template instead.")]
-    pub fn template_render<T>(
-        &self,
-        template_string: &str,
-        data: &T,
-    ) -> Result<String, TemplateRenderError>
-    where
-        T: Serialize,
-    {
-        self.render_template(template_string, data)
-    }
-
-    #[deprecated(since = "0.30.0", note = "Please use render_template_to_write instead.")]
-    pub fn template_renderw<T>(
-        &self,
-        template_string: &str,
-        data: &T,
-        writer: &mut Write,
-    ) -> Result<(), TemplateRenderError>
-    where
-        T: Serialize,
-    {
-        self.render_template_to_write(template_string, data, writer)
-    }
-
-    #[deprecated(since = "0.30.0", note = "Please use render_template_source_to_write instead.")]
-    pub fn template_renderw2<T>(
-        &self,
-        template_source: &mut Read,
-        data: &T,
-        writer: &mut Write,
-    ) -> Result<(), TemplateRenderError>
-    where
-        T: Serialize,
-    {
-        self.render_template_source_to_write(template_source, data, writer)
-    }
 }
 
 #[cfg(test)]
 mod test {
+    use context::Context;
+    use error::RenderError;
+    use helpers::HelperDef;
+    use output::Output;
     use registry::Registry;
     use render::{Helper, RenderContext, Renderable};
-    use helpers::HelperDef;
+    use std::fs::{DirBuilder, File};
+    use std::io::Write;
     use support::str::StringWriter;
-    use error::RenderError;
     use tempfile::tempdir;
-    use std::io::Write;
-    use std::fs::{File, DirBuilder};
 
     #[derive(Clone, Copy)]
     struct DummyHelper;
 
     impl HelperDef for DummyHelper {
-        fn call(
+        fn call<'reg: 'rc, 'rc>(
             &self,
             h: &Helper,
             r: &Registry,
+            ctx: &Context,
             rc: &mut RenderContext,
+            out: &mut Output,
         ) -> Result<(), RenderError> {
-            try!(h.template().unwrap().render(r, rc));
-            Ok(())
+            h.template().unwrap().render(r, ctx, rc, out)
         }
     }
 
@@ -547,8 +505,12 @@ mod test {
             let dir = tempdir().unwrap();
 
             let _ = DirBuilder::new().create(dir.path().join("french")).unwrap();
-            let _ = DirBuilder::new().create(dir.path().join("portugese")).unwrap();
-            let _ = DirBuilder::new().create(dir.path().join("italian")).unwrap();
+            let _ = DirBuilder::new()
+                .create(dir.path().join("portugese"))
+                .unwrap();
+            let _ = DirBuilder::new()
+                .create(dir.path().join("italian"))
+                .unwrap();
 
             let file1_path = dir.path().join("french/t7.hbs");
             let mut file1: File = File::create(&file1_path).unwrap();
@@ -614,9 +576,7 @@ mod test {
     #[test]
     fn test_escape() {
         let r = Registry::new();
-        let data = json!({
-            "hello": "world"
-        });
+        let data = json!({"hello": "world"});
 
         assert_eq!(
             "{{hello}}",
@@ -628,10 +588,7 @@ mod test {
             r.render_template(r" \{{hello}}", &data).unwrap()
         );
 
-        assert_eq!(
-            r"\world",
-            r.render_template(r"\\{{hello}}", &data).unwrap()
-        );
+        assert_eq!(r"\world", r.render_template(r"\\{{hello}}", &data).unwrap());
     }
 
     #[test]
@@ -655,9 +612,9 @@ mod test {
                 .is_err()
         );
 
-        let render_error =
-            r.render_template("accessing non-exists key {{the_key_never_exists}}", &data)
-                .unwrap_err();
+        let render_error = r
+            .render_template("accessing non-exists key {{the_key_never_exists}}", &data)
+            .unwrap_err();
         assert_eq!(
             render_error.as_render_error().unwrap().column_no.unwrap(),
             26
@@ -672,7 +629,8 @@ mod test {
             r.render_template("accessing invalid array index {{this.[3]}}", &data2)
                 .is_err()
         );
-        let render_error2 = r.render_template("accessing invalid array index {{this.[3]}}", &data2)
+        let render_error2 = r
+            .render_template("accessing invalid array index {{this.[3]}}", &data2)
             .unwrap_err();
         assert_eq!(
             render_error2.as_render_error().unwrap().column_no.unwrap(),
diff --git a/src/render.rs b/src/render.rs
index f6bec6f..785c60c 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -1,23 +1,23 @@
 use std::borrow::Borrow;
 use std::collections::{BTreeMap, HashMap, VecDeque};
 use std::fmt;
-use std::io::Write;
 use std::rc::Rc;
 
 use serde::Serialize;
 use serde_json::value::Value as Json;
 
-use context::{Context, JsonRender};
+use context::Context;
 use error::RenderError;
 use helpers::HelperDef;
+use output::{Output, StringOutput};
 use partial;
 use registry::Registry;
-use support::str::StringWriter;
 use template::TemplateElement::*;
 use template::{
-    BlockParam, Directive as DirectiveTemplate, HelperTemplate, Parameter, Template,
-    TemplateElement, TemplateMapping,
+    BlockParam, DirectiveTemplate, HelperTemplate, Parameter, Template, TemplateElement,
+    TemplateMapping,
 };
+use value::{JsonRender, PathAndJson, ScopedJson};
 
 static DEFAULT_VALUE: Json = Json::Null;
 
@@ -26,328 +26,356 @@ static DEFAULT_VALUE: Json = Json::Null;
 /// this context stores information of a render and a writer where generated
 /// content is written to.
 ///
-pub struct RenderContext<'a> {
+#[derive(Clone, Debug)]
+pub struct RenderContext {
+    inner: Rc<RenderContextInner>,
+    block: Rc<BlockRenderContext>,
+    // copy-on-write context
+    modified_context: Option<Rc<Context>>,
+}
+
+#[derive(Clone)]
+pub struct RenderContextInner {
     partials: HashMap<String, Rc<Template>>,
-    path: String,
-    local_path_root: VecDeque<String>,
+    local_helpers: HashMap<String, Rc<Box<HelperDef + 'static>>>,
     local_variables: HashMap<String, Json>,
-    local_helpers: &'a mut HashMap<String, Rc<Box<HelperDef + 'static>>>,
-    default_var: Json,
-    block_context: VecDeque<Context>,
-    /// the context
-    context: Context,
-    /// the `Write` where page is generated
-    pub writer: &'a mut Write,
     /// current template name
-    pub current_template: Option<String>,
+    current_template: Option<String>,
     /// root template name
-    pub root_template: Option<String>,
-    pub disable_escape: bool,
+    root_template: Option<String>,
+    disable_escape: bool,
+}
+
+#[derive(Debug, Clone)]
+pub struct BlockRenderContext {
+    path: String,
+    local_path_root: VecDeque<String>,
+    block_context: VecDeque<Context>,
+}
+
+impl Default for BlockRenderContext {
+    fn default() -> BlockRenderContext {
+        BlockRenderContext {
+            path: ".".to_owned(),
+            local_path_root: VecDeque::new(),
+            block_context: VecDeque::new(),
+        }
+    }
 }
 
-impl<'a> RenderContext<'a> {
+impl RenderContext {
     /// Create a render context from a `Write`
-    pub fn new(
-        ctx: Context,
-        local_helpers: &'a mut HashMap<String, Rc<Box<HelperDef + 'static>>>,
-        w: &'a mut Write,
-    ) -> RenderContext<'a> {
-        RenderContext {
+    pub fn new(root_template: Option<String>) -> RenderContext {
+        let inner = Rc::new(RenderContextInner {
             partials: HashMap::new(),
-            path: ".".to_string(),
-            local_path_root: VecDeque::new(),
             local_variables: HashMap::new(),
-            local_helpers: local_helpers,
-            default_var: Json::Null,
-            block_context: VecDeque::new(),
-            context: ctx,
-            writer: w,
+            local_helpers: HashMap::new(),
             current_template: None,
-            root_template: None,
+            root_template: root_template,
             disable_escape: false,
+        });
+
+        let block = Rc::new(BlockRenderContext::default());
+        let modified_context = None;
+        RenderContext {
+            inner,
+            block,
+            modified_context,
         }
     }
 
-    pub fn derive(&mut self) -> RenderContext {
+    pub fn derive(&self) -> RenderContext {
+        let inner = self.inner.clone();
+        let block = self.block.clone();
+        let modified_context = self.modified_context.clone();
+
         RenderContext {
-            partials: self.partials.clone(),
-            path: self.path.clone(),
-            local_path_root: self.local_path_root.clone(),
-            local_variables: self.local_variables.clone(),
-            current_template: self.current_template.clone(),
-            root_template: self.root_template.clone(),
-            default_var: self.default_var.clone(),
-            block_context: self.block_context.clone(),
-
-            disable_escape: self.disable_escape,
-            local_helpers: self.local_helpers,
-            context: self.context.clone(),
-            writer: self.writer,
+            inner,
+            block,
+            modified_context,
         }
     }
 
-    pub fn with_context(&mut self, ctx: Context) -> RenderContext {
-        RenderContext {
-            partials: self.partials.clone(),
-            path: ".".to_owned(),
-            local_path_root: VecDeque::new(),
-            local_variables: self.local_variables.clone(),
-            current_template: self.current_template.clone(),
-            root_template: self.root_template.clone(),
-            default_var: self.default_var.clone(),
-            block_context: VecDeque::new(),
+    pub fn new_for_block(&self) -> RenderContext {
+        let inner = self.inner.clone();
+        let block = Rc::new(BlockRenderContext::default());
+        let modified_context = self.modified_context.clone();
 
-            disable_escape: self.disable_escape,
-            local_helpers: self.local_helpers,
-            context: ctx,
-            writer: self.writer,
+        RenderContext {
+            inner,
+            block,
+            modified_context,
         }
     }
 
-    pub fn get_partial(&self, name: &str) -> Option<Rc<Template>> {
-        self.partials.get(name).map(|t| t.clone())
+    fn inner(&self) -> &RenderContextInner {
+        self.inner.borrow()
     }
 
-    pub fn set_partial(&mut self, name: String, result: Rc<Template>) {
-        self.partials.insert(name, result);
+    fn inner_mut(&mut self) -> &mut RenderContextInner {
+        Rc::make_mut(&mut self.inner)
     }
 
-    pub fn get_path(&self) -> &String {
-        &self.path
+    fn block(&self) -> &BlockRenderContext {
+        self.block.borrow()
     }
 
-    pub fn set_path(&mut self, path: String) {
-        self.path = path;
+    fn block_mut(&mut self) -> &mut BlockRenderContext {
+        Rc::make_mut(&mut self.block)
     }
 
-    pub fn get_local_path_root(&self) -> &VecDeque<String> {
-        &self.local_path_root
+    pub fn context(&self) -> Option<Rc<Context>> {
+        self.modified_context.clone()
     }
 
-    pub fn push_local_path_root(&mut self, path: String) {
-        self.local_path_root.push_front(path)
+    pub fn set_context(&mut self, ctx: Context) {
+        self.modified_context = Some(Rc::new(ctx))
     }
 
-    pub fn pop_local_path_root(&mut self) {
-        self.local_path_root.pop_front();
+    pub fn evaluate<'ctx>(
+        &self,
+        context: &'ctx Context,
+        path: &str,
+        strict: bool,
+    ) -> Result<&'ctx Json, RenderError> {
+        let value_container = context.navigate(self.get_path(), self.get_local_path_root(), path);
+        if strict {
+            value_container
+                .and_then(|v| v.ok_or(RenderError::new("Value not found in strict mode.")))
+        } else {
+            value_container.map(|v| v.unwrap_or(&DEFAULT_VALUE))
+        }
+    }
+
+    pub fn evaluate_absolute<'ctx>(
+        &self,
+        context: &'ctx Context,
+        path: &str,
+        strict: bool,
+    ) -> Result<&'ctx Json, RenderError> {
+        let value_container = context.navigate(".", &VecDeque::new(), path);
+        if strict {
+            value_container
+                .and_then(|v| v.ok_or(RenderError::new("Value not found in strict mode.")))
+        } else {
+            value_container.map(|v| v.unwrap_or(&DEFAULT_VALUE))
+        }
+    }
+
+    pub fn get_partial(&self, name: &str) -> Option<Rc<Template>> {
+        self.inner().partials.get(name).map(|t| t.clone())
+    }
+
+    pub fn set_partial(&mut self, name: String, result: Rc<Template>) {
+        self.inner_mut().partials.insert(name, result);
     }
 
     pub fn set_local_var(&mut self, name: String, value: Json) {
-        self.local_variables.insert(name, value);
+        self.inner_mut().local_variables.insert(name, value);
     }
 
     pub fn clear_local_vars(&mut self) {
-        self.local_variables.clear();
+        self.inner_mut().local_variables.clear();
     }
 
     pub fn promote_local_vars(&mut self) {
         let mut new_map: HashMap<String, Json> = HashMap::new();
-        for key in self.local_variables.keys() {
+        for key in self.inner().local_variables.keys() {
             let mut new_key = String::new();
             new_key.push_str("@../");
             new_key.push_str(&key[1..]);
 
-            let v = self.local_variables.get(key).unwrap().clone();
+            let v = self.inner().local_variables.get(key).unwrap().clone();
             new_map.insert(new_key, v);
         }
-        self.local_variables = new_map;
+        self.inner_mut().local_variables = new_map;
     }
 
     pub fn demote_local_vars(&mut self) {
         let mut new_map: HashMap<String, Json> = HashMap::new();
-        for key in self.local_variables.keys() {
+        for key in self.inner().local_variables.keys() {
             if key.starts_with("@../") {
                 let mut new_key = String::new();
                 new_key.push('@');
                 new_key.push_str(&key[4..]);
 
-                let v = self.local_variables.get(key).unwrap().clone();
+                let v = self.inner().local_variables.get(key).unwrap().clone();
                 new_map.insert(new_key, v);
             }
         }
-        self.local_variables = new_map;
+        self.inner_mut().local_variables = new_map;
     }
 
     pub fn get_local_var(&self, name: &String) -> Option<&Json> {
-        self.local_variables.get(name)
+        self.inner().local_variables.get(name)
     }
 
-    pub fn writer(&mut self) -> &mut Write {
-        self.writer
+    pub fn is_current_template(&self, p: &str) -> bool {
+        self.inner()
+            .current_template
+            .as_ref()
+            .map(|s| s == p)
+            .unwrap_or(false)
     }
 
-    pub fn push_block_context<T>(&mut self, ctx: &T) -> Result<(), RenderError>
-    where
-        T: Serialize,
-    {
-        let r = self.block_context.push_front(Context::wraps(ctx)?);
-        Ok(r)
+    pub fn register_local_helper(
+        &mut self,
+        name: &str,
+        def: Box<HelperDef + 'static>,
+    ) -> Option<Rc<Box<HelperDef + 'static>>> {
+        self.inner_mut()
+            .local_helpers
+            .insert(name.to_string(), Rc::new(def))
     }
 
-    pub fn pop_block_context(&mut self) {
-        self.block_context.pop_front();
+    pub fn unregister_local_helper(&mut self, name: &str) {
+        self.inner_mut().local_helpers.remove(name);
     }
 
-    pub fn evaluate_in_block_context(
-        &self,
-        local_path: &str,
-    ) -> Result<Option<&Json>, RenderError> {
-        for bc in self.block_context.iter() {
-            let v = bc.navigate(".", &self.local_path_root, local_path)?;
-            if v.is_some() {
-                return Ok(v);
-            }
-        }
+    pub fn get_local_helper(&self, name: &str) -> Option<Rc<Box<HelperDef + 'static>>> {
+        self.inner().local_helpers.get(name).map(|r| r.clone())
+    }
 
-        Ok(None)
+    pub fn get_current_template_name(&self) -> Option<&String> {
+        self.inner().current_template.as_ref()
     }
 
-    pub fn is_current_template(&self, p: &str) -> bool {
-        self.current_template
-            .as_ref()
-            .map(|s| s == p)
-            .unwrap_or(false)
+    pub fn set_current_template_name(&mut self, name: Option<String>) {
+        self.inner_mut().current_template = name;
     }
 
-    pub fn context(&self) -> &Context {
-        &self.context
+    pub fn get_root_template_name(&self) -> Option<&String> {
+        self.inner().root_template.as_ref()
     }
 
-    pub fn context_mut(&mut self) -> &mut Context {
-        &mut self.context
+    pub fn set_root_template_name(&mut self, name: Option<String>) {
+        self.inner_mut().root_template = name;
     }
 
-    pub fn register_local_helper(
-        &mut self,
-        name: &str,
-        def: Box<HelperDef + 'static>,
-    ) -> Option<Rc<Box<HelperDef + 'static>>> {
-        self.local_helpers.insert(name.to_string(), Rc::new(def))
+    pub fn is_disable_escape(&self) -> bool {
+        self.inner().disable_escape
     }
 
-    pub fn unregister_local_helper(&mut self, name: &str) {
-        self.local_helpers.remove(name);
+    pub fn set_disable_escape(&mut self, disable: bool) {
+        self.inner_mut().disable_escape = disable
     }
 
-    pub fn get_local_helper(&self, name: &str) -> Option<Rc<Box<HelperDef + 'static>>> {
-        self.local_helpers.get(name).map(|r| r.clone())
+    pub fn get_path(&self) -> &String {
+        &self.block().path
     }
 
-    pub fn evaluate(&self, path: &str, strict: bool) -> Result<&Json, RenderError> {
-        let value_container =
-            self.context
-                .navigate(self.get_path(), self.get_local_path_root(), path);
-        if strict {
-            value_container
-                .and_then(|v| v.ok_or(RenderError::new("Value not found in strict mode.")))
-        } else {
-            value_container.map(|v| v.unwrap_or(&DEFAULT_VALUE))
-        }
+    pub fn set_path(&mut self, path: String) {
+        self.block_mut().path = path;
     }
 
-    pub fn evaluate_absolute(&self, path: &str, strict: bool) -> Result<&Json, RenderError> {
-        let value_container = self.context.navigate(".", &VecDeque::new(), path);
-        if strict {
-            value_container
-                .and_then(|v| v.ok_or(RenderError::new("Value not found in strict mode.")))
-        } else {
-            value_container.map(|v| v.unwrap_or(&DEFAULT_VALUE))
+    pub fn get_local_path_root(&self) -> &VecDeque<String> {
+        &self.block().local_path_root
+    }
+
+    pub fn push_local_path_root(&mut self, path: String) {
+        self.block_mut().local_path_root.push_front(path)
+    }
+
+    pub fn pop_local_path_root(&mut self) {
+        self.block_mut().local_path_root.pop_front();
+    }
+
+    pub fn push_block_context<T>(&mut self, ctx: &T) -> Result<(), RenderError>
+    where
+        T: Serialize,
+    {
+        let r = self
+            .block_mut()
+            .block_context
+            .push_front(Context::wraps(ctx)?);
+        Ok(r)
+    }
+
+    pub fn pop_block_context(&mut self) {
+        self.block_mut().block_context.pop_front();
+    }
+
+    pub fn evaluate_in_block_context(
+        &self,
+        local_path: &str,
+    ) -> Result<Option<&Json>, RenderError> {
+        let block = self.block();
+        for bc in block.block_context.iter() {
+            let v = bc.navigate(".", &block.local_path_root, local_path)?;
+            if v.is_some() {
+                return Ok(v);
+            }
         }
+
+        Ok(None)
     }
 }
 
-impl<'a> fmt::Debug for RenderContext<'a> {
+impl RenderContextInner {}
+
+impl BlockRenderContext {}
+
+impl fmt::Debug for RenderContextInner {
     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        f.debug_struct("RenderContext")
-            .field("context", &self.context)
-            .field("path", &self.path)
+        f.debug_struct("RenderContextInner")
             .field("partials", &self.partials)
             .field("local_variables", &self.local_variables)
             .field("root_template", &self.root_template)
             .field("current_template", &self.current_template)
-            .field("block_context", &self.block_context)
-            .field("local_path_root", &self.local_path_root)
             .field("disable_eacape", &self.disable_escape)
             .finish()
     }
 }
 
-/// Json wrapper that holds the Json value and reference path information
-///
-#[derive(Debug)]
-pub struct ContextJson {
-    path: Option<String>,
-    value: Json,
-}
-
-impl ContextJson {
-    /// Returns relative path when the value is referenced
-    /// If the value is from a literal, the path is `None`
-    pub fn path(&self) -> Option<&String> {
-        self.path.as_ref()
-    }
-
-    /// Return root level of this path if any
-    pub fn path_root(&self) -> Option<&str> {
-        self.path
-            .as_ref()
-            .and_then(|p| p.split(|c| c == '.' || c == '/').nth(0))
-    }
-
-    /// Returns the value
-    pub fn value(&self) -> &Json {
-        &self.value
-    }
-}
-
-/// Render-time Helper data when using in a helper definition
+// Render-time Helper data when using in a helper definition
 #[derive(Debug)]
-pub struct Helper<'a> {
-    name: &'a str,
-    params: Vec<ContextJson>,
-    hash: BTreeMap<String, ContextJson>,
-    block_param: &'a Option<BlockParam>,
-    template: Option<&'a Template>,
-    inverse: Option<&'a Template>,
+pub struct Helper<'reg: 'rc, 'rc> {
+    name: &'reg String,
+    params: Vec<PathAndJson<'reg, 'rc>>,
+    hash: BTreeMap<String, PathAndJson<'reg, 'rc>>,
+    template: Option<&'reg Template>,
+    inverse: Option<&'reg Template>,
+    block_param: Option<&'reg BlockParam>,
     block: bool,
 }
 
-impl<'a, 'b> Helper<'a> {
-    fn from_template(
-        ht: &'a HelperTemplate,
-        registry: &Registry,
-        rc: &'b mut RenderContext,
-    ) -> Result<Helper<'a>, RenderError> {
-        let mut evaluated_params = Vec::new();
+impl<'reg: 'rc, 'rc> Helper<'reg, 'rc> {
+    fn try_from_template(
+        ht: &'reg HelperTemplate,
+        registry: &'reg Registry,
+        context: &'rc Context,
+        render_context: &mut RenderContext,
+    ) -> Result<Helper<'reg, 'rc>, RenderError> {
+        let mut pv = Vec::new();
         for p in ht.params.iter() {
-            let r = try!(p.expand(registry, rc));
-            evaluated_params.push(r);
+            let r = p.expand(registry, context, render_context)?;
+            pv.push(r);
         }
 
-        let mut evaluated_hash = BTreeMap::new();
+        let mut hm = BTreeMap::new();
         for (k, p) in ht.hash.iter() {
-            let r = try!(p.expand(registry, rc));
-            evaluated_hash.insert(k.clone(), r);
+            let r = p.expand(registry, context, render_context)?;
+            hm.insert(k.clone(), r);
         }
 
         Ok(Helper {
             name: &ht.name,
-            params: evaluated_params,
-            hash: evaluated_hash,
-            block_param: &ht.block_param,
+            params: pv,
+            hash: hm,
             template: ht.template.as_ref(),
             inverse: ht.inverse.as_ref(),
+            block_param: ht.block_param.as_ref(),
             block: ht.block,
         })
     }
 
     /// Returns helper name
-    pub fn name(&self) -> &str {
-        &self.name
+    pub fn name(&self) -> &'reg str {
+        self.name
     }
 
     /// Returns all helper params, resolved within the context
-    pub fn params(&self) -> &Vec<ContextJson> {
+    pub fn params(&self) -> &Vec<PathAndJson<'reg, 'rc>> {
         &self.params
     }
 
@@ -363,17 +391,18 @@ impl<'a, 'b> Helper<'a> {
     /// use handlebars::*;
     ///
     /// fn my_helper(h: &Helper, rc: &mut RenderContext) -> Result<(), RenderError> {
-    ///     let v = h.param(0).map(|v| v.value()).unwrap();
+    ///     let v = h.param(0).map(|v| v.value())
+    ///         .ok_or(RenderError::new("param not found"));
     ///     // ..
     ///     Ok(())
     /// }
     /// ```
-    pub fn param(&self, idx: usize) -> Option<&ContextJson> {
+    pub fn param(&self, idx: usize) -> Option<&PathAndJson<'reg, 'rc>> {
         self.params.get(idx)
     }
 
     /// Returns hash, resolved within the context
-    pub fn hash(&self) -> &BTreeMap<String, ContextJson> {
+    pub fn hash(&self) -> &BTreeMap<String, PathAndJson<'reg, 'rc>> {
         &self.hash
     }
 
@@ -389,12 +418,13 @@ impl<'a, 'b> Helper<'a> {
     /// use handlebars::*;
     ///
     /// fn my_helper(h: &Helper, rc: &mut RenderContext) -> Result<(), RenderError> {
-    ///     let v = h.hash_get("v").map(|v| v.value()).unwrap();
+    ///     let v = h.hash_get("v").map(|v| v.value())
+    ///         .ok_or(RenderError::new("param not found"));
     ///     // ..
     ///     Ok(())
     /// }
     /// ```
-    pub fn hash_get(&self, key: &str) -> Option<&ContextJson> {
+    pub fn hash_get(&self, key: &str) -> Option<&PathAndJson<'reg, 'rc>> {
         self.hash.get(key)
     }
 
@@ -402,12 +432,12 @@ impl<'a, 'b> Helper<'a> {
     ///
     /// Typically you will render the template via: `template.render(registry, render_context)`
     ///
-    pub fn template(&self) -> Option<&Template> {
+    pub fn template(&self) -> Option<&'reg Template> {
         self.template
     }
 
     /// Returns the template of `else` branch if any
-    pub fn inverse(&self) -> Option<&Template> {
+    pub fn inverse(&self) -> Option<&'reg Template> {
         self.inverse
     }
 
@@ -418,7 +448,7 @@ impl<'a, 'b> Helper<'a> {
 
     /// Returns block param if any
     pub fn block_param(&self) -> Option<&str> {
-        if let Some(BlockParam::Single(Parameter::Name(ref s))) = *self.block_param {
+        if let Some(BlockParam::Single(Parameter::Name(ref s))) = self.block_param {
             Some(s)
         } else {
             None
@@ -428,7 +458,7 @@ impl<'a, 'b> Helper<'a> {
     /// 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
+            self.block_param
         {
             Some((s1, s2))
         } else {
@@ -437,70 +467,71 @@ impl<'a, 'b> Helper<'a> {
     }
 }
 
-/// Render-time Decorator data when using in a decorator definition
+// Render-time Directive data when using in a directive definition
 #[derive(Debug)]
-pub struct Directive<'a> {
+pub struct Directive<'reg: 'rc, 'rc> {
     name: String,
-    params: Vec<ContextJson>,
-    hash: BTreeMap<String, ContextJson>,
-    template: Option<&'a Template>,
+    params: Vec<PathAndJson<'reg, 'rc>>,
+    hash: BTreeMap<String, PathAndJson<'reg, 'rc>>,
+    template: Option<&'reg Template>,
 }
 
-impl<'a, 'b> Directive<'a> {
-    fn from_template(
-        dt: &'a DirectiveTemplate,
-        registry: &Registry,
-        rc: &'b mut RenderContext,
-    ) -> Result<Directive<'a>, RenderError> {
-        let name = try!(dt.name.expand_as_name(registry, rc));
+impl<'reg: 'rc, 'rc> Directive<'reg, 'rc> {
+    fn try_from_template(
+        dt: &'reg DirectiveTemplate,
+        registry: &'reg Registry,
+        context: &'rc Context,
+        render_context: &mut RenderContext,
+    ) -> Result<Directive<'reg, 'rc>, RenderError> {
+        let name = try!(dt.name.expand_as_name(registry, context, render_context));
 
-        let mut evaluated_params = Vec::new();
+        let mut pv = Vec::new();
         for p in dt.params.iter() {
-            let r = try!(p.expand(registry, rc));
-            evaluated_params.push(r);
+            let r = p.expand(registry, context, render_context)?;
+            pv.push(r);
         }
 
-        let mut evaluated_hash = BTreeMap::new();
+        let mut hm = BTreeMap::new();
         for (k, p) in dt.hash.iter() {
-            let r = try!(p.expand(registry, rc));
-            evaluated_hash.insert(k.clone(), r);
+            let r = p.expand(registry, context, render_context)?;
+            hm.insert(k.clone(), r);
         }
 
         Ok(Directive {
             name: name,
-            params: evaluated_params,
-            hash: evaluated_hash,
+            params: pv,
+            hash: hm,
             template: dt.template.as_ref(),
         })
     }
 
     /// Returns helper name
-    pub fn name(&self) -> &str {
+    pub fn name(&self) -> &String {
         &self.name
     }
 
     /// Returns all helper params, resolved within the context
-    pub fn params(&self) -> &Vec<ContextJson> {
+    pub fn params(&self) -> &Vec<PathAndJson<'reg, 'rc>> {
         &self.params
     }
 
     /// Returns nth helper param, resolved within the context
-    pub fn param(&self, idx: usize) -> Option<&ContextJson> {
+    pub fn param(&self, idx: usize) -> Option<&PathAndJson<'reg, 'rc>> {
         self.params.get(idx)
     }
 
     /// Returns hash, resolved within the context
-    pub fn hash(&self) -> &BTreeMap<String, ContextJson> {
+    pub fn hash(&self) -> &BTreeMap<String, PathAndJson<'reg, 'rc>> {
         &self.hash
     }
 
     /// Return hash value of a given key, resolved within the context
-    pub fn hash_get(&self, key: &str) -> Option<&ContextJson> {
+    pub fn hash_get(&self, key: &str) -> Option<&PathAndJson<'reg, 'rc>> {
         self.hash.get(key)
     }
 
     /// Returns the default inner template if any
-    pub fn template(&self) -> Option<&Template> {
+    pub fn template(&self) -> Option<&'reg Template> {
         self.template
     }
 }
@@ -508,106 +539,140 @@ impl<'a, 'b> Directive<'a> {
 /// Render trait
 pub trait Renderable {
     /// render into RenderContext's `writer`
-    fn render(&self, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>;
+    fn render<'reg: 'rc, 'rc>(
+        &'reg self,
+        registry: &'reg Registry,
+        context: &'rc Context,
+        rc: &'rc mut RenderContext,
+        out: &mut Output,
+    ) -> Result<(), RenderError>;
 
     /// render into string
-    fn renders(&self, registry: &Registry, rc: &mut RenderContext) -> Result<String, RenderError> {
-        let mut sw = StringWriter::new();
-        {
-            let mut local_rc = rc.derive();
-            local_rc.writer = &mut sw;
-            try!(self.render(registry, &mut local_rc));
-        }
-
-        let s = sw.to_string();
-        Ok(s)
+    fn renders<'reg: 'rc, 'rc>(
+        &'reg self,
+        registry: &'reg Registry,
+        ctx: &'rc Context,
+        rc: &'rc mut RenderContext,
+    ) -> Result<String, RenderError> {
+        let mut so = StringOutput::new();
+        self.render(registry, ctx, rc, &mut so)?;
+        so.to_string().map_err(RenderError::from)
     }
 }
 
 /// Evaluate directive or decorator
 pub trait Evaluable {
-    fn eval(&self, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError>;
+    fn eval<'reg: 'rc, 'rc>(
+        &'reg self,
+        registry: &'reg Registry,
+        context: &'rc Context,
+        rc: &'rc mut RenderContext,
+    ) -> Result<(), RenderError>;
 }
 
-fn call_helper_for_value(
+fn call_helper_for_value<'reg: 'rc, 'rc>(
     hd: &Box<HelperDef>,
-    ht: &Helper,
-    registry: &Registry,
+    ht: &Helper<'reg, 'rc>,
+    r: &'reg Registry,
+    ctx: &'rc Context,
     rc: &mut RenderContext,
-) -> Result<ContextJson, RenderError> {
-    // test if helperDef has json result
-    if let Some(inner_value) = hd.call_inner(ht, registry, rc)? {
-        Ok(ContextJson {
-            path: None,
-            value: inner_value,
-        })
+) -> Result<PathAndJson<'reg, 'rc>, RenderError> {
+    if let Some(result) = hd.call_inner(ht, r, ctx, rc)? {
+        Ok(PathAndJson::new(None, result))
     } else {
         // parse value from output
-        let mut local_writer = StringWriter::new();
-        {
-            let mut local_rc = rc.derive();
-            local_rc.writer = &mut local_writer;
-            // disable html escape for subexpression
-            local_rc.disable_escape = true;
+        let mut so = StringOutput::new();
 
-            hd.call(ht, registry, &mut local_rc)?;
-        }
-        Ok(ContextJson {
-            path: None,
-            value: Json::String(local_writer.to_string()),
-        })
+        // here we don't want subexpression result escaped,
+        // so we temporarily disable it
+        let disable_escape = rc.is_disable_escape();
+        rc.set_disable_escape(true);
+
+        hd.call(ht, r, ctx, rc, &mut so)?;
+        rc.set_disable_escape(disable_escape);
+
+        let string = so.to_string().map_err(RenderError::from)?;
+        Ok(PathAndJson::new(
+            None,
+            ScopedJson::Derived(Json::String(string)),
+        ))
     }
 }
 
 impl Parameter {
-    pub fn expand_as_name(
-        &self,
-        registry: &Registry,
+    pub fn expand_as_name<'reg: 'rc, 'rc>(
+        &'reg self,
+        registry: &'reg Registry,
+        ctx: &'rc Context,
         rc: &mut RenderContext,
     ) -> Result<String, RenderError> {
         match self {
             &Parameter::Name(ref name) => Ok(name.to_owned()),
-            &Parameter::Subexpression(_) => self.expand(registry, rc).map(|v| v.value.render()),
+            &Parameter::Subexpression(_) => {
+                self.expand(registry, ctx, rc).map(|v| v.value().render())
+            }
             &Parameter::Literal(ref j) => Ok(j.render()),
         }
     }
 
-    pub fn expand(
-        &self,
-        registry: &Registry,
+    pub fn expand<'reg: 'rc, 'rc>(
+        &'reg self,
+        registry: &'reg Registry,
+        ctx: &'rc Context,
         rc: &mut RenderContext,
-    ) -> Result<ContextJson, RenderError> {
+    ) -> Result<PathAndJson<'reg, 'rc>, RenderError> {
         match self {
             &Parameter::Name(ref name) => {
+                let strict = registry.strict_mode();
                 let local_value = rc.get_local_var(&name);
                 if let Some(value) = local_value {
-                    Ok(ContextJson {
-                        path: Some(name.to_owned()),
-                        value: value.clone(),
-                    })
+                    // local var, @first, @last for example
+                    // here we count it as derived value, and simply clone it
+                    // to bypass lifetime issue
+                    Ok(PathAndJson::new(
+                        Some(name.to_owned()),
+                        ScopedJson::Derived(value.clone()),
+                    ))
                 } else {
-                    let block_context_value = rc.evaluate_in_block_context(name)?;
-                    let value = if block_context_value.is_none() {
-                        rc.evaluate(name, registry.strict_mode())?
+                    // try to evaluate using block context if any
+                    if let Some(block_context_value) = rc.evaluate_in_block_context(name)? {
+                        // we do clone for block context because it's from
+                        // render_context
+                        Ok(PathAndJson::new(
+                            Some(name.to_owned()),
+                            ScopedJson::Derived(block_context_value.clone()),
+                        ))
                     } else {
-                        block_context_value.unwrap()
-                    };
-                    Ok(ContextJson {
-                        path: Some(name.to_owned()),
-                        value: value.clone(),
-                    })
+                        if let Some(rc_context) = rc.context() {
+                            // the context is modified from a decorator
+                            // use the modified one
+                            let json = rc.evaluate(rc_context.borrow(), name, strict)?;
+                            // the data is fetched from mutable reference render_context
+                            // so we have to clone it to bypass lifetime check
+                            Ok(PathAndJson::new(
+                                Some(name.to_owned()),
+                                ScopedJson::Derived(json.clone()),
+                            ))
+                        } else {
+                            // failback to normal evaluation
+                            let json_ref = rc.evaluate(ctx, name, strict)?;
+                            // value borrowed from context data
+                            Ok(PathAndJson::new(
+                                Some(name.to_owned()),
+                                ScopedJson::Context(json_ref),
+                            ))
+                        }
+                    }
                 }
             }
-            &Parameter::Literal(ref j) => Ok(ContextJson {
-                path: None,
-                value: j.clone(),
-            }),
-            &Parameter::Subexpression(ref t) => match t.into_element() {
-                Expression(ref expr) => expr.expand(registry, rc),
+            &Parameter::Literal(ref j) => Ok(PathAndJson::new(None, ScopedJson::Constant(j))),
+            &Parameter::Subexpression(ref t) => match t.as_element() {
+                Expression(ref expr) => expr.expand(registry, ctx, rc),
                 HelperExpression(ref ht) => {
-                    let helper = Helper::from_template(ht, registry, rc)?;
+                    let h = Helper::try_from_template(ht, registry, ctx, rc)?;
                     if let Some(ref d) = rc.get_local_helper(&ht.name) {
-                        call_helper_for_value(d.borrow(), &helper, registry, rc)
+                        let helper_def = d.borrow();
+                        call_helper_for_value(helper_def, &h, registry, ctx, rc)
                     } else {
                         registry
                             .get_helper(&ht.name)
@@ -620,7 +685,7 @@ impl Parameter {
                                 "Helper not defined: {:?}",
                                 ht.name
                             )))
-                            .and_then(|d| call_helper_for_value(d, &helper, registry, rc))
+                            .and_then(move |d| call_helper_for_value(d, &h, registry, ctx, rc))
                     }
                 }
                 _ => unreachable!(),
@@ -630,12 +695,19 @@ impl Parameter {
 }
 
 impl Renderable for Template {
-    fn render(&self, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
-        rc.current_template = self.name.clone();
+    fn render<'reg: 'rc, 'rc>(
+        &'reg self,
+        registry: &'reg Registry,
+        ctx: &'rc Context,
+        rc: &mut RenderContext,
+        out: &mut Output,
+    ) -> Result<(), RenderError> {
+        let template_name = self.name.clone();
+        rc.set_current_template_name(template_name);
         let iter = self.elements.iter();
         let mut idx = 0;
         for t in iter {
-            try!(t.render(registry, rc).map_err(|mut e| {
+            t.render(registry, ctx, rc, out).map_err(|mut e| {
                 // add line/col number if the template has mapping data
                 if e.line_no.is_none() {
                     if let Some(ref mapping) = self.mapping {
@@ -651,7 +723,7 @@ impl Renderable for Template {
                 }
 
                 e
-            }));
+            })?;
             idx = idx + 1;
         }
         Ok(())
@@ -659,11 +731,16 @@ impl Renderable for Template {
 }
 
 impl Evaluable for Template {
-    fn eval(&self, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
+    fn eval<'reg: 'rc, 'rc>(
+        &'reg self,
+        registry: &'reg Registry,
+        ctx: &'rc Context,
+        rc: &mut RenderContext,
+    ) -> Result<(), RenderError> {
         let iter = self.elements.iter();
         let mut idx = 0;
         for t in iter {
-            try!(t.eval(registry, rc).map_err(|mut e| {
+            t.eval(registry, ctx, rc).map_err(|mut e| {
                 if e.line_no.is_none() {
                     if let Some(ref mapping) = self.mapping {
                         if let Some(&TemplateMapping(line, col)) = mapping.get(idx) {
@@ -675,7 +752,7 @@ impl Evaluable for Template {
 
                 e.template_name = self.name.clone();
                 e
-            }));
+            })?;
             idx = idx + 1;
         }
         Ok(())
@@ -683,34 +760,40 @@ impl Evaluable for Template {
 }
 
 impl Renderable for TemplateElement {
-    fn render(&self, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
+    fn render<'reg: 'rc, 'rc>(
+        &'reg self,
+        registry: &'reg Registry,
+        ctx: &'rc Context,
+        rc: &mut RenderContext,
+        out: &mut Output,
+    ) -> Result<(), RenderError> {
         match *self {
             RawString(ref v) => {
-                try!(rc.writer.write(v.as_bytes()));
+                out.write(v.as_ref())?;
                 Ok(())
             }
             Expression(ref v) => {
-                let context_json = try!(v.expand(registry, rc));
-                let rendered = context_json.value.render();
+                let context_json = v.expand(registry, ctx, rc)?;
+                let rendered = context_json.value().render();
 
-                let output = if !rc.disable_escape {
+                let output = if !rc.is_disable_escape() {
                     registry.get_escape_fn()(&rendered)
                 } else {
                     rendered
                 };
-                try!(rc.writer.write(output.as_ref()));
+                out.write(output.as_ref())?;
                 Ok(())
             }
             HTMLExpression(ref v) => {
-                let context_json = try!(v.expand(registry, rc));
-                let rendered = context_json.value.render();
-                try!(rc.writer.write(rendered.as_ref()));
+                let context_json = v.expand(registry, ctx, rc)?;
+                let rendered = context_json.value().render();
+                out.write(rendered.as_ref())?;
                 Ok(())
             }
             HelperExpression(ref ht) | HelperBlock(ref ht) => {
-                let helper = try!(Helper::from_template(ht, registry, rc));
+                let h = Helper::try_from_template(ht, registry, ctx, rc)?;
                 if let Some(ref d) = rc.get_local_helper(&ht.name) {
-                    d.call(&helper, registry, rc)
+                    d.call(&h, registry, ctx, rc, out)
                 } else {
                     registry
                         .get_helper(&ht.name)
@@ -723,13 +806,13 @@ impl Renderable for TemplateElement {
                             "Helper not defined: {:?}",
                             ht.name
                         )))
-                        .and_then(|d| d.call(&helper, registry, rc))
+                        .and_then(move |d| d.call(&h, registry, ctx, rc, out))
                 }
             }
-            DirectiveExpression(_) | DirectiveBlock(_) => self.eval(registry, rc),
+            DirectiveExpression(_) | DirectiveBlock(_) => self.eval(registry, ctx, rc),
             PartialExpression(ref dt) | PartialBlock(ref dt) => {
-                Directive::from_template(dt, registry, rc)
-                    .and_then(|di| partial::expand_partial(&di, registry, rc))
+                let di = Directive::try_from_template(dt, registry, ctx, rc)?;
+                partial::expand_partial(&di, registry, ctx, rc, out)
             }
             _ => Ok(()),
         }
@@ -737,18 +820,22 @@ impl Renderable for TemplateElement {
 }
 
 impl Evaluable for TemplateElement {
-    fn eval(&self, registry: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
+    fn eval<'reg: 'rc, 'rc>(
+        &'reg self,
+        registry: &'reg Registry,
+        ctx: &'rc Context,
+        rc: &mut RenderContext,
+    ) -> Result<(), RenderError> {
         match *self {
             DirectiveExpression(ref dt) | DirectiveBlock(ref dt) => {
-                Directive::from_template(dt, registry, rc).and_then(|di| {
-                    match registry.get_decorator(&di.name) {
-                        Some(d) => (**d).call(&di, registry, rc),
-                        None => Err(RenderError::new(format!(
-                            "Directive not defined: {:?}",
-                            dt.name
-                        ))),
-                    }
-                })
+                let di = Directive::try_from_template(dt, registry, ctx, rc)?;
+                match registry.get_decorator(di.name()) {
+                    Some(d) => (**d).call(&di, registry, ctx, rc),
+                    None => Err(RenderError::new(format!(
+                        "Directive not defined: {:?}",
+                        dt.name
+                    ))),
+                }
             }
             _ => Ok(()),
         }
@@ -758,67 +845,63 @@ impl Evaluable for TemplateElement {
 #[test]
 fn test_raw_string() {
     let r = Registry::new();
-    let mut sw = StringWriter::new();
+    let mut out = StringOutput::new();
     let ctx = Context::null();
-    let mut hlps = HashMap::new();
     {
-        let mut rc = RenderContext::new(ctx, &mut hlps, &mut sw);
+        let mut rc = RenderContext::new(None);
         let raw_string = RawString("<h1>hello world</h1>".to_string());
 
-        raw_string.render(&r, &mut rc).ok().unwrap();
+        raw_string.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
     }
-    assert_eq!(sw.to_string(), "<h1>hello world</h1>".to_string());
+    assert_eq!(out.to_string().unwrap(), "<h1>hello world</h1>".to_string());
 }
 
 #[test]
 fn test_expression() {
     let r = Registry::new();
-    let mut sw = StringWriter::new();
-    let mut hlps = HashMap::new();
+    let mut out = StringOutput::new();
     let mut m: HashMap<String, String> = HashMap::new();
     let value = "<p></p>".to_string();
     m.insert("hello".to_string(), value);
     let ctx = Context::wraps(&m).unwrap();
     {
-        let mut rc = RenderContext::new(ctx, &mut hlps, &mut sw);
+        let mut rc = RenderContext::new(None);
         let element = Expression(Parameter::Name("hello".into()));
 
-        element.render(&r, &mut rc).ok().unwrap();
+        element.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
     }
 
-    assert_eq!(sw.to_string(), "&lt;p&gt;&lt;/p&gt;".to_string());
+    assert_eq!(out.to_string().unwrap(), "&lt;p&gt;&lt;/p&gt;".to_string());
 }
 
 #[test]
 fn test_html_expression() {
     let r = Registry::new();
-    let mut sw = StringWriter::new();
-    let mut hlps = HashMap::new();
+    let mut out = StringOutput::new();
     let mut m: HashMap<String, String> = HashMap::new();
     let value = "world";
     m.insert("hello".to_string(), value.to_string());
     let ctx = Context::wraps(&m).unwrap();
     {
-        let mut rc = RenderContext::new(ctx, &mut hlps, &mut sw);
+        let mut rc = RenderContext::new(None);
         let element = HTMLExpression(Parameter::Name("hello".into()));
-        element.render(&r, &mut rc).ok().unwrap();
+        element.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
     }
 
-    assert_eq!(sw.to_string(), value.to_string());
+    assert_eq!(out.to_string().unwrap(), value.to_string());
 }
 
 #[test]
 fn test_template() {
     let r = Registry::new();
-    let mut sw = StringWriter::new();
-    let mut hlps = HashMap::new();
+    let mut out = StringOutput::new();
     let mut m: HashMap<String, String> = HashMap::new();
     let value = "world".to_string();
     m.insert("hello".to_string(), value);
     let ctx = Context::wraps(&m).unwrap();
 
     {
-        let mut rc = RenderContext::new(ctx, &mut hlps, &mut sw);
+        let mut rc = RenderContext::new(None);
         let mut elements: Vec<TemplateElement> = Vec::new();
 
         let e1 = RawString("<h1>".to_string());
@@ -838,20 +921,16 @@ fn test_template() {
             name: None,
             mapping: None,
         };
-        template.render(&r, &mut rc).ok().unwrap();
+        template.render(&r, &ctx, &mut rc, &mut out).ok().unwrap();
     }
 
-    assert_eq!(sw.to_string(), "<h1>world</h1>".to_string());
+    assert_eq!(out.to_string().unwrap(), "<h1>world</h1>".to_string());
 }
 
 #[test]
 fn test_render_context_promotion_and_demotion() {
-    use context::to_json;
-    let mut sw = StringWriter::new();
-    let ctx = Context::null();
-    let mut hlps = HashMap::new();
-
-    let mut render_context = RenderContext::new(ctx, &mut hlps, &mut sw);
+    use value::to_json;
+    let mut render_context = RenderContext::new(None);
 
     render_context.set_local_var("@index".to_string(), to_json(&0));
 
@@ -874,6 +953,8 @@ fn test_render_context_promotion_and_demotion() {
 
 #[test]
 fn test_render_subexpression() {
+    use support::str::StringWriter;
+
     let r = Registry::new();
     let mut sw = StringWriter::new();
 
@@ -895,17 +976,19 @@ fn test_render_subexpression() {
 
 #[test]
 fn test_render_subexpression_issue_115() {
+    use support::str::StringWriter;
+
     let mut r = Registry::new();
     r.register_helper(
         "format",
         Box::new(
-            |h: &Helper, _: &Registry, rc: &mut RenderContext| -> Result<(), RenderError> {
-                rc.writer
-                    .write(
-                        format!("{}", h.param(0).unwrap().value().render())
-                            .into_bytes()
-                            .as_ref(),
-                    )
+            |h: &Helper,
+             _: &Registry,
+             _: &Context,
+             _: &mut RenderContext,
+             out: &mut Output|
+             -> Result<(), RenderError> {
+                out.write(format!("{}", h.param(0).unwrap().value().render()).as_ref())
                     .map(|_| ())
                     .map_err(RenderError::from)
             },
@@ -953,10 +1036,12 @@ fn test_partial_failback_render() {
         r.register_template_string("parent", "<html>{{> layout}}</html>")
             .is_ok()
     );
-    assert!(r.register_template_string(
-        "child",
-        "{{#*inline \"layout\"}}content{{/inline}}{{#> parent}}{{> seg}}{{/parent}}"
-    ).is_ok());
+    assert!(
+        r.register_template_string(
+            "child",
+            "{{#*inline \"layout\"}}content{{/inline}}{{#> parent}}{{> seg}}{{/parent}}"
+        ).is_ok()
+    );
     assert!(r.register_template_string("seg", "1234").is_ok());
 
     let r = r.render("child", &true).expect("should work");
@@ -972,12 +1057,7 @@ fn test_key_with_slash() {
             .is_ok()
     );
 
-    let r = r.render(
-        "t",
-        &json!({
-        "/foo": "bar"
-    }),
-    ).expect("should work");
+    let r = r.render("t", &json!({"/foo": "bar"})).expect("should work");
 
     assert_eq!(r, "/foo: bar\n");
 }
diff --git a/src/support.rs b/src/support.rs
index 6111dd0..a3f6655 100644
--- a/src/support.rs
+++ b/src/support.rs
@@ -37,8 +37,8 @@ pub mod str {
 
     #[cfg(test)]
     mod test {
-        use support::str::StringWriter;
         use std::io::Write;
+        use support::str::StringWriter;
 
         #[test]
         fn test_string_writer() {
@@ -52,3 +52,34 @@ pub mod str {
         }
     }
 }
+
+use std::cell::Ref;
+use std::ops::Deref;
+
+pub enum RefWrapper<'a, T: 'a + ?Sized> {
+    CellRef(Ref<'a, T>),
+    Ref(&'a T),
+}
+
+impl<'a, T: 'a + ?Sized> Deref for RefWrapper<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        match self {
+            RefWrapper::CellRef(cell_ref) => cell_ref.deref(),
+            RefWrapper::Ref(normal_ref) => normal_ref.deref(),
+        }
+    }
+}
+
+impl<'a, T: 'a + ?Sized> From<Ref<'a, T>> for RefWrapper<'a, T> {
+    fn from(cell_ref: Ref<'a, T>) -> RefWrapper<'a, T> {
+        RefWrapper::CellRef(cell_ref)
+    }
+}
+
+impl<'a, T: 'a + ?Sized> From<&'a T> for RefWrapper<'a, T> {
+    fn from(normal_ref: &'a T) -> RefWrapper<'a, T> {
+        RefWrapper::Ref(normal_ref)
+    }
+}
diff --git a/src/template.rs b/src/template.rs
index 7654069..2cc639e 100644
--- a/src/template.rs
+++ b/src/template.rs
@@ -27,21 +27,68 @@ pub struct Template {
 
 #[derive(PartialEq, Clone, Debug)]
 pub struct Subexpression {
-    pub name: String,
-    pub params: Vec<Parameter>,
-    pub hash: BTreeMap<String, Parameter>,
+    // we use box here avoid resursive struct definition
+    pub element: Box<TemplateElement>,
 }
 
 impl Subexpression {
+    pub fn new(
+        name: String,
+        params: Vec<Parameter>,
+        hash: BTreeMap<String, Parameter>,
+    ) -> Subexpression {
+        if params.is_empty() && hash.is_empty() {
+            Subexpression {
+                element: Box::new(Expression(Parameter::Name(name.clone()))),
+            }
+        } else {
+            Subexpression {
+                element: Box::new(HelperExpression(HelperTemplate {
+                    name: name.clone(),
+                    params: params.clone(),
+                    hash: hash.clone(),
+                    template: None,
+                    inverse: None,
+                    block_param: None,
+                    block: false,
+                })),
+            }
+        }
+    }
+
     pub fn is_helper(&self) -> bool {
-        !(self.params.is_empty() && self.hash.is_empty())
+        match self.element.as_ref() {
+            TemplateElement::HelperExpression(_) => true,
+            _ => false,
+        }
     }
 
-    pub fn into_element(&self) -> TemplateElement {
-        if self.is_helper() {
-            HelperExpression(HelperTemplate::from(self))
-        } else {
-            Expression(Parameter::Name(self.name.clone()))
+    pub fn as_element(&self) -> &TemplateElement {
+        self.element.as_ref()
+    }
+
+    pub fn name(&self) -> &str {
+        match self.as_element() {
+            HelperExpression(ref ht) => &ht.name,
+            Expression(p) => match p {
+                Parameter::Name(ref s) => s,
+                _ => unreachable!(),
+            },
+            _ => unreachable!(),
+        }
+    }
+
+    pub fn params(&self) -> Option<&Vec<Parameter>> {
+        match self.as_element() {
+            HelperExpression(ref ht) => Some(&ht.params),
+            _ => None,
+        }
+    }
+
+    pub fn hash(&self) -> Option<&BTreeMap<String, Parameter>> {
+        match self.as_element() {
+            HelperExpression(ref ht) => Some(&ht.hash),
+            _ => None,
         }
     }
 }
@@ -80,22 +127,8 @@ pub struct HelperTemplate {
     pub block: bool,
 }
 
-impl<'a> From<&'a Subexpression> for HelperTemplate {
-    fn from(s: &Subexpression) -> HelperTemplate {
-        HelperTemplate {
-            name: s.name.clone(),
-            params: s.params.clone(),
-            hash: s.hash.clone(),
-            block_param: None,
-            template: None,
-            inverse: None,
-            block: false,
-        }
-    }
-}
-
 #[derive(PartialEq, Clone, Debug)]
-pub struct Directive {
+pub struct DirectiveTemplate {
     pub name: Parameter,
     pub params: Vec<Parameter>,
     pub hash: BTreeMap<String, Parameter>,
@@ -103,7 +136,7 @@ pub struct Directive {
 }
 
 impl Parameter {
-    pub fn as_name(self) -> Option<String> {
+    pub fn into_name(self) -> Option<String> {
         if let Parameter::Name(n) = self {
             Some(n)
         } else {
@@ -149,13 +182,13 @@ impl Template {
     where
         I: Iterator<Item = Pair<'a, Rule>>,
     {
-        let espec = try!(Template::parse_expression(source, it.by_ref(), limit));
+        let espec = Template::parse_expression(source, it.by_ref(), limit)?;
         if let Parameter::Name(name) = espec.name {
-            Ok(Parameter::Subexpression(Subexpression {
-                name: name,
-                params: espec.params,
-                hash: espec.hash,
-            }))
+            Ok(Parameter::Subexpression(Subexpression::new(
+                name,
+                espec.params,
+                espec.hash,
+            )))
         } else {
             // line/col no
             Err(TemplateError::of(TemplateErrorReason::NestedSubexpression))
@@ -210,11 +243,9 @@ impl Template {
                     Parameter::Name(s.to_owned())
                 }
             }
-            Rule::subexpression => try!(Template::parse_subexpression(
-                source,
-                it.by_ref(),
-                param_span.end(),
-            )),
+            Rule::subexpression => {
+                Template::parse_subexpression(source, it.by_ref(), param_span.end())?
+            }
             _ => unreachable!(),
         };
 
@@ -248,7 +279,7 @@ impl Template {
         // identifier
         let key = name_node.as_str().to_owned();
 
-        let value = try!(Template::parse_param(source, it.by_ref(), limit));
+        let value = Template::parse_param(source, it.by_ref(), limit)?;
         Ok((key, value))
     }
 
@@ -306,7 +337,7 @@ impl Template {
             it.next();
         }
 
-        let name = try!(Template::parse_name(source, it.by_ref(), limit));
+        let name = Template::parse_name(source, it.by_ref(), limit)?;
 
         loop {
             let rule;
@@ -327,14 +358,14 @@ impl Template {
 
             match rule {
                 Rule::param => {
-                    params.push(try!(Template::parse_param(source, it.by_ref(), end)));
+                    params.push(Template::parse_param(source, it.by_ref(), end)?);
                 }
                 Rule::hash => {
-                    let (key, value) = try!(Template::parse_hash(source, it.by_ref(), end));
+                    let (key, value) = 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)));
+                    block_param = Some(Template::parse_block_param(source, it.by_ref(), end)?);
                 }
                 Rule::pro_whitespace_omitter => {
                     omit_pro_ws = true;
@@ -406,7 +437,7 @@ impl Template {
     pub fn compile2<S: AsRef<str>>(source: S, mapping: bool) -> Result<Template, TemplateError> {
         let source = source.as_ref();
         let mut helper_stack: VecDeque<HelperTemplate> = VecDeque::new();
-        let mut directive_stack: VecDeque<Directive> = VecDeque::new();
+        let mut directive_stack: VecDeque<DirectiveTemplate> = VecDeque::new();
         let mut template_stack: VecDeque<Template> = VecDeque::new();
 
         let mut omit_pro_ws = false;
@@ -450,7 +481,9 @@ impl Template {
 
                 if rule != Rule::template {
                     // trailing string check
-                    if span.start() != prev_end && !omit_pro_ws && rule != Rule::raw_text
+                    if span.start() != prev_end
+                        && !omit_pro_ws
+                        && rule != Rule::raw_text
                         && rule != Rule::raw_block_text
                     {
                         let (line_no, col_no) = span.start_pos().line_col();
@@ -501,12 +534,12 @@ impl Template {
                     | Rule::raw_block_start
                     | Rule::directive_block_start
                     | Rule::partial_block_start => {
-                        let exp = try!(Template::parse_expression(source, it.by_ref(), span.end()));
+                        let exp = Template::parse_expression(source, it.by_ref(), span.end())?;
 
                         match rule {
                             Rule::helper_block_start | Rule::raw_block_start => {
                                 let helper_template = HelperTemplate {
-                                    name: exp.name.as_name().unwrap(),
+                                    name: exp.name.into_name().unwrap(),
                                     params: exp.params,
                                     hash: exp.hash,
                                     block_param: exp.block_param,
@@ -517,7 +550,7 @@ impl Template {
                                 helper_stack.push_front(helper_template);
                             }
                             Rule::directive_block_start | Rule::partial_block_start => {
-                                let directive = Directive {
+                                let directive = DirectiveTemplate {
                                     name: exp.name,
                                     params: exp.params,
                                     hash: exp.hash,
@@ -541,7 +574,7 @@ impl Template {
                     Rule::invert_tag => {
                         // hack: invert_tag structure is similar to ExpressionSpec, so I
                         // use it here to represent the data
-                        let exp = try!(Template::parse_expression(source, it.by_ref(), span.end()));
+                        let exp = Template::parse_expression(source, it.by_ref(), span.end())?;
 
                         if exp.omit_pre_ws {
                             Template::remove_previous_whitespace(&mut template_stack);
@@ -570,7 +603,7 @@ impl Template {
                     | Rule::raw_block_end
                     | Rule::directive_block_end
                     | Rule::partial_block_end => {
-                        let exp = try!(Template::parse_expression(source, it.by_ref(), span.end()));
+                        let exp = Template::parse_expression(source, it.by_ref(), span.end())?;
                         if exp.omit_pre_ws {
                             Template::remove_previous_whitespace(&mut template_stack);
                         }
@@ -590,7 +623,7 @@ impl Template {
                             }
                             Rule::helper_expression => {
                                 let helper_template = HelperTemplate {
-                                    name: exp.name.as_name().unwrap(),
+                                    name: exp.name.into_name().unwrap(),
                                     params: exp.params,
                                     hash: exp.hash,
                                     block_param: exp.block_param,
@@ -603,7 +636,7 @@ impl Template {
                                 t.push_element(el, line_no, col_no);
                             }
                             Rule::directive_expression | Rule::partial_expression => {
-                                let directive = Directive {
+                                let directive = DirectiveTemplate {
                                     name: exp.name,
                                     params: exp.params,
                                     hash: exp.hash,
@@ -619,7 +652,7 @@ impl Template {
                             }
                             Rule::helper_block_end | Rule::raw_block_end => {
                                 let mut h = helper_stack.pop_front().unwrap();
-                                let close_tag_name = exp.name.as_name().unwrap();
+                                let close_tag_name = exp.name.into_name().unwrap();
                                 if h.name == close_tag_name {
                                     let prev_t = template_stack.pop_front().unwrap();
                                     if h.template.is_some() {
@@ -663,14 +696,16 @@ impl Template {
                         }
                     }
                     Rule::hbs_comment_compact => {
-                        let text = span.as_str()
+                        let text = span
+                            .as_str()
                             .trim_left_matches("{{!")
                             .trim_right_matches("}}");
                         let t = template_stack.front_mut().unwrap();
                         t.push_element(Comment(text.to_owned()), line_no, col_no);
                     }
                     Rule::hbs_comment => {
-                        let text = span.as_str()
+                        let text = span
+                            .as_str()
                             .trim_left_matches("{{!--")
                             .trim_right_matches("--}}");
                         let t = template_stack.front_mut().unwrap();
@@ -718,10 +753,10 @@ pub enum TemplateElement {
     HTMLExpression(Parameter),
     HelperExpression(HelperTemplate),
     HelperBlock(HelperTemplate),
-    DirectiveExpression(Directive),
-    DirectiveBlock(Directive),
-    PartialExpression(Directive),
-    PartialBlock(Directive),
+    DirectiveExpression(DirectiveTemplate),
+    DirectiveBlock(DirectiveTemplate),
+    PartialExpression(DirectiveTemplate),
+    PartialBlock(DirectiveTemplate),
     Comment(String),
 }
 
@@ -832,7 +867,7 @@ fn test_subexpression() {
             assert_eq!(h.name, "foo".to_owned());
             assert_eq!(h.params.len(), 1);
             if let &Parameter::Subexpression(ref t) = h.params.get(0).unwrap() {
-                assert_eq!(t.name, "bar".to_owned());
+                assert_eq!(t.name(), "bar".to_owned());
             } else {
                 panic!("Subexpression expected");
             }
@@ -847,8 +882,8 @@ fn test_subexpression() {
             assert_eq!(h.name, "foo".to_string());
             assert_eq!(h.params.len(), 1);
             if let &Parameter::Subexpression(ref t) = h.params.get(0).unwrap() {
-                assert_eq!(t.name, "bar".to_owned());
-                if let Some(&Parameter::Name(ref n)) = t.params.get(0) {
+                assert_eq!(t.name(), "bar".to_owned());
+                if let Some(&Parameter::Name(ref n)) = t.params().unwrap().get(0) {
                     assert_eq!(n, "baz");
                 } else {
                     panic!("non-empty param expected ");
@@ -869,8 +904,8 @@ fn test_subexpression() {
             assert_eq!(h.hash.len(), 1);
 
             if let &Parameter::Subexpression(ref t) = h.params.get(0).unwrap() {
-                assert_eq!(t.name, "baz".to_owned());
-                if let Some(&Parameter::Name(ref n)) = t.params.get(0) {
+                assert_eq!(t.name(), "baz".to_owned());
+                if let Some(&Parameter::Name(ref n)) = t.params().unwrap().get(0) {
                     assert_eq!(n, "bar");
                 } else {
                     panic!("non-empty param expected ");
@@ -880,7 +915,7 @@ fn test_subexpression() {
             }
 
             if let &Parameter::Subexpression(ref t) = h.hash.get("then").unwrap() {
-                assert_eq!(t.name, "bar".to_owned());
+                assert_eq!(t.name(), "bar".to_owned());
             } else {
                 panic!("Subexpression expected (bar)");
             }
diff --git a/src/value.rs b/src/value.rs
new file mode 100644
index 0000000..ddbeaae
--- /dev/null
+++ b/src/value.rs
@@ -0,0 +1,124 @@
+use serde::Serialize;
+use serde_json::value::{to_value, Value as Json};
+
+#[derive(Debug)]
+pub enum ScopedJson<'reg: 'rc, 'rc> {
+    Constant(&'reg Json),
+    Derived(Json),
+    Context(&'rc Json),
+}
+
+impl<'reg: 'rc, 'rc> ScopedJson<'reg, 'rc> {
+    pub fn as_json(&self) -> &Json {
+        match self {
+            ScopedJson::Constant(j) => j,
+            ScopedJson::Derived(ref j) => j,
+            ScopedJson::Context(j) => j,
+        }
+    }
+
+    pub fn render(&self) -> String {
+        self.as_json().render()
+    }
+}
+
+impl<'reg: 'rc, 'rc> From<Json> for ScopedJson<'reg, 'rc> {
+    fn from(v: Json) -> ScopedJson<'reg, 'rc> {
+        ScopedJson::Derived(v)
+    }
+}
+
+/// Json wrapper that holds the Json value and reference path information
+///
+#[derive(Debug)]
+pub struct PathAndJson<'reg: 'rc, 'rc> {
+    path: Option<String>,
+    value: ScopedJson<'reg, 'rc>,
+}
+
+impl<'reg: 'rc, 'rc> PathAndJson<'reg, 'rc> {
+    pub fn new(path: Option<String>, value: ScopedJson<'reg, 'rc>) -> PathAndJson<'reg, 'rc> {
+        PathAndJson { path, value }
+    }
+
+    /// Returns relative path when the value is referenced
+    /// If the value is from a literal, the path is `None`
+    pub fn path(&self) -> Option<&String> {
+        self.path.as_ref()
+    }
+
+    /// Return root level of this path if any
+    pub fn path_root(&self) -> Option<&str> {
+        self.path
+            .as_ref()
+            .and_then(|p| p.split(|c| c == '.' || c == '/').nth(0))
+    }
+
+    /// Returns the value
+    pub fn value(&self) -> &Json {
+        self.value.as_json()
+    }
+}
+
+/// Render Json data with default format
+pub trait JsonRender {
+    fn render(&self) -> String;
+}
+
+pub trait JsonTruthy {
+    fn is_truthy(&self) -> bool;
+}
+
+impl JsonRender for Json {
+    fn render(&self) -> String {
+        match *self {
+            Json::String(ref s) => s.to_string(),
+            Json::Bool(i) => i.to_string(),
+            Json::Number(ref n) => n.to_string(),
+            Json::Null => "".to_owned(),
+            Json::Array(ref a) => {
+                let mut buf = String::new();
+                buf.push('[');
+                for i in a.iter() {
+                    buf.push_str(i.render().as_ref());
+                    buf.push_str(", ");
+                }
+                buf.push(']');
+                buf
+            }
+            Json::Object(_) => "[object]".to_owned(),
+        }
+    }
+}
+
+pub fn to_json<T>(src: &T) -> Json
+where
+    T: Serialize,
+{
+    to_value(src).unwrap_or_default()
+}
+
+pub fn as_string(src: &Json) -> Option<&str> {
+    src.as_str()
+}
+
+impl JsonTruthy for Json {
+    fn is_truthy(&self) -> bool {
+        match *self {
+            Json::Bool(ref i) => *i,
+            Json::Number(ref n) => n.as_f64().map(|f| f.is_normal()).unwrap_or(false),
+            Json::Null => false,
+            Json::String(ref i) => i.len() > 0,
+            Json::Array(ref i) => i.len() > 0,
+            Json::Object(ref i) => i.len() > 0,
+        }
+    }
+}
+
+#[test]
+fn test_json_render() {
+    let raw = "<p>Hello world</p>\n<p thing=\"hello\"</p>";
+    let thing = Json::String(raw.to_string());
+
+    assert_eq!(raw, thing.render());
+}


Test patch:
diff --git a/tests/subexpression.rs b/tests/subexpression.rs
index 339a11e..81b739d 100644
--- a/tests/subexpression.rs
+++ b/tests/subexpression.rs
@@ -2,55 +2,54 @@ extern crate handlebars;
 #[macro_use]
 extern crate serde_json;
 
-use handlebars::{Handlebars, Helper, HelperDef, RenderContext, RenderError};
+use handlebars::{Context, Handlebars, Helper, HelperDef, RenderContext, RenderError, ScopedJson};
 use serde_json::Value;
 
 struct GtHelper;
 
 impl HelperDef for GtHelper {
-    fn call_inner(
+    fn call_inner<'reg: 'rc, 'rc>(
         &self,
         h: &Helper,
         _: &Handlebars,
+        _: &Context,
         _: &mut RenderContext,
-    ) -> Result<Option<Value>, RenderError> {
-        let p1 = try!(
-            h.param(0,)
-                .and_then(|v| v.value().as_i64(),)
-                .ok_or(RenderError::new(
-                    "Param 0 with i64 type is required for gt helper."
-                ),)
-        );
-        let p2 = try!(
-            h.param(1,)
-                .and_then(|v| v.value().as_i64(),)
-                .ok_or(RenderError::new(
-                    "Param 1 with i64 type is required for gt helper."
-                ),)
-        );
-
-        Ok(Some(Value::Bool(p1 > p2)))
+    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
+        let p1 = h
+            .param(0)
+            .and_then(|v| v.value().as_i64())
+            .ok_or(RenderError::new(
+                "Param 0 with i64 type is required for gt helper.",
+            ))?;
+        let p2 = h
+            .param(1)
+            .and_then(|v| v.value().as_i64())
+            .ok_or(RenderError::new(
+                "Param 1 with i64 type is required for gt helper.",
+            ))?;
+
+        Ok(Some(Value::Bool(p1 > p2).into()))
     }
 }
 
 struct NotHelper;
 
 impl HelperDef for NotHelper {
-    fn call_inner(
+    fn call_inner<'reg: 'rc, 'rc>(
         &self,
         h: &Helper,
         _: &Handlebars,
+        _: &Context,
         _: &mut RenderContext,
-    ) -> Result<Option<Value>, RenderError> {
-        let p1 = try!(
-            h.param(0,)
-                .and_then(|v| v.value().as_bool(),)
-                .ok_or(RenderError::new(
-                    "Param 0 with bool type is required for not helper."
-                ),)
-        );
-
-        Ok(Some(Value::Bool(!p1)))
+    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
+        let p1 = h
+            .param(0)
+            .and_then(|v| v.value().as_bool())
+            .ok_or(RenderError::new(
+                "Param 0 with bool type is required for not helper.",
+            ))?;
+
+        Ok(Some(Value::Bool(!p1).into()))
     }
 }