From fc63765ee1713e88453329ab87afce565e316126 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Mon, 7 Aug 2017 20:38:13 +0900 Subject: [PATCH] Add a get_static_url global fn Fix #108 --- CHANGELOG.md | 1 + Cargo.lock | 2 + README.md | 14 +++++- components/config/Cargo.toml | 1 + components/config/src/lib.rs | 9 +++- components/site/src/lib.rs | 1 + components/templates/Cargo.toml | 1 + components/templates/src/global_fns.rs | 59 +++++++++++++++++++++++++- components/templates/src/lib.rs | 1 + 9 files changed, 86 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fdc4ce..dd49e3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ root page - Make `title` in config optional - Improved `gutenberg init` UX and users first experience +- Add a `get_static_url` global function that can do cachebusting ## 0.1.1 (2017-07-16) diff --git a/Cargo.lock b/Cargo.lock index c2e0d2c..34d44d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,6 +233,7 @@ dependencies = [ name = "config" version = "0.1.0" dependencies = [ + "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "errors 0.1.0", "rendering 0.1.0", "serde 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1144,6 +1145,7 @@ name = "templates" version = "0.1.0" dependencies = [ "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "config 0.1.0", "content 0.1.0", "errors 0.1.0", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/README.md b/README.md index 5d9a741..7efb170 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ the absolute URL of the current page and `current_path` that represents the path If you want to know all the data present in a template content, simply put `{{ __tera_context }}` in the templates and it will print it. -Gutenberg also ships with 3 Tera global functions: +Gutenberg also ships with a few Tera global functions: #### `get_page` Takes a path to a `.md` file and returns the associated page @@ -88,6 +88,18 @@ link in markdown. {% set url = get_url(link="./blog/_index.md") %} ``` +#### `get_static_url` +Gets the permalink for a given path, with cachebusting support. + +```jinja2 +{% get_static_url(path="path.css") %} +``` + +The path should start at the static folder root and should not being with a `/`. By default, +this will add a cachebust of the format `?t=1290192` at the end of a URL. You can disable it +by passing `cachebust=false` to the function. + + ### Static files Everything in the `static` folder will be copied into the output directory as-is. diff --git a/components/config/Cargo.toml b/components/config/Cargo.toml index 589359d..207c411 100644 --- a/components/config/Cargo.toml +++ b/components/config/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Vincent Prouillet "] toml = "0.4" serde = "1.0" serde_derive = "1.0" +chrono = "0.4" errors = { path = "../errors" } rendering = { path = "../rendering" } diff --git a/components/config/src/lib.rs b/components/config/src/lib.rs index 32391c9..c74206f 100644 --- a/components/config/src/lib.rs +++ b/components/config/src/lib.rs @@ -4,6 +4,7 @@ extern crate toml; #[macro_use] extern crate errors; extern crate rendering; +extern crate chrono; use std::collections::HashMap; use std::fs::File; @@ -11,12 +12,13 @@ use std::io::prelude::*; use std::path::Path; use toml::{Value as Toml}; +use chrono::Utc; use errors::{Result, ResultExt}; use rendering::highlighting::THEME_SET; -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Config { /// Base URL of the site, the only required config argument pub base_url: String, @@ -48,6 +50,9 @@ pub struct Config { /// All user params set in [extra] in the config pub extra: Option>, + + /// Set automatically when instantiating the config. Used for cachebusting + pub build_timestamp: Option, } macro_rules! set_default { @@ -85,6 +90,7 @@ impl Config { None => config.highlight_theme = Some("base16-ocean-dark".to_string()) }; + config.build_timestamp = Some(Utc::now().timestamp()); Ok(config) } @@ -136,6 +142,7 @@ impl Default for Config { insert_anchor_links: Some(false), compile_sass: Some(false), extra: None, + build_timestamp: Some(1), } } } diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index b66b677..25a10f0 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -218,6 +218,7 @@ impl Site { self.tera.register_global_function("get_page", global_fns::make_get_page(&self.pages)); self.tera.register_global_function("get_section", global_fns::make_get_section(&self.sections)); + self.tera.register_global_function("get_static_url", global_fns::make_get_static_url(self.config.clone())); self.register_get_url_fn(); Ok(()) diff --git a/components/templates/Cargo.toml b/components/templates/Cargo.toml index 7bb0ec8..033f29b 100644 --- a/components/templates/Cargo.toml +++ b/components/templates/Cargo.toml @@ -12,3 +12,4 @@ pulldown-cmark = "0" errors = { path = "../errors" } utils = { path = "../utils" } content = { path = "../content" } +config = { path = "../config" } diff --git a/components/templates/src/global_fns.rs b/components/templates/src/global_fns.rs index a945405..982966d 100644 --- a/components/templates/src/global_fns.rs +++ b/components/templates/src/global_fns.rs @@ -4,6 +4,7 @@ use std::path::{PathBuf}; use tera::{GlobalFn, Value, from_value, to_value, Result}; use content::{Page, Section}; +use config::Config; use utils::site::resolve_internal_link; @@ -51,7 +52,7 @@ pub fn make_get_section(all_sections: &HashMap) -> GlobalFn { }) } -pub fn make_get_url(permalinks: HashMap,) -> GlobalFn { +pub fn make_get_url(permalinks: HashMap) -> GlobalFn { Box::new(move |args| -> Result { match args.get("link") { Some(val) => match from_value::(val.clone()) { @@ -65,3 +66,59 @@ pub fn make_get_url(permalinks: HashMap,) -> GlobalFn { } }) } + +pub fn make_get_static_url(config: Config) -> GlobalFn { + Box::new(move |args| -> Result { + let cachebust = args + .get("cachebust") + .map_or(true, |c| { + from_value::(c.clone()).unwrap_or(true) + }); + + match args.get("path") { + Some(val) => match from_value::(val.clone()) { + Ok(v) => { + let mut permalink = config.make_permalink(&v); + if cachebust { + permalink = format!("{}?t={}", permalink, config.build_timestamp.unwrap()); + } + Ok(to_value(permalink).unwrap()) + }, + Err(_) => Err(format!("`get_static_url` received path={:?} but it requires a string", val).into()), + + }, + None => Err("`get_static_url` requires a `path` argument.".into()), + } + }) +} + +#[cfg(test)] +mod tests { + use super::make_get_static_url; + + use std::collections::HashMap; + + use tera::to_value; + + use config::Config; + + + #[test] + fn add_cachebust_to_static_url_by_default() { + let config = Config::default(); + let static_fn = make_get_static_url(config); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("app.css").unwrap()); + assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/?t=1"); + } + + #[test] + fn can_disable_cachebust_for_static_url() { + let config = Config::default(); + let static_fn = make_get_static_url(config); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("app.css").unwrap()); + args.insert("cachebust".to_string(), to_value(false).unwrap()); + assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/"); + } +} diff --git a/components/templates/src/lib.rs b/components/templates/src/lib.rs index c2b8d78..180a871 100644 --- a/components/templates/src/lib.rs +++ b/components/templates/src/lib.rs @@ -8,6 +8,7 @@ extern crate pulldown_cmark; extern crate errors; extern crate utils; extern crate content; +extern crate config; pub mod filters; pub mod global_fns;