@@ -6,6 +6,7 @@ | |||||
root page | root page | ||||
- Make `title` in config optional | - Make `title` in config optional | ||||
- Improved `gutenberg init` UX and users first experience | - 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) | ## 0.1.1 (2017-07-16) | ||||
@@ -233,6 +233,7 @@ dependencies = [ | |||||
name = "config" | name = "config" | ||||
version = "0.1.0" | version = "0.1.0" | ||||
dependencies = [ | dependencies = [ | ||||
"chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"errors 0.1.0", | "errors 0.1.0", | ||||
"rendering 0.1.0", | "rendering 0.1.0", | ||||
"serde 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -1144,6 +1145,7 @@ name = "templates" | |||||
version = "0.1.0" | version = "0.1.0" | ||||
dependencies = [ | dependencies = [ | ||||
"base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", | "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"config 0.1.0", | |||||
"content 0.1.0", | "content 0.1.0", | ||||
"errors 0.1.0", | "errors 0.1.0", | ||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -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 }}` | 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. | 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` | #### `get_page` | ||||
Takes a path to a `.md` file and returns the associated 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") %} | {% 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 | ### Static files | ||||
Everything in the `static` folder will be copied into the output directory as-is. | Everything in the `static` folder will be copied into the output directory as-is. | ||||
@@ -7,6 +7,7 @@ authors = ["Vincent Prouillet <vincent@wearewizards.io>"] | |||||
toml = "0.4" | toml = "0.4" | ||||
serde = "1.0" | serde = "1.0" | ||||
serde_derive = "1.0" | serde_derive = "1.0" | ||||
chrono = "0.4" | |||||
errors = { path = "../errors" } | errors = { path = "../errors" } | ||||
rendering = { path = "../rendering" } | rendering = { path = "../rendering" } |
@@ -4,6 +4,7 @@ extern crate toml; | |||||
#[macro_use] | #[macro_use] | ||||
extern crate errors; | extern crate errors; | ||||
extern crate rendering; | extern crate rendering; | ||||
extern crate chrono; | |||||
use std::collections::HashMap; | use std::collections::HashMap; | ||||
use std::fs::File; | use std::fs::File; | ||||
@@ -11,12 +12,13 @@ use std::io::prelude::*; | |||||
use std::path::Path; | use std::path::Path; | ||||
use toml::{Value as Toml}; | use toml::{Value as Toml}; | ||||
use chrono::Utc; | |||||
use errors::{Result, ResultExt}; | use errors::{Result, ResultExt}; | ||||
use rendering::highlighting::THEME_SET; | use rendering::highlighting::THEME_SET; | ||||
#[derive(Debug, PartialEq, Serialize, Deserialize)] | |||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | |||||
pub struct Config { | pub struct Config { | ||||
/// Base URL of the site, the only required config argument | /// Base URL of the site, the only required config argument | ||||
pub base_url: String, | pub base_url: String, | ||||
@@ -48,6 +50,9 @@ pub struct Config { | |||||
/// All user params set in [extra] in the config | /// All user params set in [extra] in the config | ||||
pub extra: Option<HashMap<String, Toml>>, | pub extra: Option<HashMap<String, Toml>>, | ||||
/// Set automatically when instantiating the config. Used for cachebusting | |||||
pub build_timestamp: Option<i64>, | |||||
} | } | ||||
macro_rules! set_default { | macro_rules! set_default { | ||||
@@ -85,6 +90,7 @@ impl Config { | |||||
None => config.highlight_theme = Some("base16-ocean-dark".to_string()) | None => config.highlight_theme = Some("base16-ocean-dark".to_string()) | ||||
}; | }; | ||||
config.build_timestamp = Some(Utc::now().timestamp()); | |||||
Ok(config) | Ok(config) | ||||
} | } | ||||
@@ -136,6 +142,7 @@ impl Default for Config { | |||||
insert_anchor_links: Some(false), | insert_anchor_links: Some(false), | ||||
compile_sass: Some(false), | compile_sass: Some(false), | ||||
extra: None, | extra: None, | ||||
build_timestamp: Some(1), | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -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_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_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(); | self.register_get_url_fn(); | ||||
Ok(()) | Ok(()) | ||||
@@ -12,3 +12,4 @@ pulldown-cmark = "0" | |||||
errors = { path = "../errors" } | errors = { path = "../errors" } | ||||
utils = { path = "../utils" } | utils = { path = "../utils" } | ||||
content = { path = "../content" } | content = { path = "../content" } | ||||
config = { path = "../config" } |
@@ -4,6 +4,7 @@ use std::path::{PathBuf}; | |||||
use tera::{GlobalFn, Value, from_value, to_value, Result}; | use tera::{GlobalFn, Value, from_value, to_value, Result}; | ||||
use content::{Page, Section}; | use content::{Page, Section}; | ||||
use config::Config; | |||||
use utils::site::resolve_internal_link; | use utils::site::resolve_internal_link; | ||||
@@ -51,7 +52,7 @@ pub fn make_get_section(all_sections: &HashMap<PathBuf, Section>) -> GlobalFn { | |||||
}) | }) | ||||
} | } | ||||
pub fn make_get_url(permalinks: HashMap<String, String>,) -> GlobalFn { | |||||
pub fn make_get_url(permalinks: HashMap<String, String>) -> GlobalFn { | |||||
Box::new(move |args| -> Result<Value> { | Box::new(move |args| -> Result<Value> { | ||||
match args.get("link") { | match args.get("link") { | ||||
Some(val) => match from_value::<String>(val.clone()) { | Some(val) => match from_value::<String>(val.clone()) { | ||||
@@ -65,3 +66,59 @@ pub fn make_get_url(permalinks: HashMap<String, String>,) -> GlobalFn { | |||||
} | } | ||||
}) | }) | ||||
} | } | ||||
pub fn make_get_static_url(config: Config) -> GlobalFn { | |||||
Box::new(move |args| -> Result<Value> { | |||||
let cachebust = args | |||||
.get("cachebust") | |||||
.map_or(true, |c| { | |||||
from_value::<bool>(c.clone()).unwrap_or(true) | |||||
}); | |||||
match args.get("path") { | |||||
Some(val) => match from_value::<String>(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/"); | |||||
} | |||||
} |
@@ -8,6 +8,7 @@ extern crate pulldown_cmark; | |||||
extern crate errors; | extern crate errors; | ||||
extern crate utils; | extern crate utils; | ||||
extern crate content; | extern crate content; | ||||
extern crate config; | |||||
pub mod filters; | pub mod filters; | ||||
pub mod global_fns; | pub mod global_fns; | ||||