IronRuby/RHTML based templates for .NET

Posted by – December 8, 2009

.NET Logo

During last few days I was prototyping some of new features I would like to add to ByteCarrot application. One of them is new template engine which will replace current XSLT based solution. I wanted something with more friendly syntax, something easy to create, change and maintain. I took a look at many template engines available for .NET platform but none of them met my needs. I thought that it would be nice to have solution with scripting support and syntax similar to this available in default ASP.NET MVC view engine.

After digging I have found an ASP.NET MVC application prototype with view engine based on IronRuby. It was created by Phil Haack‘s team and its syntax was exactly the same as RHTML used in Ruby on Rails views. It turned out that this engine was exactly what I needed, so I have taken parts of the code and adapted it to my scenario. The result is amazing because now I have fully functional templates engine with support of dynamic language where I can handle every possible HTML generation scenario.

As an attachment to this post I have added the code created during prototyping. The code contains few classes where the most important is RhtmlEngine. It has very simple, self-expanatory interface and acts as an “entry point” for using mentioned templates engine. Below you can find two samples showing capabilities of my solution but if you want to take a look at the implementation and make your hands dirty feel free to download the code and play with it for yourself.

public class RhtmlEngine
{
    public Result Render(object model, string temlateFile, string targetFile)
        ...
    public Result Render(object model, StreamReader rhtml, StreamWriter writer)
        ...
}

Simple template

The templates engine requires three things to be able to generate HTML:

  • Model representing the data which should be presented in form of HTML page,
  • Template defining how model should be presented,
  • Target where generated HTML page should be saved.

Listing below shows simple template generating a header and a list of hobbies. Notice that IronRuby blocks are embedded exactly the same as Visual Basic .NET or C# code is embedded in ASP.NET MVC views.

SimpleTemplate.rhtml

<html>
    <body>
        <h1>Hi! My name is: <%= model.FirstName %><%= model.LastName %></h2>
        I like:
        <ul>
        <% model.Hobbies.each do |hobby| %>
			<li><%= hobby %></li>
        <% end %>
        </ul>
    </body>
</html>

Template shown above can be used to generate an HTML representation of the following model:

var model = new SampleModel
{
    FirstName = "Marcin",
    LastName = "Obel",
    Hobbies = new List<string>
    {
        "yachting",
        "fishing",
        "cycling"
    }
};

As you can notice looking at listing below, because the templates engine is very simple, you need only one line to change the model into HTML page.

var result = new RhtmlEngine().Render(model, "SimpleTemplate.rhtml", "SimpleHtml.html");

In result (as shown below) the code above will produce clean HTML filled in with the data from model.

SimpleHtml.html

<html>
    <body>
        <h1>Hi! My name is: MarcinObel</h2>
        I like:
        <ul>
            <li>yachting</li>
            <li>fishing</li>
            <li>cycling</li>
        </ul>
    </body>
</html>

More advanced example

The example above does not show the most powerful part of this solution which is in fact inherited from IronRuby itself. Because templates are based on fully functional, dynamic language there is a possibility to extend basic features like loops and if statements (available in all templates engines) with things like functions, classes or even use some functionalities available in .NET Framework. Next example extends this one mentioned in previous section. In the listing below you can notice two new functions:

  • encode - uses HttpUtility class from .NET Framework to encode HTML code
  • render_li – renders an item of HTML list

AdvancedTemplate.rhtml

<%
require 'System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
include System::Web

def encode(text)
    HttpUtility.HtmlEncode(text)
end

def render_li(text)
    writer.Write "<li>#{text}</li>";
end
%>

<html>
    <body>
        <h1>Hi! My name is: <%= model.FirstName %><%= model.LastName %></h2>
        I like:
        <ul>
        <% model.Hobbies.each do |hobby|
                 render_li hobby
             end %>
        </ul>
        <pre>
            <%= encode model.UglyJavaScript %>
        </pre>
    </body>
</html>

In order to present a usage of custom IronRuby function added to the template, below you can find extended model from previous example. Now the model contains additional property named UglyJavaScript containing JavaScript code which should be encoded before it can be presented in HTML.

var model = new SampleModel
{
    FirstName = "Marcin",
    LastName = "Obel",
    Hobbies = new List<string>
    {
        "yachting",
        "fishing",
        "cycling"
    },
    UglyJavaScript = @"<script type='text/javascript'>window.close();</script>"
};

var result = new RhtmlEngine().Render(model, "AdvancedTemplate.rhtml", "AdvancedHtml.html");

Like in previous example after passing the model and the template into engine we receive an HTML.  This time as you can see it contains JavaScript code from model encoded using .NET Framework functionality in order to be able to display it.

<html>
    <body>
        <h1>Hi! My name is: MarcinObel</h2>
        I like:
        <ul>
        <li>yachting</li><li>fishing</li><li>cycling</li>
        </ul>
        <pre>
        &lt;script type='text/javascript'&gt;window.close();&lt;/script&gt;
        </pre>
    </body>
</html>

Resources

Hey, keep in touch!! Follow me on Twitter, @marcinobel or subscribe to this blog.
3 Comments on IronRuby/RHTML based templates for .NET

Respond | Trackback

  1. Tom Janssens says:

    IMHO you should have taken a look at the spark viewengine (http://www.sparkviewengine.com/); it does everything you wanted, and a lot more (f.e. null handling, partial templates, … ); this is your example :

    Hi! My name is: ${model.FirstName} ${model.LastName}
    I like:

    ${hobby}

    ${model.UglyJavaScript}

    !{Model.UglyJavaScript}

  2. Tom Janssens says:

    Apparently the HTMLcodes aren’t escaped; this is the second attempt ;
    <html>
    <body>
    <h1>Hi! My name is: ${model.FirstName} ${model.LastName} </h2>
    I like:
    <ul>
    <li each="var hobby in Model.Hobbies">${hobby}</li>
    </ul>
    <pre>
    ${model.UglyJavaScript}
    </pre>
    <!– Include the javascript unencoded –>
    <script type="text/javascript">
    !{Model.UglyJavaScript}
    </script>
    </body>
    </html>

  3. Marcin Obel says:

    I know Spark view engine but it doesn’t meet my needs. I would consider to use it in some ASP.NET MVC project but I require templates with something more than it is available in Spark. My project is not a web page and I am using templates to generate HTML reports.

Respond

Comments

Comments