From e809c0be6b906ea93114c9aa4f393d8d7a1d3012 Mon Sep 17 00:00:00 2001 From: sdomi Date: Sun, 18 May 2025 14:14:00 +0200 Subject: [PATCH 1/3] template: implement relative paths. bump to 0.97.1 --- src/template.sh | 50 +++++++++++++++++++++++++++++++++++++++++-------- src/version.sh | 2 +- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/template.sh b/src/template.sh index 61a0b5b..0a34a50 100755 --- a/src/template.sh +++ b/src/template.sh @@ -6,11 +6,18 @@ function render() { local _tpl_newline=$'\01' local _tpl_ctrl=$'\02' + local tplfile + + _template_find_absolute_path "$2" + + if [[ ! "$tplfile" ]]; then + exit 1 # fail hard + fi if [[ "$3" != true ]]; then - local template="$(tr -d "${_tpl_newline}${_tpl_ctrl}" < "$2" | sed 's/\&/�UwU�/g')" + local template="$(tr -d "${_tpl_newline}${_tpl_ctrl}" < "$tplfile" | sed 's/\&/�UwU�/g')" else - local template="$(tr -d "${_tpl_ctrl}" < "$2" | sed -E 's/\\/\\\\/g')" + local template="$(tr -d "${_tpl_ctrl}" < "$tplfile" | sed -E 's/\\/\\\\/g')" fi local buf= local garbage="$template"$'\n' @@ -24,14 +31,19 @@ function render() { # below check prevents the loop loading itself as a template. # this is possibly not enough to prevent all recursions, but # i see it as a last-ditch measure. so it'll do here. - if [[ "$file" == "$2" ]]; then + if [[ "$file" == "$tplfile" ]]; then subtemplate+="s${_tpl_ctrl}\{\{\#$key\}\}${_tpl_ctrl}I cowardly refuse to endlessly recurse\!${_tpl_ctrl}g;" - elif [[ -f "$key" ]]; then - local input="$(tr -d "${_tpl_ctrl}${_tpl_newline}" < "$key" | sed 's/\&/�UwU�/g')" + # elif [[ -f "$key" ]]; then + else + local i + local IFS='' + + _template_find_absolute_path "$key" + local input="$(tr -d "${_tpl_ctrl}${_tpl_newline}" < "$tplfile" | sed 's/\&/�UwU�/g')" garbage+="$input"$'\n' input="$(tr $'\n' "${_tpl_newline}" <<< "$input")" # for another hack subtemplate+="s${_tpl_ctrl}\{\{\#$key\}\}${_tpl_ctrl}${input}${_tpl_ctrl};" - _template_find_special_uri "$(cat "$key")" + _template_find_special_uri "$(cat "$tplfile")" fi done <<< "$(grep -Poh '{{#\K(.*?)(?=}})' <<< "$template")" @@ -118,6 +130,27 @@ function render() { [[ "$3" != true ]] && _template_uri_list=() } +# internal function that looks for the current template. uses path relative to +# the namespace, unless overriden with ${template_relative_paths[@]} +# +# - /dev/stdin is a special value, which gets passed literally. +# - /dev/fd/* allows file substitutions `<(echo ...)` to be used. +# +# _template_find_absolute_path(name) -> $tplfile +_template_find_absolute_path() { + if [[ ! "${template_relative_paths}" || "$1" == /dev/stdin || "$1" == "/dev/fd/"* ]]; then + tplfile="$1" + else + for (( i=0; i<${#template_relative_paths[@]}; i++ )); do + if [[ -f "${template_relative_paths[i]}/$1" ]]; then + tplfile="${template_relative_paths[i]}/$1" + break + fi + done + fi + +} + _template_uri_list=() # internal function that finds all occurences of the special `{{-uri-N}}` tag. # here to also make it run on subtemplates @@ -168,8 +201,9 @@ function nested_add() { ref+=("$nested_id") } -# nested_get(ref, i) +# nested_get(ref, i, [res]) function nested_get() { local -n ref=$1 - declare -g -n res=_${ref["$2"]} + local name=${3:-res} + declare -g -n $name=_${ref["$2"]} } diff --git a/src/version.sh b/src/version.sh index fb5aaae..fd70d1b 100644 --- a/src/version.sh +++ b/src/version.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -HTTPSH_VERSION=0.97 +HTTPSH_VERSION=0.97.1 From e9018284f57e1db985ab839babeec41b2eb09220 Mon Sep 17 00:00:00 2001 From: sdomi Date: Sun, 18 May 2025 14:14:18 +0200 Subject: [PATCH 2/3] docs: upgrade template docs for 0.97.1 --- docs/template-examples.md | 11 +++++++---- docs/template.md | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/docs/template-examples.md b/docs/template-examples.md index 4b2e3d7..10ab8fb 100644 --- a/docs/template-examples.md +++ b/docs/template-examples.md @@ -10,7 +10,7 @@ declare -A str str[title]="Hello, world!" str[test]="meow" -render str "${cfg[namespace]}/templates/main.htm" +render str "templates/main.htm" ``` `render` is the core of the templating engine; it takes an assoc array, iterates over it, applies @@ -18,7 +18,10 @@ additional magic and outputs the response directly to stdout. It is likely the f to run in your script. The script above has referenced an HTML file; For this example, we put it under -`app/templates/main.htm`, but you're free to use any directory structure for this. +`app/templates/main.htm`, but you're free to use any directory structure for this. An observant +reader might have noticed the relative path; All paths are treated as relative to the namespace's +directory. This behavior can be modified by setting `template_relative_paths`, which is described +in greater detail by the [main template documentation](./template.md). ``` @@ -100,7 +103,7 @@ done # once we have a full list of elements, assign it to the array passed to render str[_list]=list -render str "${cfg[namespace]}/templates/main.htm" +render str "templates/main.htm" ``` And the template... @@ -182,7 +185,7 @@ declare -A str str[title]="time pretty-print" str[+time]="$EPOCHSECONDS" -render str "${cfg[namespace]}/templates/main.htm" +render str "templates/main.htm" ``` ![netscape displays the current date and time](https://f.sakamoto.pl/IwIvf3Axw.png) diff --git a/docs/template.md b/docs/template.md index 1f775ac..4abfa27 100644 --- a/docs/template.md +++ b/docs/template.md @@ -23,8 +23,34 @@ For practical examples, see the [template examples](template-examples.md) page. `render [recurse]` The first param points to an associative array containing the replacement data. Second one points -to a file containing the template itself. Third is optional, and controls whether `render` will -recurse or not. This is mostly used internally, you likely won't ever need to set it. +to a file containing the template itself (see section below for special usage). Third is optional, +and controls whether `render` will recurse or not (this is mostly used internally, you likely +won't ever need to set it). + +### File paths + +Starting with HTTP.sh 0.97.1 (2025-05-18), paths in calls to `render` and include tags are +relative to the namespace directory (usually `app/`). This behavior can be changed by defining +an array called `template_relative_paths`: + +``` +template_relative_paths=( + "${cfg[namespace]}/templates/neue_theme/" + "${cfg[namespace]}/templates/default/" +) +``` + +The templating engine will check all the files in order and pick the first one that exists. This +can be used to implement basic inheritance. + +Before 0.97.1, paths were relative to the main HTTP.sh directory. Take care when upgrading, either +fix the paths, or set `template_relative_paths="./"` to emulate the previous behavior. + +### Inline templates + +For some purposes, it may be beneficial to store the template within the script file itself. +This can be done either through passing `/dev/stdin` as a file name, or through inlining +a file substitution, such as `<(echo ...)`. ## Simple replace @@ -58,9 +84,8 @@ output (for filling out hidden form values, etc.) Template includes are special, in that you don't have to define them in the array. They get processed first to "glue together" one singular template. -Currently, the path starts at the root of HTTPsh's directory. We don't support expanding variables -inside the include tag, so for now you'll need to hardcode `{{#app/templates/...}}`. This will -likely get changed in a future release, starting the path in your namespace. +The path starts at the root of your namespace (usually `app/`). This behavior can be changed, +see section "File paths" above. **Warning**: No recursion is supported within included templates; This means that you can't have an "include chain". Furthermore, some interactions between included templates and loops/ifs are From c6311cf4c11c32b884fae18e66f4fdd3c23d3377 Mon Sep 17 00:00:00 2001 From: sdomi Date: Sun, 18 May 2025 16:44:37 +0200 Subject: [PATCH 3/3] tests: add testcases for v0.97.1 template extensions --- tests/02-template.sh | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/02-template.sh b/tests/02-template.sh index a87a9fa..487a1aa 100644 --- a/tests/02-template.sh +++ b/tests/02-template.sh @@ -51,6 +51,45 @@ tpl_date_invalid() { match="value: 1970-01-01 01:00:00" } +tpl_path_custom() { + prepare() { + declare -ga template_relative_paths=("/tmp/") + + tempfile="$(mktemp)" || return 1 + } + + tst() { + declare -A meow + render meow "$(basename "$tempfile")" + } +} + +tpl_path_inheritance() { + prepare() { + tempdir="$(mktemp -d)" || return 1 + declare -ga template_relative_paths=( + "$tempdir" + "/tmp/" + ) + } +} + +tpl_path_include() { + prepare() { + another_tempfile="$(mktemp)" + echo "meow?" > "$another_tempfile" + echo "{{#$(basename "$another_tempfile")}}" > "$tempfile" + } + + match="meow?" + + cleanup() { + rm -R "$tempdir" + rm "$tempfile" "$another_tempfile" + } +} + + subtest_list=( tpl_basic tpl_basic_specialchars @@ -59,4 +98,8 @@ subtest_list=( tpl_date tpl_date_empty tpl_date_invalid + + tpl_path_custom + tpl_path_inheritance + tpl_path_include )