EDIT: This thread started as a proposition for some new template functions but has morphed into a discussion of python templates. Most of what is said in this first post is no longer being considered.
------
Several times over the last years I have wanted to have 'real' python-like dictionaries in the template language. By that I mean a variable that holds "key:value" pairs, where both key and value are arbitrary strings. I am thinking about adding this support. Before I spend the time, I am asking "Would anyone else use this?"
The problem: how to do it that makes sense in the context of the template language. What I am considering: a set of "dict_...()" functions as follows. In all cases the parameters are strings. The function explanation code uses python syntax.
- dict_set(dict_name, dict_key, dict_value): Set the key to the value in dict.
Code:
dict_name.set(dict_key, dict_value)
- dict_get(dict_name, dict_key[, default]): return the value from the dict. If default is not provided then the funtion returns the empty string if the key doesn't exist in the dict.
Code:
dict_name.get(dict_key, default)
- dict_keys(dict_name, sep): return a list of keys separated by sep.
Code:
sep.join(dict_name.keys())
- dict_values(dict_name, sep): return a list of values separated by sep.
Code:
sep.join(dict_name.values())
- dict_to_string(dict_name): return the dict JSON encoded. This lets you put the dict into template variables, for example globals or arguments.
Code:
json.dumps(dict_name)
- dict_from_string(dict_name, string_form_of_dict): convert a JSON encoded dict into a template dict.
Code:
dict_name = json.loads(string_form_of_var)
Example: a template search for series with more than one author. The template (that won't work because the dict_() functions don't exist):
Code:
program:
# Get the already-computed dict if it exists
d = globals(series_authors);
if d then
dict_from_string('series_authors', d)
else
# Compute the dict the first time through and save it as a global
series = book_values('series', 'series:True', ':::', 0);
for s in series separator ':::':
authors = book_values('authors', 'series:"""=' & a & '"""', '&', 0);
for a in authors separator '&':
dict_set('series_authors', s, list_union(dict_get('series_authors'), a, '&'))
rof
rof;
set_globals(series_authors=dict_to_string('series_authors)
fi;
# At this point we have a dict keyed by series containing a list of authors of that series.
# Check if the current book has a series, and if so does it have more than one author.
# Return the series name if it does, '' if it doesn't.
authors = dict_get('series_authors', $series)
if list_count(authors, '&') ># 1 then
return $series
else
return ''
fi
Here is a template search that would use the template. It would select books with a series that has more than one author. The authors can be co-authors or single authors of separate books in the same series.
You might ask "How does this differ from using select() on identfier-like strings? Some of the differences:
- Both keys and values are arbitrary strings. There is no assumption about format or content. This avoids the problem that keys and values in identifiers are always separated by a colon.
- The value in a dict can itself be a (json encoded) dict, allowing nesting.
- Performance is much better because the template processer will use real python dicts without converting to/from strings.