|
BrainDump
This page is my initial brain dump on template syntax and interface. This is all subject to change. SyntaxCode LinesLines of javascript can be mixed with the output by prefixing each line with a ':', e.g. : if (validation_error) {
<p class="error">Sort it out geezer.</p>
: }Multiple lines run together, i.e. this works: : if (validation_error &&
: user.is_muppet()) {
<p>Hello Ben</p>
: }Expression TagsAn expression tag inserts the result of a javascript expression into the output, e.g. Hello <( user.name )>. There should be some filtering syntax, but I am not convinced on the one I have yet: Hello <( user.name )h>. (escape html entities in the name) Code TagsCode tags allow you to insert larger chunks of javascript code. <[
var blah = ...;
....
]>
: if (blah) {
: }Suppressed Line BreaksTo suppress a line-break, insert a backslash at the end of a line (or before trailing whitespace on the end of a line), e.g. Hello \ <( user.name )> (results in 'Hello Tom' without a line-break). This is more useful in plain text formats (e.g. plain text email) than html generation. Bookends(one of the few features not lifted from mason) Bookends allow you to turn output of content (outside of template tags off and on again). Again, this is more useful for plain text formats than for html (although sometimes ie is a bit pissy about whitespace, when this comes in handy), e.g. |-
: for (var i = 0; i < 3; i++) {
-|<( i )> |-
: }
-|(results in '0 1 2 ' with no extra whitespace). Method DefinitionsMethod definitions are the primary unit of reuse. See below for how they are called. <~method_name(parameter, list)> I am the output of the method_name method. </~> Method CallsMethod calls are ordinary javascript method calls (this is different to mason, which has a special method call syntax - I think this is unnecessary), e.g. The output is <[ this.method_name('blah', 'blah') ]>.InheritanceThere needs to be some way for a component file to declare it's parent component, I think at compile-time (is this right?). Something like this? <~=super autohandler.hst ~> Multiple Component RootsThere should be an ordered list of paths that are searched for any component method. Where a component file exists in two component roots, the files should be searched in order for the requested method (this is different to mason, which only considers the first file). Component Calls with ContentSometimes it is useful to pass a content generator into a method (mason calls this 'calling with content' I think). This is really just functional programming, where functions are a first class type that can be passed around. There is no special features for this (like in mason) as javascript already has adequate support for it: <[
this.mailmerge(
'Hello',
function (name) {
]>
Dear <( name )>, ...
<[
}
);
]>(Looks a bit scary at first, but you do get used to it. This will be for the more advanced users.) Calling Methods in Overridden ComponentsSuppose you have two component roots, 'common' and 'site', with each containing an email.jist template with a 'body' method. You can use the 'next_comp_root' method to call an overridden method in a secondary component root, e.g. roots/site/email.hst <~body> : this.next_comp_root.body(); ------------------------------ : this.disclaimer(); </~> roots/common/email.hst <~body> Hello blah, ... </~> Calling Methods in Parent ComponentsTo call a method in a parent component, use the 'super' method (got to check if that is a reserved word in javascript), e.g. <~blah> : this.super.blah(); </~> ImplementationThe parser is pretty much done, I just have to finish porting it to javascript. It uses regexes (m/\G.../g in perl, Regex.exec(/.../g) in javascript) and creates an abstract syntax tree of objects representing each type of construct above (again these classes need to be ported to javascript). There is then a decorator for each target (I think it is a decorator, but I am not too familiar with that pattern, and somebody took the GOF book on holiday with them), which walks the AST and outputs code in the target language. For example there would be a JavascriptDecorator that outputs the javascript code necessary to make the template run and a HTMLSytaxHighlighterDecorator to produce highlighted syntax of the template as html code (can't think of any others right now). The javscript output will be: In: blah Out: something.write('blah')In: : if (true) {
blah
: }Out: if (true) {
something.write(' blah\n');
}In: blah <( blah )> blah. Out: something.write('blah ' + blah + ' blah');Or: something.write('blah ');something.write(blah);something.write(' blah');..and so on. I think that everything should be required to be in a method. The method will be called on some kind of context object that encapsulates the something (perhaps that should be this.write('blah')), as well as doing the method resolution when the user calls a method and providing a 'super' and 'next_comp_root' methods, that return appropriate context objects. All that remains after that is an class for finding and compiling components, caching them in memory and providing the appropriate context objects. InterfaceThe common interface that we define for different templating languages should support multiple component roots and specifying a component and method when invoking it. It should support passing in a data structure containing hashes (javascript objects, but not real objects), arrays and simple data types (basically anything that can be serialized as json for remoting purposes). This interface should be available to pure javascript with wrappers for each backend technology that we support. |
Sign in to add a comment