Skip to content

Commit

Permalink
Update default tag to work for any level of object
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Bottriell <[email protected]>
  • Loading branch information
rydrman committed Oct 8, 2022
1 parent 98de95c commit 97fb39c
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 43 deletions.
2 changes: 1 addition & 1 deletion crates/spk-schema/crates/liquid/src/error_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn test_error_position_extraction() {
crate::render_template(TPL, &json!({})).expect_err("expected template render to fail");
let expected = r#"
1 | {% default = data | replace ''%}
| ^ unexpected "="; expected Identifier
| ^ unexpected "="; expected Variable
"#;
let message = err.to_string();
assert_eq!(message, expected);
Expand Down
71 changes: 62 additions & 9 deletions crates/spk-schema/crates/liquid/src/tag_default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@
// SPDX-License-Identifier: Apache-2.0
// https://github.com/imageworks/spk

use liquid::{Object, ValueView};
use liquid_core::error::ResultLiquidExt;
use liquid_core::parser::FilterChain;
use liquid_core::{Language, ParseTag, Renderable, Result, Runtime, TagReflection, TagTokenIter};
use liquid_core::runtime::Variable;
use liquid_core::{
Language,
ParseTag,
Renderable,
Result,
Runtime,
TagReflection,
TagTokenIter,
Value,
};

#[cfg(test)]
#[path = "./tag_default_test.rs"]
Expand Down Expand Up @@ -37,10 +48,8 @@ impl ParseTag for DefaultTag {
) -> Result<Box<dyn Renderable>> {
let dst = arguments
.expect_next("Identifier expected.")?
.expect_identifier()
.into_result()?
.to_string()
.into();
.expect_variable()
.into_result()?;

arguments
.expect_next("Assignment operator \"=\" expected.")?
Expand All @@ -65,7 +74,7 @@ impl ParseTag for DefaultTag {

#[derive(Debug)]
struct Default {
dst: liquid_core::model::KString,
dst: Variable,
src: FilterChain,
}

Expand All @@ -83,10 +92,54 @@ impl Renderable for Default {
.trace_with(|| self.trace().into())?
.into_owned();

let name = self.dst.as_str().into();
if runtime.try_get(&[name]).is_none() {
runtime.set_global(self.dst.clone(), value);
let dst = self.dst.evaluate(runtime)?;
let (base_name, sub_path) = dst
.split_first()
.expect("Path guarantees at least one entry");
let mut existing = runtime
.try_get(&[base_name.clone()])
.map(|v| v.to_value())
.unwrap_or(Value::Nil);
let mut current = &mut existing;
for key in sub_path.iter() {
match current {
Value::Nil => {
*current = Value::Object(Object::new());
current = current
.as_object_mut()
.expect("current was just assigned as an object")
.entry(key.to_kstr())
.or_insert(Value::Nil);
}
Value::Scalar(_) => {
return Err(liquid::Error::with_msg(
"cannot index into scalar for default assignment",
)
.trace(format!("trying to traverse into {key:?}")))
}
Value::Array(_) => {
return Err(liquid::Error::with_msg(
"cannot index into array for default assignment",
)
.trace(format!("trying to traverse into {key:?}")))
}
Value::Object(obj) => {
current = obj.entry(key.to_kstr()).or_insert(Value::Nil);
}
Value::State(_) => {
return Err(liquid::Error::with_msg(
"cannot index into state for default assignment",
)
.trace(format!("trying to traverse into {key:?}")))
}
}
}
if current.is_nil() {
tracing::debug!("setting template default for: {dst} = {value:?}");
*current = value;
}
// put back the extracted value that has now been modified
runtime.set_global(base_name.to_kstr().into(), existing);
Ok(())
}
}
30 changes: 30 additions & 0 deletions crates/spk-schema/crates/liquid/src/tag_default_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,33 @@ sources:
crate::render_template(TPL, &options).expect("template should not fail to render");
assert_eq!(rendered, EXPECTED);
}

#[rstest]
fn test_template_rendering_defaults_depth() {
// ensure that defaults can be set at any
// level without interfering

let options = json!({
"one": {
"a": 10,
}
});
static TPL: &str = r#"
{% default two = 2 -%}
{% default one.b = 20 -%}
{% default other.a.b = 50 -%}
{{ two }}
{{ one.a }}
{{ one.b }}
{{ other.a.b }}
"#;
static EXPECTED: &str = r#"
2
10
20
50
"#;
let rendered =
crate::render_template(TPL, &options).expect("template should not fail to render");
assert_eq!(rendered, EXPECTED);
}
2 changes: 1 addition & 1 deletion crates/spk-schema/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub use spk_schema_foundation::{
FromYaml,
};
pub use spk_schema_ident::{self as ident, Ident};
pub use template::{Template, TemplateExt, TemplateData};
pub use template::{Template, TemplateData, TemplateExt};
pub use test_spec::TestStage;
pub use validation::{default_validators, ValidationSpec, Validator};
pub use {serde_json, spk_schema_validators as validators};
Expand Down
5 changes: 2 additions & 3 deletions crates/spk-schema/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ pub trait TemplateExt: Template {
fn from_file(path: &Path) -> Result<Self>;
}


/// The structured data that should be made available
/// when rendering spk templates into recipes
#[derive(serde::Serialize, Debug, Clone)]
Expand All @@ -52,7 +51,7 @@ struct SpkInfo {
impl Default for SpkInfo {
fn default() -> Self {
Self {
version: env!("CARGO_PKG_VERSION")
version: env!("CARGO_PKG_VERSION"),
}
}
}
Expand All @@ -63,7 +62,7 @@ impl TemplateData {
TemplateData {
spk: SpkInfo::default(),
opt: options.to_yaml_value_expanded(),
env: std::env::vars().into_iter().collect()
env: std::env::vars().into_iter().collect(),
}
}
}
18 changes: 9 additions & 9 deletions docs/use/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ One common templating use case is to allow your package spec to be reused to bui

```yaml
# {% default opt.version = "2.3.4" %}
pkg: my-package/{{ version }}
pkg: my-package/{{ opt.version }}
```

Which could then be invoked for different versions at the command line:
Expand All @@ -453,11 +453,11 @@ In addition to the default tags and filters within the liquid language, spk prov

**default**

The `default` tag can be used to more easily declare the default value for a variable. The following two statements are equivalent:
The `default` tag can be used to more easily declare the default value for a variable at any level of the template data structure. Most commonly, this can be used to declare default option or environment variable values so that they are not required to be given by the developer.

```liquid
{% assign version = version | default: "2.3.4" %}
{% default version = "2.3.4" %}
{% default opt.version = "2.3.4" %}
{% default env.PWD = "/tmp" %}
```

##### Filters
Expand All @@ -467,10 +467,10 @@ The `default` tag can be used to more easily declare the default value for a var
The `compare_version` allows for comparing spk versions using any of the [version comparison operators](/use/versioning). It takes one or two parameters, depending on the data that you have to give. In all cases, the parameters are concatenated together and parsed as a version range. For example, the following assignments to py_3 all end up checking the same statement.

```liquid
{% assign is_py3 = python.version | compare_version: ">=3" %}
{% assign is_py3 = python.version | compare_version: ">=", 3 %}
{% assign is_py3 = opt.python.version | compare_version: ">=3" %}
{% assign is_py3 = opt.python.version | compare_version: ">=", 3 %}
{% assign three = 3 %}
{% assign is_py3 = python.version | compare_version: ">=", three %}
{% assign is_py3 = opt.python.version | compare_version: ">=", three %}
```

**parse_version**
Expand All @@ -494,7 +494,7 @@ The `parse_version` filter breaks down an spk version into it's components, eith
The `replace_re` filter works like the built-in `replace` filter, except that it matches using a perl-style regular expression and allows group replacement in the output. These regular expressions do not support look-arounds or back-references. For example:

```liquid
{% default version = "2.3.4" %}
{% assign major_minor = version | replace_re: "(\d+)\.(\d+).*", "$1.$2" %}
{% default opt.version = "2.3.4" %}
{% assign major_minor = opt.version | replace_re: "(\d+)\.(\d+).*", "$1.$2" %}
{{ major_minor }} # 2.3
```
8 changes: 4 additions & 4 deletions packages/cmake/cmake.spk.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# {% assign version = opt.version | default: "3.16.4" %}
pkg: cmake/{{ version }}
# {% default opt.version = "3.16.4" %}
pkg: cmake/{{ opt.version }}
api: v0/package

sources:
Expand All @@ -8,8 +8,8 @@ sources:
# the tar file.
- path: ./
- script:
- export TARFILE=cmake-{{ version }}-Linux-x86_64.tar.gz
- if [ ! -e ./$TARFILE ] ; then wget https://github.com/Kitware/CMake/releases/download/v{{ version }}/$TARFILE ; fi
- export TARFILE=cmake-{{ opt.version }}-Linux-x86_64.tar.gz
- if [ ! -e ./$TARFILE ] ; then wget https://github.com/Kitware/CMake/releases/download/v{{ opt.version }}/$TARFILE ; fi

build:
options:
Expand Down
12 changes: 6 additions & 6 deletions packages/gnu/gcc/gcc48.spk.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# {{ assign version = opt.version | default: "4.8.5" }}
pkg: gcc/{{ version }}
# {{ default opt.version = "4.8.5" }}
pkg: gcc/{{ opt.version }}
api: v0/package

sources:
- tar: http://ftpmirror.gnu.org/gnu/gcc/gcc-{{ version }}/gcc-{{ version }}.tar.gz
- tar: http://ftpmirror.gnu.org/gnu/gcc/gcc-{{ opt.version }}/gcc-{{ opt.version }}.tar.gz
- path: patch-gcc46-texi.diff

build:
Expand All @@ -25,10 +25,10 @@ build:
- pkg: coreutils
- pkg: binutils
- pkg: make
- pkg: gcc/<={{ version }}
- pkg: gcc/<={{ opt.version }}
script:
- patch -d gcc-{{ version }} -p0 <patch-gcc46-texi.diff
- cd gcc-{{ version }}
- patch -d gcc-{{ opt.version }} -p0 <patch-gcc46-texi.diff
- cd gcc-{{ opt.version }}
- ./configure
--prefix=/spfs
--with-mpfr=/spfs
Expand Down
10 changes: 5 additions & 5 deletions packages/gnu/gcc/gcc63.spk.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# {% assign version = opt.version | default: "6.3.3" %}
pkg: gcc/{{ version }}
# {% default opt.version = "6.3.3" %}
pkg: gcc/{{ opt.version }}
api: v0/package

sources:
- tar: http://ftpmirror.gnu.org/gnu/gcc/gcc-{{ version }}/gcc-{{ version }}.tar.gz
- tar: http://ftpmirror.gnu.org/gnu/gcc/gcc-{{ opt.version }}/gcc-{{ opt.version }}.tar.gz

build:
options:
Expand All @@ -23,9 +23,9 @@ build:
- pkg: coreutils
- pkg: make
- pkg: zip
- pkg: gcc/<={{ version }}
- pkg: gcc/<={{ opt.version }}
script:
- cd gcc-{{ version }}
- cd gcc-{{ opt.version }}
- ./configure
--prefix=/spfs
--with-mpfr=/spfs
Expand Down
7 changes: 3 additions & 4 deletions packages/python/python2.spk.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# {% assign version = env.VV | default: "2.7.5" %}
# {% assign version = opt.version | default: "2.7.5" %}
# {% default opt.version = "2.7.5" %}
# {% assign cpXX = opt.version | replace_re: "(\d+)\.(\d+).*", "cp$1$2" %}
pkg: python/{{ version }}
pkg: python/{{ opt.version }}
api: v0/package
sources:
- git: https://github.com/python/cpython
ref: v{{ version }}
ref: v{{ opt.version }}
build:
options:
- var: os
Expand Down
2 changes: 1 addition & 1 deletion packages/python/python3.spk.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# {% assign version = opt.version | default: "3.7.3" %}
# {% default opt.version = "3.7.3" %}
# {% assign cpXX = version | replace_re: "(\d+)\.(\d+).*", "cp$1$2" %}
pkg: python/{{ version }}
api: v0/package
Expand Down

0 comments on commit 97fb39c

Please sign in to comment.