Fabian is proud to present you this little tip:

Compiler Pipelines for Templates

Written by Michael Fellinger and Andrew Thompson, edited by Fabian Buch

Templates have to be transformed to be finally presented to your browser. In Nitro there are several stages a template can take to get there and also several ways.

There is a standard compiler pipeline, so that you don't have to care about it in simple standard applications. But there are tasks where you might want to alter the compiler pipeline or add new ones (for internationalization for example).

This is subject of a debate and might change in future (probably small parts only and more in the understanding of it).

Static Include transformer

But let's start with something simple. The compiler pipeline can be defined in your run.rb as follows:

def transform_template(template)
  template = StaticInclude.transform(template, self)
end

This performs static includes. Typically you should include this compiler as the first stage of the compile pipeline. This compiler is extremely helpful, typically you would want to use static includes in many many cases. Statically include sub-template files. The target file is included at compile time. If the given path is relative, the template_root stack of the controller is traversed. If an absolute path is provided, templates are searched only in Template.root

Quote from gmosx:

must be xformed before the ``

Example:

<?include href="root/myfile.xhtml" ?>

Morphing transformer

template = Morphing.transform(template, self)

A collection of standard morphers. The base morpher class. Morphers are triggered by a special 'key' attribute in the xml stream and transform the owner element.

attribute: if, unless

<div prop1="one" if="@mycond" prop2="two">@mycond is true</div>

becomes

<?r if @mycond ?>
  <div prop1="one" prop2="two">@mycond is true</div>
<?r end ?>

attribute: each, for

<li each="item in array">my item is #{item}</li>

becomes

<?r for item in array ?>
  <li>my item is #{item}</li>
<?r end ?>

attribute: times

<li times="3">...</li>

becomes

<?r 3.times do ?>
  <li>...</li>
<?r end ?>

Layout Compiler transformer

template = LayoutCompiler.transform(template, self)

We have to confess that we've got no idea what the heck is going on in there but for anybody interested in methods to continue from a rails-application might be able to figure this one out. Its a very simple Compiler anyway.

Elements transformer

template = Elements.transform(template, self)

Finds element tags, looks up an Element class for them, creates an instance of that Element and sets its instance vars to any variables passed in the tag. It then pushes this new instance onto the stack, adding it to its parent if it has one.

Elements are compiled at startup, they are static to some extent and can only change their behaviour based on instance variables. One way to have access to the outside world of an Element are sessions, which are globally available.

There are some problems due to its place in the pipeline, so you have to be careful how to use variables in what scope:

<?r if session[:foo] ?>
  \#{session[:foo]}
<?r end ?>
<?r if flash[:error] ?>
  \#{flash[:error]}
<?r end ?>
<?r if @context.path =\~ /financial/ ?>
  #{financial_menu} is a method in the Element.
  the =\~ is because we are within a %~~ string.
<?r end ?>

Example:

template.xhtml:

<?r bar = "foobar" ?>
<Page> Some Content </Page>
<Page foo="#{bar}" />
<Page />

skin.rb:

class Page < Nitro::Element
  def render
    %~
      <div class="page">
        #{foo} #{content} #{foo}
      </div>
    ~
  end
end

becomes:

<div class="page">
  Some Content
</div>
<div class="page">
  foobar  foobar
</div>
<div class="page">

</div>

Markup transformer

template = Markup.transform(template)

Transform the markup macros. Maps #(..) to :sanitize. (escapes XML elements?) Maps #|..| to :markup. (should do RedCloth markup)

glue/lib/glue/markup.rb is where you can try to figure out what the heck is going on.

Script Compiler transformer

template = ScriptCompiler.transform(template, self)

This one is too metaphysical to be explained by a mere human being. So we leave it as an exercise to the reader to figure out how that black magic works. Important for the understanding is especially the FlickR-example and it helps when you dig a bit through the rest of nitro too :)

However, if you want to use it, you can just try stuff like

#{alert("foobar")}

i think that should work... somehow...

Cleanup transformer

template = Cleanup.transform(template)

This one is best explained by what it really does:

<style something="red"></style> => <style something="red"/>
<include href="magic.xhtml"></include> => <include href="magic.xhtml"/>

Essentially it reduces empty tags while preserving any attributes. It tries to do something to textareas, but apparently it just ends up doing nothing but in an expensive fashion.

def self.cleanup(buf)
  out = buf.dup
  elements = "input|img|br|hr|link|style|render|include|inject|base|meta"
  out.gsub! /<textarea ([^>]*)><\/textarea>/, '<textarea \1>#{}<\/textarea>'
  out.gsub! /<(#{elements}) ([^>]*)><\/\1>/, '<\1 \2 />'
  out.gsub! /<(#{elements})><\/\1>/, '<\1 />'
  out
end

Template transformer

template = Template.transform(template)

Now to the very heart of the Compilers, the Template itself. It basically parses the files in Template.root combined with a method in the Controller (method and template together are called action and can act without the other part, so you can have a method without template and a template without method)

It basically enables you to use:

<?r some ruby-code ?>
<?rsome ruby-code %>
<?rby> some ruby-code </ruby>

(why the heck is this defined two times?) However, this one is static (compiled at startup)?

<include href="foo.xhtml" /> or <inject href="foo.xhtml" />

And this one is rendered at every view... calls the corresponding action

<render href="foo" />

To use your variables in the template, you can use one of these methods

#{my_val} for compilation at parse-time
#\my_val\ for compilation at parse-time (handy for XSL-style)
#[my_val] for compilation at render/eval-time, makes most sense with localization.

XHTML entities can be used like %rquo; and they'll be converted to &rquo;