/// Thursday, October 27

Boo as a template compiler

Lazy Style Code Fu master (ahem) me presents Boo the template compiler.

If you read "custom preprocessor idea for Boo" then now you'll see the start of one of the projects I had in mind. Thanks to the Boo newsgroup I discovered Boo has compiler attributes that have the behaviour I want. I started working on it yesterday and code generation works.

Say you want to generate classes for tables in your databases. First retrieve the database metadata. I represent the metadata returned in a Hash named model. model contains the tables for the database as well as the name and columns for each table.

# controller.boo
import test from "test"

model = {
    "tables" : [
        {   "name": "user",
            "columns": ["user_id", "username", "password"]
        {   "name": "item",
            "columns": ["item_id", "name", "description"]

f = View(model)
print f.RunTemplate()

Don't let the Hash scare you. If you plan to do any AJAX, get used to the JSON format. I much prefer it over XML. You would probably build stongly typed classes for the metadata. JSON is just a quick way to represent objects. Back on topic, staying within the MVC pattern, create a view for model.

# view.boo
import MGutz.CodeInjector from "MGutz.CodeInjector"

[InjectCode(TemplateCodeBuilder, "view.tpl")]
class View:
    _model as Hash

    def constructor(model as Hash):
        _model = model

View doesn't do much but accept a model. The magic happens in the compiler. InjectCode is a special attribute instructing the Boo compiler, "invoke me when you compile this class." When the atribute is invoked, it instantiates TemplateCodeBuilder to process the contents of the view.tpl file. TemplateCodeBuilder converts the file into Boo code and it becomes the body for a RunTemplate() method. InjectCode then injects RunTemplate() into the class View.

Look at the view's template using the model.

# view.tpl - template for the view
<% for table as Hash in Model['tables']: %>
class <%= table['name'] %>:

    <% for column in table['columns']: %>
    _<%=column%> as string

    <% end %>
<% end %>

It's ASP(.NET) syntax. Notice how Model is accessible here. You could access the instance variable _model just as well. Remember this template is injected as a function into View, it has access to private methods, private fields, ... like any member of the View. Also note the end keyword. This is an alternate Boo syntax when using it in markup scripts. end is a required keyword in your templates.

Compile this baby. view.boo + view.tpl merges into a single class into test.dll.

> booc -t:library -out:test.dll view.boo

Run the controller.

> booi controller.boo

class user:
        _user_id as string

        _username as string

        _password as string

class item:
        _item_id as string

        _name as string

        _description as string

Voila! Instant classes for your database. Remember, the template is compiled. Think of a template as a function in an abstract format. You now have a compiled class that executes your template without any run-time parsing, merging, etc. This is ideal not only for code generation but also for views in a web framework.

I'll finish up by saying Boo is such a wonderful language. It's what VB.NET should have been. Even down at the compiler pipeline level you just sense how solid Boo is.

If you're wondering what the generated code for View.RunTemplate() looks like.

def RunTemplate() as string:
        out = System.Text.StringBuilder()
         for table as Hash in Model['tables']:
out.Append("class ")
out.Append( table['name'] )

 for column in table['columns']:
out.Append(" as string\r\n\r\n")


        return out.ToString()


Yep, boo is fantastic ;-)
Do you have the source somewhere to download and play with? I'm also interested in using boo for templating purposes...
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?