Skip to content

Commit

Permalink
feat: Upgrade liquid
Browse files Browse the repository at this point in the history
Features
* Provide context on errors (good enough for #136)
* Implement basic `compact` support

Bug Fixes
* `date_in_tz` can't parse cobalt's dates
* Improve value coercion practices
  * Every `Value` is now treated as a string.
  * Whole numbers are now preserved.
  * Whole numbers can convert to fractional numbers but not the other way
    around.
  * You can convert a fractional number to a whole number using the
    filters `round`, `ceil`, or `floor`.
  * Operations on only whole numbers preserve their whole number nature.
  * for tag ranges, indexing, etc only accept whole numbers

Fixes #136

BREAKING CHANGES: There might be minor behavior changes form the coercion
changes.
  • Loading branch information
epage committed Jan 24, 2018
1 parent 7fe1004 commit 8fca434
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 76 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ doc = false

[dependencies]
clap = "2.24"
liquid = {version = "0.13", features=["serde"]}
liquid = {version = "0.14.1", features=["serde"]}
walkdir = "2.0"
chrono = "0.4"
log = "0.3.9"
Expand Down
10 changes: 5 additions & 5 deletions src/cobalt_model/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ impl CollectionBuilder {
}

let mut attributes: liquid::Object =
vec![("title".to_owned(), liquid::Value::str(&title)),
("slug".to_owned(), liquid::Value::str(&slug)),
vec![("title".to_owned(), liquid::Value::scalar(&title)),
("slug".to_owned(), liquid::Value::scalar(&slug)),
("description".to_owned(),
liquid::Value::Str(description.clone().unwrap_or_else(|| "".to_owned())))]
liquid::Value::scalar(description.clone().unwrap_or_else(|| "".to_owned())))]
.into_iter()
.collect();
if let Some(ref rss) = rss {
attributes.insert("rss".to_owned(), liquid::Value::str(rss));
attributes.insert("rss".to_owned(), liquid::Value::scalar(rss));
}
if let Some(ref jsonfeed) = jsonfeed {
attributes.insert("jsonfeed".to_owned(), liquid::Value::str(jsonfeed));
attributes.insert("jsonfeed".to_owned(), liquid::Value::scalar(jsonfeed));
}

let default = default.set_collection(slug.clone());
Expand Down
6 changes: 3 additions & 3 deletions src/cobalt_model/site.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ impl SiteBuilder {

let mut attributes = liquid::Object::new();
if let Some(ref title) = title {
attributes.insert("title".to_owned(), liquid::Value::str(title));
attributes.insert("title".to_owned(), liquid::Value::scalar(title));
}
if let Some(ref description) = description {
attributes.insert("description".to_owned(), liquid::Value::str(description));
attributes.insert("description".to_owned(), liquid::Value::scalar(description));
}
if let Some(ref base_url) = base_url {
attributes.insert("base_url".to_owned(), liquid::Value::str(base_url));
attributes.insert("base_url".to_owned(), liquid::Value::scalar(base_url));
}
let mut data = data.unwrap_or_default();
insert_data_dir(&mut data, &root.join(data_dir))?;
Expand Down
70 changes: 33 additions & 37 deletions src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,34 +42,37 @@ fn permalink_attributes(front: &cobalt_model::Frontmatter, dest_file: &Path) ->
let mut attributes = liquid::Object::new();

attributes.insert("parent".to_owned(),
Value::Str(format_path_variable(dest_file)));
Value::scalar(format_path_variable(dest_file)));

let filename = dest_file.file_stem().and_then(|s| s.to_str()).unwrap_or("");
attributes.insert("name".to_owned(), Value::str(filename));
attributes.insert("name".to_owned(), Value::scalar(filename));

attributes.insert("ext".to_owned(), Value::str(".html"));
attributes.insert("ext".to_owned(), Value::scalar(".html"));

// TODO(epage): Add `collection` (the collection's slug), see #257
// or `parent.slug`, see #323

attributes.insert("slug".to_owned(), Value::str(&front.slug));
attributes.insert("slug".to_owned(), Value::scalar(&front.slug));

attributes.insert("categories".to_owned(),
Value::Str(itertools::join(front.categories.iter().map(slug::slugify), "/")));
Value::scalar(itertools::join(front.categories.iter().map(slug::slugify),
"/")));

