From 7aff8962924fb4a391b84659c9cb4ef87f4a9b16 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Wed, 29 Dec 2021 18:41:22 -0500 Subject: [PATCH] Pass handlebars render context down when rendering via `get` helper. This isn't used yet, but will allow the helper to modify the context (useful for #10). --- src/content/content_engine.rs | 1 + src/content/content_item.rs | 62 ++++++++++++++++++++++++--- src/content/content_registry.rs | 1 + src/content/handlebars_helpers/get.rs | 10 ++--- src/content/mod.rs | 11 +++++ src/content/test_lib.rs | 1 + 6 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/content/content_engine.rs b/src/content/content_engine.rs index 1e6339e..f948026 100644 --- a/src/content/content_engine.rs +++ b/src/content/content_engine.rs @@ -383,6 +383,7 @@ where ) -> RenderContext { RenderContext { content_engine: self, + handlebars_render_context: None, data: RenderData { server_info: self.server_info.clone(), index: self.index.clone(), diff --git a/src/content/content_item.rs b/src/content/content_item.rs index 36d7f13..8efbdca 100644 --- a/src/content/content_item.rs +++ b/src/content/content_item.rs @@ -45,6 +45,9 @@ pub enum RenderingFailedError { #[from] source: io::Error, }, + + #[error("{} This should never happen: {}", bug_message!(), .0)] + Bug(String), } /// A static file from the content directory (such as an image or a text file). @@ -88,6 +91,7 @@ impl RegisteredTemplate { &self, handlebars_registry: &Handlebars, render_data: RenderData, + handlebars_render_context: Option, ) -> Result, RenderingFailedError> where ServerInfo: Clone + Serialize, @@ -96,7 +100,23 @@ impl RegisteredTemplate { target_media_type: Some(self.rendered_media_type.clone()), ..render_data }; - let rendered_content = handlebars_registry.render(&self.name_in_registry, &render_data)?; + let rendered_content = match handlebars_render_context { + None => handlebars_registry.render(&self.name_in_registry, &render_data)?, + Some(mut handlebars_render_context) => handlebars_registry + .get_template(&self.name_in_registry) + .ok_or_else(|| { + RenderingFailedError::Bug(format!( + "Template '{}' was not found in the registry", + &self.name_in_registry + )) + })? + .renders( + handlebars_registry, + &handlebars::Context::wraps(&render_data)?, + &mut handlebars_render_context, + )?, + }; + Ok(Media::new( self.rendered_media_type.clone(), InMemoryBody(rendered_content.bytes().collect()), @@ -247,7 +267,7 @@ mod tests { use crate::test_lib::*; use crate::ServerInfo; use ::mime; - use std::collections::HashMap; + use maplit::hashmap; use std::fs; use std::io::Write; use std::str; @@ -262,8 +282,8 @@ mod tests { error_code: None, request: RequestData { route: None, - query_parameters: HashMap::new(), - request_headers: HashMap::new(), + query_parameters: hashmap![], + request_headers: hashmap![], }, } } @@ -317,7 +337,7 @@ mod tests { let rendered = template.render_to_native_media_type( content_engine.handlebars_registry(), content_engine - .render_context(None, HashMap::new(), HashMap::new()) + .render_context(None, hashmap![], hashmap![]) .data, ); @@ -339,14 +359,44 @@ mod tests { let rendered = template.render_to_native_media_type( content_engine.handlebars_registry(), content_engine - .render_context(Some(route("/test")), HashMap::new(), HashMap::new()) + .render_context(Some(route("/test")), hashmap![], hashmap![]) .data, + None, ); let template_output = media_to_string(rendered.expect("Rendering failed")); assert_eq!(template_output, "it works!"); } + #[test] + fn registered_template_can_be_rendered_with_custom_handlebars_context() { + let mut content_engine = MockContentEngine::new(); + content_engine + .register_template("test", "{{ ping }}") + .expect("Could not register test template"); + + let template = RegisteredTemplate::new( + "test", + MediaType::from_media_range(mime::TEXT_PLAIN).unwrap(), + ); + + let replaced_render_data = handlebars::Context::wraps(hashmap!["ping" => "pong"]) + .expect("Could not create fake render data"); + let mut handlebars_render_context = handlebars::RenderContext::new(None); + handlebars_render_context.set_context(replaced_render_data); + + let rendered = template.render_to_native_media_type( + content_engine.handlebars_registry(), + content_engine + .render_context(Some(route("/test")), hashmap![], hashmap![]) + .data, + Some(handlebars_render_context), + ); + + let template_output = media_to_string(rendered.expect("Rendering failed")); + assert_eq!(template_output, "pong"); + } + #[test] fn executables_execute_when_rendered() { let path = format!("{}/src", PROJECT_DIRECTORY); diff --git a/src/content/content_registry.rs b/src/content/content_registry.rs index 1e43372..7118f2c 100644 --- a/src/content/content_registry.rs +++ b/src/content/content_registry.rs @@ -62,6 +62,7 @@ impl Render for ContentRepresentations { .render_to_native_media_type( context.content_engine.handlebars_registry(), context.data.clone(), + context.handlebars_render_context.clone(), ) .map(box_media), RegisteredContent::Executable(renderable) => renderable diff --git a/src/content/handlebars_helpers/get.rs b/src/content/handlebars_helpers/get.rs index 9a87829..e938969 100644 --- a/src/content/handlebars_helpers/get.rs +++ b/src/content/handlebars_helpers/get.rs @@ -38,7 +38,7 @@ where helper: &handlebars::Helper<'registry, 'context>, _: &'registry Handlebars<'registry>, handlebars_context: &'context handlebars::Context, - _: &mut handlebars::RenderContext<'registry, 'context>, + handlebars_render_context: &mut handlebars::RenderContext<'registry, 'context>, output: &mut dyn handlebars::Output, ) -> handlebars::HelperResult { let content_engine = self @@ -91,11 +91,9 @@ where let query_parameters = get_query_parameters(current_render_data, handlebars_context)?; let request_headers = get_request_headers(current_render_data, handlebars_context)?; - let context = content_engine.render_context( - optional_request_route, - query_parameters, - request_headers, - ); + let context = content_engine + .render_context(optional_request_route, query_parameters, request_headers) + .with_handlebars_render_context(handlebars_render_context.clone()); let rendered = content_item .render(context, &[target_media_type.into_media_range()]).map_err(|render_error| { diff --git a/src/content/mod.rs b/src/content/mod.rs index 6b4431e..83ffd36 100644 --- a/src/content/mod.rs +++ b/src/content/mod.rs @@ -168,6 +168,7 @@ where { content_engine: &'engine Engine, data: RenderData, + handlebars_render_context: Option>, } impl<'engine, ServerInfo, Engine> RenderContext<'engine, ServerInfo, Engine> @@ -184,4 +185,14 @@ where ..self } } + + pub fn with_handlebars_render_context( + self, + handlebars_render_context: handlebars::RenderContext<'engine, 'engine>, + ) -> Self { + RenderContext { + handlebars_render_context: Some(handlebars_render_context), + ..self + } + } } diff --git a/src/content/test_lib.rs b/src/content/test_lib.rs index cc4efef..841fb1a 100644 --- a/src/content/test_lib.rs +++ b/src/content/test_lib.rs @@ -31,6 +31,7 @@ impl<'a> ContentEngine<()> for MockContentEngine<'a> { ) -> RenderContext<(), Self> { RenderContext { content_engine: self, + handlebars_render_context: None, data: RenderData { server_info: (), index: ContentIndex::Directory(ContentIndexEntries::new()),