Joshua Morey | 2014-03-27

Filter Chaining

Not long after my last post, I updated the Skhema graph generation to support filter chaining. Functions and variables are now implemented as a new type of evaluation token with a stack of filters, using a shared syntax. The following examples show basic function usage.

{%cycle[white,LightGray]}
{%format-url[post]}

The cycle function is one of the built-in functions that I described previously. The only change is that the syntax has been tweaked to use bracketed options. The format-url function is one of the user-defined functions used by Bramble to generate a URL from a data context. In this case the post option requires that context has both a 'time' and 'slug' variable which can be transformed into a URL such as /2014/01/skhema-filters. It's somewhat opaque this way, since just looking at the template does not necessarily provide enough information to know which variables the model needs to provide, but I think that encapsulating this functionality in user-defined functions is better in the long run.

{$content:subvert[code,root]}
{$time:format-date}
{%first[list/time]:format-date[atom]}

The filter syntax has not changed, but I updated the implementation so that filters can be stacked on variables, functions, and other filters. Bramble has enough functionality at this point to give me an assurance that the current approach should work well.

Joshua Morey | 2014-01-27

Skhema Filters

I finally decided on a syntax, so Skhema now has filter support. As a result, I have updated Bramble by moving the markdown parser references from models to views (where they always belonged). Skhema templates can now use filters on variables like {$var:filter[key1,key2=val]}, where brackets contain the optional filter parameters. This is shown in the following template that Bramble uses during the rendering of this post.

{@PageSection}
<div class="article">
<div class="title">
<h2><a href="{$url}">{$title}</a></h2>
</div>
<div class="post">
<div class="entry">
{$content:subvert[code,root,header=3]}
</div>
</div>
</div>
{/@}

The subvert filter handler needs to be registered with the TemplateManager, and any applicable filter options are the responsibility of the filter handler. From this example template, the Subvert renderer enables code formatting, sets a root URL for relative URL handling, and sets the current header level at h3 (because the page design already uses h1 and h2).

TemplateManager::RegisterFilter('subvert', function($var, $filter_options, $context) {
// handle options
// ...
return Subvert::Parse($var, $options);
});

I added user-defined function handlers, in addition to filters, because they use the same mechanism. I just don't have any use-cases for them at this time.

Joshua Morey | 2014-01-17

Skhema Templating Engine

In my last post, I wrote about my new templating engine. As I mentioned, the main unknown at the time was whether the language would adequately cover my use-cases. Since then, I have integrated it into the MVC framework of the Bramble Web Application Framework, and I am very satisfied with the results. As part of the integration, I changed the project name from templ@te to Skhema, since the old name was not web-friendly. This site is now running on Bramble using Skhema for rendering models.

The language has not changed much since last year. My concern was mostly with making sure that it was a good design, but I did find time for a few things. I improved the deserialization performance and provided alternate serialization modes. I improved the graph evaluation performance, and added preliminary function support. Functions are my primary extension-point at the moment, but they aren't particularly fast yet because the graph does an inefficient late-evaluation. All I have at this point are some basic operations like iteration index and cycle, but once I get back to this project I will properly generate the graph nodes for functions and add support for more powerful operations and user extensions.

<table>
{?list}
<tr class="{%cycle=odd,even}">
<td>{%iteration}</td>
<td><a href="{$url}">{$name}</a></td>
</tr>
{/}
</table>

For now, functions are scope-limited. I haven't decided on a syntax yet for accessing the functions of the outer list from the following example.

{?outer}
{?inner}
{%iteration}<br>
{/}
{/}

I didn't cover this in my last post, but one of the main differences that Bramble has from many other PHP templating engines is that mine does not compile to PHP statements, but rather to a graph that is evaluated using the data binding source. Originally, I had intended the graph/tree to be an intermediate structure that would get converted to PHP code for evaluation. Surprisingly, however, the graph evaluation proved to be faster than the alternative template engines that I was using for comparison, so I decided to simply serialize the graph instead.