mirror of https://github.com/rwf2/Rocket.git
Add 'context!' for ad-hoc templating contexts.
This commit is contained in:
parent
6a3d1ac1d5
commit
8e3ad40beb
|
@ -119,7 +119,8 @@
|
||||||
//! Templates are rendered with the `render` method. The method takes in the
|
//! Templates are rendered with the `render` method. The method takes in the
|
||||||
//! name of a template and a context to render the template with. The context
|
//! name of a template and a context to render the template with. The context
|
||||||
//! can be any type that implements [`Serialize`] and would serialize to an
|
//! can be any type that implements [`Serialize`] and would serialize to an
|
||||||
//! `Object` value.
|
//! `Object` value. The [`context!`] macro can also be used to create inline
|
||||||
|
//! `Serialize`-able context objects.
|
||||||
//!
|
//!
|
||||||
//! ## Automatic Reloading
|
//! ## Automatic Reloading
|
||||||
//!
|
//!
|
||||||
|
@ -167,6 +168,9 @@ use self::context::{Context, ContextManager};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use rocket::serde;
|
||||||
|
|
||||||
use rocket::{Rocket, Orbit, Ignite, Sentinel};
|
use rocket::{Rocket, Orbit, Ignite, Sentinel};
|
||||||
use rocket::request::Request;
|
use rocket::request::Request;
|
||||||
use rocket::fairing::Fairing;
|
use rocket::fairing::Fairing;
|
||||||
|
@ -301,20 +305,32 @@ impl Template {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the template named `name` with the context `context`. The
|
/// Render the template named `name` with the context `context`. The
|
||||||
/// `context` can be of any type that implements `Serialize`. This is
|
/// `context` is typically created using the [`context!`] macro, but it can
|
||||||
/// typically a `HashMap` or a custom `struct`.
|
/// be of any type that implements `Serialize`, such as `HashMap` or a
|
||||||
|
/// custom `struct`.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Using the `context` macro:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket_dyn_templates::{Template, context};
|
||||||
|
///
|
||||||
|
/// let template = Template::render("index", context! {
|
||||||
|
/// foo: "Hello, world!",
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Using a `HashMap` as the context:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::collections::HashMap;
|
/// use std::collections::HashMap;
|
||||||
/// use rocket_dyn_templates::Template;
|
/// use rocket_dyn_templates::Template;
|
||||||
///
|
///
|
||||||
/// // Create a `context`. Here, just an empty `HashMap`.
|
/// // Create a `context` from a `HashMap`.
|
||||||
/// let mut context = HashMap::new();
|
/// let mut context = HashMap::new();
|
||||||
|
/// context.insert("foo", "Hello, world!");
|
||||||
///
|
///
|
||||||
/// # context.insert("test", "test");
|
|
||||||
/// # #[allow(unused_variables)]
|
|
||||||
/// let template = Template::render("index", context);
|
/// let template = Template::render("index", context);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -353,9 +369,7 @@ impl Template {
|
||||||
///
|
///
|
||||||
/// // Create a `context`. Here, just an empty `HashMap`.
|
/// // Create a `context`. Here, just an empty `HashMap`.
|
||||||
/// let mut context = HashMap::new();
|
/// let mut context = HashMap::new();
|
||||||
///
|
|
||||||
/// # context.insert("test", "test");
|
/// # context.insert("test", "test");
|
||||||
/// # #[allow(unused_variables)]
|
|
||||||
/// let template = Template::show(client.rocket(), "index", context);
|
/// let template = Template::show(client.rocket(), "index", context);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -435,3 +449,105 @@ impl Sentinel for Template {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A macro to easily create a template rendering context.
|
||||||
|
///
|
||||||
|
/// Invocations of this macro expand to a value of an anonymous type which
|
||||||
|
/// implements [`serde::Serialize`]. Fields can be literal expressions or
|
||||||
|
/// variables captured from a surrounding scope, as long as all fields implement
|
||||||
|
/// `Serialize`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// The following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[macro_use] extern crate rocket;
|
||||||
|
/// # use rocket_dyn_templates::{Template, context};
|
||||||
|
/// #[get("/<foo>")]
|
||||||
|
/// fn render_index(foo: u64) -> Template {
|
||||||
|
/// Template::render("index", context! {
|
||||||
|
/// // Note that shorthand field syntax is supports.
|
||||||
|
/// // This is equivalent to `foo: foo,`
|
||||||
|
/// foo,
|
||||||
|
/// bar: "Hello world",
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// is equivalent to the following, but without the need to manually define an
|
||||||
|
/// `IndexContext` struct:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use rocket_dyn_templates::Template;
|
||||||
|
/// # use rocket::serde::Serialize;
|
||||||
|
/// # use rocket::get;
|
||||||
|
/// #[derive(Serialize)]
|
||||||
|
/// # #[serde(crate = "rocket::serde")]
|
||||||
|
/// struct IndexContext<'a> {
|
||||||
|
/// foo: u64,
|
||||||
|
/// bar: &'a str,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[get("/<foo>")]
|
||||||
|
/// fn render_index(foo: u64) -> Template {
|
||||||
|
/// Template::render("index", IndexContext {
|
||||||
|
/// foo,
|
||||||
|
/// bar: "Hello world",
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Nesting
|
||||||
|
///
|
||||||
|
/// Nested objects can be created by nesting calls to `context!`:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use rocket_dyn_templates::context;
|
||||||
|
/// # fn main() {
|
||||||
|
/// let ctx = context! {
|
||||||
|
/// planet: "Earth",
|
||||||
|
/// info: context! {
|
||||||
|
/// mass: 5.97e24,
|
||||||
|
/// radius: "6371 km",
|
||||||
|
/// moons: 1,
|
||||||
|
/// },
|
||||||
|
/// };
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! context {
|
||||||
|
($($key:ident $(: $value:expr)?),*$(,)?) => {{
|
||||||
|
use $crate::serde::ser::{Serialize, Serializer, SerializeMap};
|
||||||
|
use ::std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
struct ContextMacroCtxObject<$($key: Serialize),*> {
|
||||||
|
$($key: $key),*
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
impl<$($key: Serialize),*> Serialize for ContextMacroCtxObject<$($key),*> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: Serializer,
|
||||||
|
{
|
||||||
|
let mut map = serializer.serialize_map(None)?;
|
||||||
|
$(map.serialize_entry(stringify!($key), &self.$key)?;)*
|
||||||
|
map.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
impl<$($key: Debug + Serialize),*> Debug for ContextMacroCtxObject<$($key),*> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> ::std::fmt::Result {
|
||||||
|
f.debug_struct("context!")
|
||||||
|
$(.field(stringify!($key), &self.$key))*
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextMacroCtxObject {
|
||||||
|
$($key $(: $value)?),*
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
|
@ -14,13 +14,12 @@ use crate::context::ContextManager;
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # #[macro_use] extern crate rocket;
|
/// # #[macro_use] extern crate rocket;
|
||||||
/// # #[macro_use] extern crate rocket_dyn_templates;
|
/// # #[macro_use] extern crate rocket_dyn_templates;
|
||||||
/// use rocket_dyn_templates::{Template, Metadata};
|
/// use rocket_dyn_templates::{Template, Metadata, context};
|
||||||
///
|
///
|
||||||
/// #[get("/")]
|
/// #[get("/")]
|
||||||
/// fn homepage(metadata: Metadata) -> Template {
|
/// fn homepage(metadata: Metadata) -> Template {
|
||||||
/// # use std::collections::HashMap;
|
|
||||||
/// # let context: HashMap<String, String> = HashMap::new();
|
|
||||||
/// // Conditionally render a template if it's available.
|
/// // Conditionally render a template if it's available.
|
||||||
|
/// # let context = ();
|
||||||
/// if metadata.contains_template("some-template") {
|
/// if metadata.contains_template("some-template") {
|
||||||
/// Template::render("some-template", &context)
|
/// Template::render("some-template", &context)
|
||||||
/// } else {
|
/// } else {
|
||||||
|
|
|
@ -4,7 +4,9 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use rocket::{Rocket, Build};
|
use rocket::{Rocket, Build};
|
||||||
use rocket::config::Config;
|
use rocket::config::Config;
|
||||||
use rocket_dyn_templates::{Template, Metadata};
|
use rocket::figment::value::Value;
|
||||||
|
use rocket::serde::{Serialize, Deserialize};
|
||||||
|
use rocket_dyn_templates::{Template, Metadata, context};
|
||||||
|
|
||||||
#[get("/<engine>/<name>")]
|
#[get("/<engine>/<name>")]
|
||||||
fn template_check(md: Metadata<'_>, engine: &str, name: &str) -> Option<()> {
|
fn template_check(md: Metadata<'_>, engine: &str, name: &str) -> Option<()> {
|
||||||
|
@ -98,6 +100,109 @@ fn test_sentinel() {
|
||||||
Client::debug_with(routes![always_ok_sentinel]).expect("no sentinel abort");
|
Client::debug_with(routes![always_ok_sentinel]).expect("no sentinel abort");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_context_macro() {
|
||||||
|
macro_rules! assert_same_object {
|
||||||
|
($ctx:expr, $obj:expr $(,)?) => {{
|
||||||
|
let ser_ctx = Value::serialize(&$ctx).unwrap();
|
||||||
|
let deser_ctx = ser_ctx.deserialize().unwrap();
|
||||||
|
assert_eq!($obj, deser_ctx);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
struct Empty { }
|
||||||
|
|
||||||
|
assert_same_object!(context! { }, Empty { });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
struct Object {
|
||||||
|
a: u32,
|
||||||
|
b: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = 93;
|
||||||
|
let b = "Hello".to_string();
|
||||||
|
|
||||||
|
fn make_context() -> impl Serialize {
|
||||||
|
let b = "Hello".to_string();
|
||||||
|
|
||||||
|
context! { a: 93, b: b }
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_same_object!(
|
||||||
|
make_context(),
|
||||||
|
Object { a, b },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
struct Outer {
|
||||||
|
s: String,
|
||||||
|
inner: Inner,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
struct Inner {
|
||||||
|
center: Center,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
struct Center {
|
||||||
|
value_a: bool,
|
||||||
|
value_b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = true;
|
||||||
|
let value_b = 123;
|
||||||
|
let outer_string = String::from("abc 123");
|
||||||
|
|
||||||
|
assert_same_object!(
|
||||||
|
context! {
|
||||||
|
s: &outer_string,
|
||||||
|
inner: context! {
|
||||||
|
center: context! {
|
||||||
|
value_a: a,
|
||||||
|
value_b,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outer {
|
||||||
|
s: outer_string,
|
||||||
|
inner: Inner {
|
||||||
|
center: Center {
|
||||||
|
value_a: a,
|
||||||
|
value_b,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
struct Object {
|
||||||
|
a: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
let owned = String::from("foo");
|
||||||
|
let ctx = context! { a: &owned };
|
||||||
|
assert_same_object!(ctx, Object { a: "foo".into() });
|
||||||
|
drop(ctx);
|
||||||
|
drop(owned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "tera")]
|
#[cfg(feature = "tera")]
|
||||||
mod tera_tests {
|
mod tera_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use rocket::form::Form;
|
use rocket::form::Form;
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
use rocket::http::{Cookie, CookieJar};
|
use rocket::http::{Cookie, CookieJar};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::{Template, context};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! message_uri {
|
macro_rules! message_uri {
|
||||||
|
@ -21,12 +19,10 @@ fn submit(cookies: &CookieJar<'_>, message: Form<&str>) -> Redirect {
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn index(cookies: &CookieJar<'_>) -> Template {
|
fn index(cookies: &CookieJar<'_>) -> Template {
|
||||||
let cookie = cookies.get("message");
|
let cookie = cookies.get("message");
|
||||||
let mut context = HashMap::new();
|
|
||||||
if let Some(ref cookie) = cookie {
|
|
||||||
context.insert("message", cookie.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
Template::render("message", &context)
|
Template::render("message", context! {
|
||||||
|
message: cookie.map(|c| c.value()),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn routes() -> Vec<rocket::Route> {
|
pub fn routes() -> Vec<rocket::Route> {
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use rocket::outcome::IntoOutcome;
|
use rocket::outcome::IntoOutcome;
|
||||||
use rocket::request::{self, FlashMessage, FromRequest, Request};
|
use rocket::request::{self, FlashMessage, FromRequest, Request};
|
||||||
use rocket::response::{Redirect, Flash};
|
use rocket::response::{Redirect, Flash};
|
||||||
use rocket::http::{Cookie, CookieJar};
|
use rocket::http::{Cookie, CookieJar};
|
||||||
use rocket::form::Form;
|
use rocket::form::Form;
|
||||||
|
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::{Template, context};
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
struct Login<'r> {
|
struct Login<'r> {
|
||||||
|
@ -39,9 +37,9 @@ pub use session_uri as uri;
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn index(user: User) -> Template {
|
fn index(user: User) -> Template {
|
||||||
let mut context = HashMap::new();
|
Template::render("session", context! {
|
||||||
context.insert("user_id", user.0);
|
user_id: user.0,
|
||||||
Template::render("session", &context)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/", rank = 2)]
|
#[get("/", rank = 2)]
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
use rocket::Request;
|
use rocket::Request;
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
use rocket::serde::Serialize;
|
|
||||||
|
|
||||||
use rocket_dyn_templates::{Template, handlebars};
|
use rocket_dyn_templates::{Template, handlebars, context};
|
||||||
|
|
||||||
use self::handlebars::{Handlebars, JsonRender};
|
use self::handlebars::{Handlebars, JsonRender};
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
struct TemplateContext<'r> {
|
|
||||||
title: &'r str,
|
|
||||||
name: Option<&'r str>,
|
|
||||||
items: Vec<&'r str>,
|
|
||||||
// This special key tells handlebars which template is the parent.
|
|
||||||
parent: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub fn index() -> Redirect {
|
pub fn index() -> Redirect {
|
||||||
Redirect::to(uri!("/hbs", hello(name = "Your Name")))
|
Redirect::to(uri!("/hbs", hello(name = "Your Name")))
|
||||||
|
@ -23,27 +12,28 @@ pub fn index() -> Redirect {
|
||||||
|
|
||||||
#[get("/hello/<name>")]
|
#[get("/hello/<name>")]
|
||||||
pub fn hello(name: &str) -> Template {
|
pub fn hello(name: &str) -> Template {
|
||||||
Template::render("hbs/index", &TemplateContext {
|
Template::render("hbs/index", context! {
|
||||||
title: "Hello",
|
title: "Hello",
|
||||||
name: Some(name),
|
name: Some(name),
|
||||||
items: vec!["One", "Two", "Three"],
|
items: vec!["One", "Two", "Three"],
|
||||||
|
// This special key tells handlebars which template is the parent.
|
||||||
parent: "hbs/layout",
|
parent: "hbs/layout",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/about")]
|
#[get("/about")]
|
||||||
pub fn about() -> Template {
|
pub fn about() -> Template {
|
||||||
let mut map = std::collections::HashMap::new();
|
Template::render("hbs/about.html", context! {
|
||||||
map.insert("title", "About");
|
title: "About",
|
||||||
map.insert("parent", "hbs/layout");
|
parent: "hbs/layout",
|
||||||
Template::render("hbs/about.html", &map)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[catch(404)]
|
#[catch(404)]
|
||||||
pub fn not_found(req: &Request<'_>) -> Template {
|
pub fn not_found(req: &Request<'_>) -> Template {
|
||||||
let mut map = std::collections::HashMap::new();
|
Template::render("hbs/error/404", context! {
|
||||||
map.insert("path", req.uri().path().raw());
|
uri: req.uri()
|
||||||
Template::render("hbs/error/404", &map)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wow_helper(
|
fn wow_helper(
|
||||||
|
|
|
@ -1,18 +1,7 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use rocket::Request;
|
use rocket::Request;
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
use rocket::serde::Serialize;
|
|
||||||
|
|
||||||
use rocket_dyn_templates::{Template, tera::Tera};
|
use rocket_dyn_templates::{Template, tera::Tera, context};
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
struct TemplateContext<'r> {
|
|
||||||
title: &'r str,
|
|
||||||
name: Option<&'r str>,
|
|
||||||
items: Vec<&'r str>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub fn index() -> Redirect {
|
pub fn index() -> Redirect {
|
||||||
|
@ -21,7 +10,7 @@ pub fn index() -> Redirect {
|
||||||
|
|
||||||
#[get("/hello/<name>")]
|
#[get("/hello/<name>")]
|
||||||
pub fn hello(name: &str) -> Template {
|
pub fn hello(name: &str) -> Template {
|
||||||
Template::render("tera/index", &TemplateContext {
|
Template::render("tera/index", context! {
|
||||||
title: "Hello",
|
title: "Hello",
|
||||||
name: Some(name),
|
name: Some(name),
|
||||||
items: vec!["One", "Two", "Three"],
|
items: vec!["One", "Two", "Three"],
|
||||||
|
@ -30,16 +19,16 @@ pub fn hello(name: &str) -> Template {
|
||||||
|
|
||||||
#[get("/about")]
|
#[get("/about")]
|
||||||
pub fn about() -> Template {
|
pub fn about() -> Template {
|
||||||
let mut map = HashMap::new();
|
Template::render("tera/about.html", context! {
|
||||||
map.insert("title", "About");
|
title: "About",
|
||||||
Template::render("tera/about.html", &map)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[catch(404)]
|
#[catch(404)]
|
||||||
pub fn not_found(req: &Request<'_>) -> Template {
|
pub fn not_found(req: &Request<'_>) -> Template {
|
||||||
let mut map = HashMap::new();
|
Template::render("tera/error/404", context! {
|
||||||
map.insert("path", req.uri().path().raw());
|
uri: req.uri()
|
||||||
Template::render("tera/error/404", &map)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn customize(tera: &mut Tera) {
|
pub fn customize(tera: &mut Tera) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use super::rocket;
|
||||||
|
|
||||||
use rocket::http::{RawStr, Status, Method::*};
|
use rocket::http::{RawStr, Status, Method::*};
|
||||||
use rocket::local::blocking::Client;
|
use rocket::local::blocking::Client;
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::{Template, context};
|
||||||
|
|
||||||
fn test_root(kind: &str) {
|
fn test_root(kind: &str) {
|
||||||
// Check that the redirect works.
|
// Check that the redirect works.
|
||||||
|
@ -18,9 +18,8 @@ fn test_root(kind: &str) {
|
||||||
|
|
||||||
// Check that other request methods are not accepted (and instead caught).
|
// Check that other request methods are not accepted (and instead caught).
|
||||||
for method in &[Post, Put, Delete, Options, Trace, Connect, Patch] {
|
for method in &[Post, Put, Delete, Options, Trace, Connect, Patch] {
|
||||||
let mut map = std::collections::HashMap::new();
|
let context = context! { uri: format!("/{}", kind) };
|
||||||
map.insert("path", format!("/{}", kind));
|
let expected = Template::show(client.rocket(), format!("{}/error/404", kind), &context);
|
||||||
let expected = Template::show(client.rocket(), format!("{}/error/404", kind), &map);
|
|
||||||
|
|
||||||
let response = client.req(*method, format!("/{}", kind)).dispatch();
|
let response = client.req(*method, format!("/{}", kind)).dispatch();
|
||||||
assert_eq!(response.status(), Status::NotFound);
|
assert_eq!(response.status(), Status::NotFound);
|
||||||
|
|
|
@ -6,6 +6,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>404: Hey! There's nothing here.</h1>
|
<h1>404: Hey! There's nothing here.</h1>
|
||||||
The page at {{ path }} does not exist!
|
The page at {{ uri }} does not exist!
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -6,6 +6,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>404: Hey! There's nothing here.</h1>
|
<h1>404: Hey! There's nothing here.</h1>
|
||||||
The page at {{ path }} does not exist!
|
The page at {{ uri }} does not exist!
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -441,6 +441,24 @@ a template and a context to render the template with. The context can be any
|
||||||
type that implements `Serialize` and serializes into an `Object` value, such as
|
type that implements `Serialize` and serializes into an `Object` value, such as
|
||||||
structs, `HashMaps`, and others.
|
structs, `HashMaps`, and others.
|
||||||
|
|
||||||
|
You can also use [`context!`] to create ad-hoc templating contexts without
|
||||||
|
defining a new type:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# #[macro_use] extern crate rocket;
|
||||||
|
# #[macro_use] extern crate rocket_dyn_templates;
|
||||||
|
# fn main() {}
|
||||||
|
|
||||||
|
use rocket_dyn_templates::Template;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn index() -> Template {
|
||||||
|
Template::render("index", context! {
|
||||||
|
foo: 123,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
For a template to be renderable, it must first be registered. The `Template`
|
For a template to be renderable, it must first be registered. The `Template`
|
||||||
fairing automatically registers all discoverable templates when attached. The
|
fairing automatically registers all discoverable templates when attached. The
|
||||||
[Fairings](../fairings) sections of the guide provides more information on
|
[Fairings](../fairings) sections of the guide provides more information on
|
||||||
|
@ -472,6 +490,8 @@ used.
|
||||||
the name `"index"` in templates, i.e, `{% extends "index" %}` or `{% extends
|
the name `"index"` in templates, i.e, `{% extends "index" %}` or `{% extends
|
||||||
"base" %}` for `base.html.tera`.
|
"base" %}` for `base.html.tera`.
|
||||||
|
|
||||||
|
[`context`]: @api/rocket_dyn_templates/macro.context.html
|
||||||
|
|
||||||
### Live Reloading
|
### Live Reloading
|
||||||
|
|
||||||
When your application is compiled in `debug` mode (without the `--release` flag
|
When your application is compiled in `debug` mode (without the `--release` flag
|
||||||
|
|
Loading…
Reference in New Issue