if let Some(ref date) = front.published_date {
attributes.insert("year".to_owned(), Value::Str(date.year().to_string()));
attributes.insert("year".to_owned(), Value::scalar(date.year().to_string()));
attributes.insert("month".to_owned(),
Value::Str(format!("{:02}", &date.month())));
attributes.insert("i_month".to_owned(), Value::Str(date.month().to_string()));
attributes.insert("day".to_owned(), Value::Str(format!("{:02}", &date.day())));
attributes.insert("i_day".to_owned(), Value::Str(date.day().to_string()));
Value::scalar(format!("{:02}", &date.month())));
attributes.insert("i_month".to_owned(),
Value::scalar(date.month().to_string()));
attributes.insert("day".to_owned(),
Value::scalar(format!("{:02}", &date.day())));
attributes.insert("i_day".to_owned(), Value::scalar(date.day().to_string()));
attributes.insert("hour".to_owned(),
Value::Str(format!("{:02}", &date.hour())));
Value::scalar(format!("{:02}", &date.hour())));
attributes.insert("minute".to_owned(),
Value::Str(format!("{:02}", &date.minute())));
Value::scalar(format!("{:02}", &date.minute())));
attributes.insert("second".to_owned(),
Value::Str(format!("{:02}", &date.second())));
Value::scalar(format!("{:02}", &date.second())));
}

attributes.insert("data".to_owned(), Value::Object(front.data.clone()));
Expand Down Expand Up @@ -129,35 +132,32 @@ fn document_attributes(front: &cobalt_model::Frontmatter,
source_file: &Path,
url_path: &str)
-> liquid::Object {
let categories = liquid::Value::Array(front
.categories
.iter()
.map(|c| liquid::Value::str(c))
.collect());
let categories =
liquid::Value::Array(front.categories.iter().map(liquid::Value::scalar).collect());
// Reason for `file`:
// - Allow access to assets in the original location
// - Ease linking back to page's source
let file: liquid::Object =
vec![("permalink".to_owned(), liquid::Value::str(source_file.to_str().unwrap_or(""))),
vec![("permalink".to_owned(), liquid::Value::scalar(source_file.to_str().unwrap_or(""))),
("parent".to_owned(),
liquid::Value::str(source_file.parent().and_then(Path::to_str).unwrap_or("")))]
liquid::Value::scalar(source_file.parent().and_then(Path::to_str).unwrap_or("")))]
.into_iter()
.collect();
let attributes =
vec![("permalink".to_owned(), liquid::Value::str(url_path)),
("title".to_owned(), liquid::Value::str(&front.title)),
vec![("permalink".to_owned(), liquid::Value::scalar(url_path)),
("title".to_owned(), liquid::Value::scalar(&front.title)),
("description".to_owned(),
liquid::Value::str(front.description.as_ref().map(|s| s.as_str()).unwrap_or(""))),
liquid::Value::scalar(front.description.as_ref().map(|s| s.as_str()).unwrap_or(""))),
("categories".to_owned(), categories),
("is_draft".to_owned(), liquid::Value::Bool(front.is_draft)),
("is_draft".to_owned(), liquid::Value::scalar(front.is_draft)),
("file".to_owned(), liquid::Value::Object(file)),
("collection".to_owned(), liquid::Value::str(&front.collection)),
("collection".to_owned(), liquid::Value::scalar(&front.collection)),
("data".to_owned(), liquid::Value::Object(front.data.clone()))];
let mut attributes: liquid::Object = attributes.into_iter().collect();

if let Some(ref published_date) = front.published_date {
attributes.insert("published_date".to_owned(),
liquid::Value::Str(published_date.format()));
liquid::Value::scalar(liquid::Date::from(*published_date)));
}

attributes
Expand Down Expand Up @@ -258,11 +258,9 @@ impl Document {
fn description_to_str(&self) -> Option<String> {
self.front
.description
.as_ref()
.map(|s| s.as_str())
.or_else(|| self.attributes.get("excerpt").and_then(|s| s.as_str()))
.or_else(|| self.attributes.get("content").and_then(|s| s.as_str()))
.map(|s| s.to_owned())
.clone()
.or_else(|| self.attributes.get("excerpt").map(|s| s.to_string()))
.or_else(|| self.attributes.get("content").map(|s| s.to_string()))
}

/// Renders liquid templates into HTML in the context of current document.
Expand Down Expand Up @@ -300,15 +298,15 @@ impl Document {
-> Result<()> {
let value = if let Some(excerpt_str) = self.front.excerpt.as_ref() {
let excerpt = self.render_html(excerpt_str, globals, parser, syntax_theme)?;
Value::Str(excerpt)
Value::scalar(excerpt)
} else if self.front.excerpt_separator.is_empty() {
Value::Nil
} else {
let excerpt = extract_excerpt(&self.content,
self.front.format,
&self.front.excerpt_separator);
let excerpt = self.render_html(&excerpt, globals, parser, syntax_theme)?;
Value::Str(excerpt)
Value::scalar(excerpt)
};

self.attributes.insert("excerpt".to_owned(), value);
Expand All @@ -325,7 +323,7 @@ impl Document {
-> Result<()> {
let content_html = self.render_html(&self.content, globals, parser, syntax_theme)?;
self.attributes
.insert("content".to_owned(), Value::Str(content_html.clone()));
.insert("content".to_owned(), Value::scalar(content_html.clone()));
Ok(())
}

Expand Down Expand Up @@ -368,9 +366,7 @@ impl Document {
.ok_or("Internal error: page isn't in globals")?
.get(&liquid::Index::with_key("content"))
.ok_or("Internal error: page.content isn't in globals")?
.as_str()
.ok_or("Internal error: bad content format")?
.to_owned();
.to_string();

Ok(content_html)
}
Expand Down
21 changes: 10 additions & 11 deletions src/jekyll_model/frontmatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,31 +41,30 @@ impl From<FrontmatterBuilder> for cobalt_model::FrontmatterBuilder {
// Convert jekyll frontmatter into frontmatter (with `custom`)
let mut unprocessed_attributes = jk_front.0;
cobalt_model::FrontmatterBuilder::new()
.merge_slug(unprocessed_attributes
.remove("slug")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.merge_slug(unprocessed_attributes.remove("slug").map(|v| v.to_string()))
.merge_title(unprocessed_attributes
.remove("title")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.map(|v| v.to_string()))
.merge_description(unprocessed_attributes
.remove("excerpt")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.map(|v| v.to_string()))
.merge_categories(unprocessed_attributes.remove("categories").and_then(|v| {
v.as_array()
.map(|v| v.iter().map(|v| v.to_string()).collect())
}))
.merge_permalink(unprocessed_attributes
.remove("permalink")
.and_then(|v| v.as_str().map(convert_permalink)))
.map(|v| convert_permalink(v.to_str().as_ref())))
.merge_draft(unprocessed_attributes
.remove("published")
.and_then(|v| v.as_bool().map(|b| !b)))
.and_then(|v| v.as_scalar().and_then(|v| v.to_bool())))
.merge_layout(unprocessed_attributes
.remove("layout")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.merge_published_date(unprocessed_attributes.remove("date").and_then(|d| {
d.as_str().and_then(cobalt_model::DateTime::parse)
}))
.map(|v| v.to_string()))
.merge_published_date(unprocessed_attributes
.remove("date")
.and_then(|d| d.as_scalar().and_then(|d| d.to_date()))
.map(|d| d.into()))
.merge_data(unprocessed_attributes)
}
}
Expand Down
26 changes: 12 additions & 14 deletions src/legacy_model/frontmatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::fmt;
use liquid;

use cobalt_model;
use super::DateTime;
use super::Permalink;
use super::Part;
use super::VARIABLES;
Expand Down Expand Up @@ -39,35 +38,34 @@ impl From<FrontmatterBuilder> for cobalt_model::FrontmatterBuilder {
cobalt_model::FrontmatterBuilder::new()
.merge_title(unprocessed_attributes
.remove("title")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.map(|v| v.to_string()))
.merge_description(unprocessed_attributes
.remove("description")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.map(|v| v.to_string()))
.merge_excerpt(unprocessed_attributes
.remove("excerpt")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.map(|v| v.to_string()))
.merge_categories(unprocessed_attributes.remove("categories").and_then(|v| {
v.as_array()
.map(|v| v.iter().map(|v| v.to_string()).collect())
}))
.merge_slug(unprocessed_attributes
.remove("slug")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.merge_slug(unprocessed_attributes.remove("slug").map(|v| v.to_string()))
.merge_permalink(unprocessed_attributes
.remove("path")
.and_then(|v| v.as_str().map(convert_permalink)))
.map(|v| convert_permalink(v.to_str().as_ref())))
.merge_draft(unprocessed_attributes
.remove("draft")
.and_then(|v| v.as_bool()))
.and_then(|v| v.as_scalar().and_then(|v| v.to_bool())))
.merge_excerpt_separator(unprocessed_attributes
.remove("excerpt_separator")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.map(|v| v.to_string()))
.merge_layout(unprocessed_attributes
.remove("extends")
.and_then(|v| v.as_str().map(|s| s.to_owned())))
.merge_published_date(unprocessed_attributes.remove("date").and_then(|d| {
d.as_str().and_then(DateTime::parse).map(|d| d.into())
}))
.map(|v| v.to_string()))
.merge_published_date(unprocessed_attributes
.remove("date")
.and_then(|d| d.as_scalar().and_then(|d| d.to_date()))
.map(|d| d.into()))
.merge_data(unprocessed_attributes)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl liquid::compiler::Include for InMemoryInclude {
.unwrap_or_else(|| {
let content = self.legacy
.as_ref()
.ok_or_else(|| liquid::Error::from(&*format!("{:?} does not exist", path)))?
.ok_or_else(|| liquid::Error::with_msg("No legacy path specified"))?
.include(path)?;
warn!("Loading `include`s relative to `source` is deprecated, see {}.",
path);
Expand Down
2 changes: 1 addition & 1 deletion tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ pub fn liquid_error() {
let err = run_test("liquid_error");
assert!(err.is_err());
assert_contains!(format!("{}", err.unwrap_err().display_chain()),
"{{{ is not a valid identifier");
"Invalid identifier");
}

#[test]
Expand Down

0 comments on commit 8fca434

Please sign in to comment.