Spark Documentation

What is Spark

Spark is a view engine for Asp.Net Mvc and Castle Project MonoRail frameworks. The idea is to allow the html to dominate the flow and the code to fit seamlessly.

For example,

<ul>
  <li each='var p in ViewData.Model.Products'>
    ${p.Name} !{Html.ActionLink[[ProductController]](c=>c.Edit(p.Id), "Edit")}
  </li>  
</ul>

The full csharp language is available in a way that doesn't interfere with the harmony and balance of the markup. The view template files produced compiled classes.

Needs documentation

Configuring Spark

Adding to Castle MonoRail

In the web application add references to the Castle.MonoRail.Views.Spark and Spark assemblies.

In the <monorail> section configure the spark view engine.

<monorail>
  <viewEngines viewPathRoot="Views">
    <add type="Castle.MonoRail.Views.Spark.SparkViewFactory, Castle.MonoRail.Views.Spark"/>
  </viewEngines>
</monorail>

That's about it really.

Adding to Asp.Net MVC

View engines are no managed by the controller. This section is now correct for Preview 5

The simplest way to use Spark from an Asp.Net MVC web application is to register an instance of the SparkViewFactory inn your Global Application_Start function at the same point you would be registering routes and such.

protected void Application_Start(object sender, EventArgs e)
{
    ViewEngines.Engines.Add(new SparkViewFactory());
}

This is also an excellent place to provide any other non-default services to the view factory. Such as an IViewFolder that reads from embedded resources instead of the file system, or an IViewActivatorFactory which can take over the responsibility of instantiating the compiled view types.

todo - document use of IoC for this

Spark settings in config file

After the Spark view engine is integrated into your MVC, MonoRail, or other application there are some additional settings you may want to configure. One way to do this is with a <spark> config file section.

<configSections>
  <section name="spark" type="Spark.Configuration.SparkSectionHandler, Spark"/>
</configSections>
<spark>
  <compilation debug="true|false">
    <assemblies>
      <add assembly="YourAssemblyName" />
      <add assembly="YourOtherStrongAssemblyName, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </assemblies>
  </compilation>
  <pages pageBaseType="Your.NonDefault.BaseSparkView" prefix="{optional string}">
    <namespaces>
      <add namespace="System"/>
      <add namespace="System.Collections.Generic"/>
      <add namespace="System.Linq"/>
      <add namespace="System.Web.Mvc"/>
    </namespaces>
    <resources>
      <add match="/content/css" location="http://www.yourcdnprovider.com/youraccount/allstyles/css"/>
      <add match="/content/js" location="http://www.yourcdnprovider.com/youraccount/appname/js"/>
    </resources>
  </pages>
</spark>

Additional assemblies and namespaces can be referenced with <use assembly="..." namespace="..." /> in view, layout, and partial files. All assemblies loaded in the app domain are automatically referenced, there may be times you want to add references explicitly for assemblies that aren't yet loaded.

The default page base type, MvcContrib.SparkViewEngine.SparkView or Castle.MonoRail.Views.Spark.SparkView, will be used if one isn't specified. If you do provide a class as the page base type you'll probably inherit from the default base class.

An optional prefix may be selected, which must then be used on all Spark elements and attributes in your template files. The prefix should not be set here if you're defining a prefix in the templates with xmlns attributes.

The resources section may be used to control the result of ~/ style urls. For example, <script src="~/content/js/foo.js"/> will normally appear as a ${SiteRoot} prefixed url but the match for "/content/js" above will make that url appear as the given location + "/foo.js". The assumption for high-volume sites is you would use the normal urls for development and map them to a static file server or to a cdn provider in production.

Spark settings in code

Another way to provide these settings to the spark engine is to provide an ISparkSettings instance to the constructor.

protected void Application_Start(object sender, EventArgs e)
{
    var settings = new SparkSettings()
        .SetDebug(true)
        .SetPageBaseType("Your.NonDefault.BaseSparkView")
        .AddAssembly("YourAssembly")
        .AddNamespace("System")
        .AddNamespace("System.Collections.Generic")
        .AddNamespace("System.Linq")
        .AddNamespace("System.Web.Mvc");
 
    ViewEngines.Engines.Add(new SparkViewFactory(settings));
}

General Syntax

Basic markup

A view file has basic html markup that can appear pretty much as you'd expect.

<div>
  <p>Hello <span>world</span>!</p>
</div>

Including code

CSharp code may be used to produce output. Any helper methods, object properties, or expression that evaluates to a non-void value can be used.

<p>${"Hello world"}</p>
<p>${Guid.NewGuid().ToString("n")}</p>

Code will also appear in special attributes or elements like "if", "else", and "var". These types of areas will never require ${} or <%= %> notation.

<var names="new [] {'alpha', 'beta', 'gamma'}"/>
<for each="var name in names">
  <test if="name == 'beta'">
    <p>beta is my favorite.</p>
    <else/>
    <p>${name} is okay too I suppose. 
  </test>
</for>

However if you use code to produce a normal attribute's value, that is not a special attribute does require the code-output notation.

<viewdata currentProduct="Product"/>
<p>${currentProduct.Name} <a href="/Product/Edit/${currentProduct.Id}">Edit</a></p>

Special characters in code

The entire functionality of the csharp language is available. As always, please use responsibly. :) Be sure to put business logic in your controller's actions and write unit test for it.

There are a small number of characters that are parsed in a special way to avoid simple mechanical problems you can encounter in a standard xml editor.

The sequences [[ and ]] are parsed as if they are < and >. This can be convenient when you're using generic classes or methods from a spark file, to avoid the editor's tendency to add a closing elements for you. The [[ and ]] will not be changed if they occur inside a string literal.

String literals may be expressed in 'single quotes' in addition to "double quotes". This is again for convenience when using expressions inside attributes values that are contained in double quotes. If you do need to use a single char constant in an expression, using an explicit type-cast (char)'x' will avoid having the string changed to "x".

<viewdata products="IList[[Products]]"/>
<var styles="new[] {'even', 'odd'}"/>

Spark element prefix with config

By default, Spark tags are unqualified. All of the examples you'll see are written that way. That can cause a problem if Spark is being used to create xml rather than xhtml. One of the ways to disambiguate spark xml from output xml is to set a prefix for your application.

<spark>
  <pages prefix="s"/>
</spark>

Or

var settings = new SparkSettings()
    .SetPrefix("s");

At which point all of the native spark elements and attributes must include the s: prefix to be recognized. None of the built-in prefixes macro:, content:, use:, render:, and section: are affected by the use of the prefix setting.

<div>
  <s:viewdata user="UserInfo"/>
  <p s:if="user != null">${user.Name}</p>
  <s:LoginForm/>
</div>

Spark element prefix with xmlns namespaces

Another way to disambiguate the markup is to declare xmlns attributes, which will result in well-formed and valid xml. All of the Spark namespace Uris begin with http://sparkviewengine.com/. If a template file contains a Spark xmlns attribute then the other built-in prefixes (macro:, etc.) will only function if their namespace Uri's are also declared.

Default prefix Namespace Uri
none - spark tags and partial files http://sparkviewengine/
content: http://sparkviewengine/content/
use: http://sparkviewengine/use/
macro: http://sparkviewengine/macro/
section: http://sparkviewengine/section/
render: http://sparkviewengine/render/

You can use the defaults or specify any prefix you choose. Once an xmlns attribute is defined only the value on the namespace Uri is significant.

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:s="http://sparkviewengine.com/"
      xmlns:fn="http://sparkviewengine.com/macro/">
<body>
  <fn:ShowNames favorite="string">
    <s:var names="new [] {'alpha', 'beta', 'gamma'}"/>
    <s:for each="var name in names">
      <s:test if="name == favorite">
        <p>${favorite} is my favorite.</p>
        <s:else/>
        <p>${name} is okay too I suppose.
      </s:test>
    </s:for>
  </fn:ShowNames>
  ${ShowNames("beta")}
  ${ShowNames("gamma")}
</body>
</html>

Variables

Declaring local variables

The var element is a quick and easy way to declare local variables. The default behavior is to create a local of automatic type "var" which is initialized inline.

For example, the following:

<var styles="new [] {'odd', 'even'}" i="0"/>

Produces the following:

var styles = new [] {"odd", "even"};
var i = 0;

The var element has an optional type attribute which can be used to provide an explicit type used instead of the automatic type syntax "var".

<var foo="null" bar="null" type="string"/>

Will declare variables of type string instead of var.

string foo = null;
string bar = null;

Finally if the var element is not declared as a self-closing tag the scope of the variables will be limited. For example:

<div>
  <var styles="new [] {'even', 'odd'}" i="0">
    <p>${i} is ${styles[i%2]}</p>
  </var>
  <set i="5"/> <!-- compiler error - variable i is out of scope -->
</div>

will result in:

Output.Write("<div>");
{
var styles=new[] {"even", "odd"};
var i = 0;
Output.Write("<p>");
Output.Write(i);
Output.Write(" is ");
Output.Write(styles[i%2]);
Output.Write("</p>");
}
Output.Write("</div>");
i = 5;

In this case the local variables are only available inside the element. This can be convenient to scope explicitly.

Declaring global variables

Local variables have a limited scope. They're declared in the generated class right in line in the methods that are generating output. Globals on the other hand can be declared at any location in the view, master, or partial template files and the value can be used and assigned at any point.

<h2>${Title}</h2>
<global Title="'My Default Title'"/>

Global variables are actually implemented as field members of the view class. So the above example would contain object Title="My Default Title"; which is available to from any method. You may also use provide a type attribute to avoid using the default field type object. Multiple variables may be defined in the same element.

<global type="string" Foo="'Hello'" Bar="'World'" />

Setting local and global values

The ${expression} and <%=expression%> syntax is only useful for producing a value to send to output. For local and global variable assignment the set element may be used.

Using global for title

<html>
  <head>
    <global type='string' Title='"Site Name"'/>
    <title>${Title}</title>
  </head>
  <body>
    <div><use content="view"/></div>
  </body>
</html>

View that sets the title

<set Title='product.Name + " - " + Title'/>
<!-- or -->
<set Title='string.Format("{0} - {1}", product.Name, Title)'/>

Using view data

A ViewData property is available as an object dictionary on the base class so syntax like ${ViewData["blah"]} is valid. There is an easier option however which adds a strongly typed property accessor for a view data member.

<viewdata 
  Caption="string" 
  Products="System.Collections.Generic.IList[[MyApp.Models.Product]]"/>
<div>
  <h3>${Caption}</h3>
  <div  each="var product in Products">
    <h4>${product.Name}</h4>
    <div>${product.Description}</div>
  </div>
</div>

The viewdata element may occur anywhere in the view or master file actually. The net result is the same which is to add something like the following to the generated class.

string Caption 
{get {return (string)ViewData["Caption"];}}
 
System.Collections.Generic.IList<MyApp.Models.Product> Products 
{get {return (System.Collections.Generic.IList<MyApp.Models.Product>)ViewData["Products"];}}

In this case the [[ and ]] are replaced with < and > because of the difficulty xml-centric editors have with certain characters appearing in attributes.

Using view data with a typed model

The viewdata element may have a model attribute in the view or master file.

<viewdata model="MyApp.Models.Catalog"/>
<p>(${ViewData.Model.CatalogID}) ${ViewData.Model.Name} </p>

The net effect is to have the model attribute used as the TModel parameter for the ViewDataDictionary.

The model attribute can be used in conjunction with the other viewdata variable declaration. For example if your master template uses a typecast named viewdata argument to access a member variable, like <viewdata Title="string"/>, your views can still use a specific <viewdata model="typename"/> if the type has a string Title property.

<viewdata Title="string"/>
<title>${Html.Encode(Title ?? "My Sample MVC Application")}</title>

The above example enables you to add a string Title property to any class used as a TModel, or as an automatic class new {Title = "hello"}, or as a dictionary entry in an untyped view data dictionary.

Expressions

Simple csharp expressions

Any csharp syntax can appear as ${expression} or <%=expression%>

The expression text itself is reproduced verbatim as a TextWriter.Write(expression); line. So anything that evaluates to a string, simple type, or object with ToString can be used in an expression.

<div>
  <p>The time is ${DateTime.Now}.</p>
  <p>The time is <%=DateTime.Now%>.</p>
</div>

Nulls in expressions

By default when a NullReferenceException is thrown from the ${expressions} or <%=expression%> it will be caught and the original expression will be output. This is similar to the behavior you will find in NVelocity and is intended to assist troubleshooting simple data problems.

The syntax $!{expression} can also be used if you want to ensure any null values and NullReferenceException that result from the expression will produce no output at all.

For example, with the following template:

<div class="shipto">
  <p>$!{currentInvoice.ShipTo.FullName}</p>
  <p>$!{currentInvoice.ShipTo.Address.City}, $!{currentInvoice.ShipTo.Address.State} $!{currentInvoice.ShipTo.Address.Zip}</p>
</div>

If there is a null currentInvoice or a null ShipTo, the output would be:

<div class="shipto">
  <p></p>
  <p>,  </p>
</div>

It's not ideal by a long shot, but slightly better than a yellow screen of death if you don't manage to get a hundred percent correct null testing in your views.

Using Mvc Helpers

There are instances of HtmlHelper, UrlHelper, and AjaxHelper available as Html, Url, and Ajax properties respectively. Any of their methods which return string-friendly information can be used as an output expressions.

<!-- Link to the Sort action on the current controller -->
<p>${Html.ActionLink("Click me", "Sort")}</p>
<!-- Put out data in a way that's safe -->
<p>${Html.Encode(stuff)}</p>

Conditional elements if, test, and else

The elements <if> and <else> can be used to produce content conditionally. The if element must have a condition attribute which is used literally as the boolean csharp expression.

<var x='5'/>
 
<if condition='x == 5'>
  <p class='resultmessage'>Some value is five</p>
</if>

An <if> element may be followed by any number of optional <else> elements that have if attributes and finally a single, optional <else> element. Only whitespace may be between the if/else elements.

<viewdata user='UserInfo'/>
 
<if condition='!user.IsLoggedIn()'>
  <p>Here's a login form</p>
</if>
<else if='user.HasRole(RoleType.Administrator)'>
  <p>Hello - you're an admin</p>
</else>
<else if='user.HasRole(RoleType.Registered)'>
  <p>Hello - you're a registered user</p>
</else>
<else>
  <p>I have no idea what type of person you are</p>
</else>

If you prefer, instread of <if condition=""> you can use the <test if=""> syntax. The functionality is the same. Another variation on the syntax is the ability to use empty <else if=""/> and <else/> elements inside of the or . The following identical to the example above.

<viewdata user='UserInfo'/>
 
<test if='!user.IsLoggedIn()'>
  <p>Here's a login form</p>
  <else if='user.HasRole(RoleType.Administrator)'/>
  <p>Hello - you're an admin</p>
  <else if='user.HasRole(RoleType.Registered)'/>
  <p>Hello - you're a registered user</p>
  <else/>
  <p>I have no idea what type of person you are</p>
</test>

Conditional attributes if and elseif

The if and elseif attributes can be used on any other element. This has the same effect as having a wrapping if or else element.

<var x='5'/>
 
<p if='x==5' class='resultmessage'>Some value is five</p>

The same rules about order and whitespace apply. An element with an elseif="" attribute, or an else element, may only follow another conditional element.

<use namespace='SampleApp.Models'/>
<viewdata user='UserInfo'/>
 
<p if='!user.IsLoggedIn()'>Here's a login form</p>
<p elseif='user.HasRole(RoleType.Administrator)'>Hello - you're an admin</p>
<p elseif='user.HasRole(RoleType.Registered)'>Hello - you're a registered user</p>
<else>
  <p>I have no idea what type of person you are</p>
</else>

Conditional attribute once

A fairly typical situation can come up where a partial will need to add a stylesheet reference or some jquery code. On the one hand you don't want to have those resources pulled in multiple times if the partial is used more than once, and on the other hand you don't want to simply include that resource just once in the site layout for all pages whether they need it or not. One way to implement the middle ground is to declare a boolean flag and set it once when you add a script to the header.

To make this code smaller the attribute once="flagname" was added. The contents of the element will be rendered only the first time any given "flagname" values is used when rendering a page. Any conventions can be used about what values are used as flags, the following is just an example. You may also use ${expr} or <%=expr%> syntax in the value of the once attribute.

<content name="head">
  <!--  add jquery if it hasn't been yet, and add countdown plugin -->
  <script once="jquery" type="text/javascript" src="~/content/js/jquery-1.2.6.js"/>
  <script once="jquery-countdown" type="text/javascript" src="~/content/js/jquery.countdown.js"/>
</content>

The implementation if this attribute results in the bool Once(string flagname) method being called which returns true the first time a distinct value is used for the argument. This method may also be called from code.

<macro name="Peculiar" issue="string">
#if (Once("log-peculiar")) 
#  Logger.Warn("Something's not right on this page. First issue: " + issue);
</macro>

Conditional attribute output

The syntax ?{boolean} can be used only inside the text of an attribute. Text and code before the condition (up to and including the previous whitespace) will be controlled by the result of the boolean expression. In the generated code the boolean will appear directly inside the parenthesis of an if statement, so anything that evaluates to a bool is okay.

<ul>
  <li each="var product in Products" class="first?{productIsFirst} last?{productIsLast}">
    ${H(product.Name)}
  </li>
</ul>

will output

<ul>
  <li class="first">Alpha</li>
  <li>Beta</li>
  <li>Gamma</li>
  <li class=" last">Delta</li>
</ul>
or
<ul>
  <li class="first last">just one product</li>
</ul>

Plus if all of the text of an attribute is killed, or if the only thing in the attribute was the test, then the entire attribute will disappear from the output. That's for checkboxes and such.

<input type="checkbox" name="chkhello" checked="?{isHelloChecked}"></input>

will output

<input type="checkbox" name="chkhello" checked=""></input>
or
<input type="checkbox" name="chkhello"></input>

Looping and iteration

A foreach loop is produced with the element for and the attribute each. The each attribute is used verbatim so must have the type, variable name, "in", and collection.

<viewdata Posts="IList[[MyApp.Models.Post]"/>
<for each="var post in Posts">
  <p>${post.Title}</p>
</for>

Any for loop comes with several optional local variables. Their name is always the same as the looping variable, plus the suffixes "Index", "Count", "IsFirst", and "IsLast". The variables are scoped to the for loop itself, and only the ones which appear to be used in the contained code will be created.

<viewdata Posts="IList[[MyApp.Models.Post]"/>
<var styles="new[] {'even','odd'}"/>
<for each="var post in Posts">
  <p class="${styles[postIndex%2]}">${postIndex}. ${post.Title}</p>
</for>

Something to be aware with the use of the "Count" and "IsLast" variables - they both need to know how many items are in the collection before the iteration begins. For collections that resolve to IEnumerable<T> the Linq extension method .Count() is used, and for plain old IEnumerable Spark will run iterate the collection incrementing an integer to get the count. Not a performance problem of course, just worth mentioning.

<for each="var lineItem in CurrentInvoice.LineItems">
  <tr><td>Item ${lineItemIndex} of ${lineItemCount}</td><td>${lineItem.Etc}</td></tr>
  <tr if="${lineItemIsLast}"><td>Grand total</td><td>etc</td></tr>
</for>

The for element can also have additional attributes which will evaluated as a variable assignment. Note - the named variable of the correct type must already exist. Maybe at some point the code generator will keep track of declared variables in scope and create new 'var's where appropriate. I think I'll add a trac item for that.

<viewdata currentProductId="int"/>
<var styles="new [] {'even', 'odd'}" isCurrent="false">
  <for each="var product in Products" isCurrent="product.Id==currentProductId">
    <p class="highlighted?{isCurrent} ${styles[productIndex%2]}">${Html.Encode(product.Name)}</p>
  </for>
</var>

Iteration with the each attribute

You may also add the each attribute to any plain element.

<table>
  <tr>
    <td>Name</td>
    <td>Type</td>
  </tr>
  <tr each='var user in users'>
    <td>${user.Name}</td>
    <td>${user.UserType}</td>
  </tr>
</table>

If you do that other attributes are not treated as variable assignment, but they may contain ${expression} evaluation.

<table>
  <tr>
    <td>Name</td>
    <td>Type</td>
  </tr>
  <var classes="new [] {'even','odd'}">
    <tr each="var user in users" class="${classes[userIndex%2]}">
      <td>${userIndex}) ${user.Name}</td>
      <td>${user.UserType}</td>
    </tr>
  </var>
</table>

Using namespace

You can add using statements to avoid typing out fully qualified type names, and to make extension methods to helpers available.

<use namespace="System.Collections.Generic"/>
<use namespace="System.Web.Mvc"/>
<viewdata Names="IList[[string]]"/>

The use namespace elements may appear anywhere in the view, master, or partial template files. The namespaces are de-duplicated so it's safe to include them where they're needed in views and partials if you prefer even though the same namespace could appear several times.

Inline code

Sometimes, when push comes to shove, you have a situation where you're not writing output and there isn't a markup construct for what you want to do. As a last resort you can produce code directly in-place in the generated class.

This takes two forms. An aspx-like <%statement%> version is available as well as a #statement variation.

<test if="user.IsLoggedIn()">
  <p>Hello, ${user.Name}.</p>
  <else if="user.HasValidTrialSession()"/>
  <p>Hello, Valued Future Customer.</p>
  <else/>
  <p>Hello, er... you.</p>
  # System.Diagnostic.Trace.WriteLine("Unexpected anon user.");
  # if (System.Diagnostic.Debugger.IsAttached)
  #   System.Diagnostic.Debugger.Break();
</test>

Declaring Macros

When it comes right down to it a helper method is a function that takes arguments and returns a string. A Spark <macro> produces something which is much the same: a function that takes arguments and returns a string.

Because a macro is declared in a the spark template it's contents may contain any literal html, rendering of partial files, expression evaluation, etc. All of the output from this is captured as the simple return value. This enables you to invoke a <macro name="foo"> using ${foo()} or <%=foo()%> from anywhere in your template files.

<viewdata errorMessage="string" />
 
<macro name="ShowError" caption="string" message="string">
<div class="message error">
  <h3>${H(caption)}</h3>
  <div>${message}</div>
  <% Logger.Warn(caption); %> <!-- this is a MR example. asp.net mvc would use different logging -->
</div>
</macro>
 
<h2>Place Order</h2>
<test if="!string.IsNullOrEmpty(errorMessage)">
  ${ShowError("Failed to place order", errorMessage)}
</test>
 
<!-- form here, field validation messages, etc. -->

The following method generated code is the result

string ShowError(string caption, string message)
{
  using(OutputScope(new System.IO.StringWriter())) 
  { 
    Output.Write("\r\n  <div class=\"message error\">\r\n    <h3>");
    Output.Write(H(caption));
    Output.Write("</h3>\r\n    <div>");
    Output.Write(message);
    Output.Write("</div>\r\n    ");
    Logger.Warn(caption);
    Output.Write("  </div>\r\n");
    return Output.ToString(); 
  }
}
 
//... and in the RenderViewContent method
Output.Write("\r\n<h2>Place Order</h2>\r\n");
if (!string.IsNullOrEmpty(errorMessage)) 
{
  Output.Write(ShowError("Failed to place order", errorMessage));
} // if (!string.IsNullOrEmpty(errorMessage))
Output.Write("\r\n\r\n<!-- form here, field validation messages, etc. -->\r\n\r\n");

A macro may also call itself recursively, which is convenient for producing something like a threaded comment list or a nested ul/ul/li tree structure.

Organizing Content

Master-View relationship

The view contents are rendered first, followed by the master contents. The master template includes the view at the appropriate location with ${Content["view"]}, or <use content="view"/>, or <use:view/>.

<html>
  <head>
    <title>${Title}</title>
  </head>
  <body>
    <div id="header">dot dot dot</div>
    <div id="pageContent"><use content="view"/></div>
    <div id="footer">dot dot dot</div>
  </body>
</html>

Named content sections

Defining additional number of content sections or modules is one way you can create extensible blocks or regions in your layout. It's also a convenient way to enable a view to include additional css and js references in the head section.

This example shows a master layout which provides the ability for the views to add script and style to the head element, and add sections to a sidebar container.

Making named content appear in output

<html>
  <head>
    <title>${Title}</title>
    <use content="head"/>
  </head>
  <body>
    <div id="header">dot dot dot</div>
    <div id="sidebar"><use content="sidebar"/></div>
    <div id="pageContent"><use content="view"/></div>
    <div id="footer">dot dot dot</div>
  </body>
</html>

Adding information to named content locations

<p>this goes in the pageContent</p>
 
<content name="sidebar">
  <h3>Search</h3>
  <p>This is added to the sidebar</p>
<content>
 
<content name="head">
  <script type="text/javascript" src="yadda/file.js"></script>
<content>
 
<content name="sidebar">
  <h3>See also</h3>
  <p>This is also added to the sidebar</p>
<content>

The implementation details of this are very straightforward. The generated class is appending values to the current textwriter. When a element is encountered it changes which textwriter is current. Later when a <use content="foo"/> or ${Content["foo"]} is encountered the accumulated text in the named content is written to the textwriter that is current at the time.

There is one intrinsic content section named "view". It will contain the output of the previous level when rendering view/master. In MonoRail there can be several layouts named, and the section named "view" will always contain the total result of the previous levels.

You can also use the prefixes as an alias for <content name="foo"> and <use content="foo">. Those would be content:foo and use:foo respectively.

<content:message>
  <p>This is a message</p>
</content:message>
 
<content:message>
  <p>This is another message</p>
</content:message>
 
<div class="messages" if="Content.ContainsKey('message')">
  <use:message/>
</div>

Parsing and rendering partial files

You can include and render a partial file at a particular location with the following syntax:

viewfile

<use file="mypartial"/>

This will look for a mypartial.xml file in the same directory as the view or in the Shared directory.

You can also declare local variables as attributes of the "use" element. Those variables, and anything else that was in context, can be used within the scope of the partial as local variables.

viewfile

<use file="mypartial" caption="product.Name"/>

mypartial.xml

<div>
<h3>${caption}</h3>
</div>

Implicit partial rendering

Finally, if your partial file starts with an underscore character the rest of the file name can be used as a new special element. This is nothing more than being used as a shortcut for but it sure looks cool.

viewfile

<var styles='new[] {"even", "odd"}' i='0'>
  <for each='var product in Products' i='i+1'>
    <ProductSummary cssclass="styles[i%2]" number="i"/>
  </for>
</var>

_ProductSummary.xml

<div class="${cssclass}">
<p>${number}) ${product.Name}</p>
</div>

Partial files starting with an underscore will be used from the view directory of the controller and from the "Shared" view directory.

Importing files

Many of the elements like <global>, <viewdata>, <macro name="">, <use namespace="">, and <use assembly=""> have an effect on the generated view class but do not produce output in-line.

You can remove a lot of these declarations from view, partial, and layout files by moving them into a spark file which you then import.

Shared\\CommonMacros.spark

General-purpose references
<use namespace="System"/>
<use namespace="System.Collections.Generic"/>
<use assembly="MyWebApp"/>
 
These are null if they're not in the viewdatadictionary or propertybag
<viewdata warning="string" error="string"/>
 
This macro writes out a warning or error if it's in the viewdata.
<macro:WarningOrError>
  <div class="msgbox warning" if="!string.IsNullOrEmpty(warning)">
    ${H(warning)}
  </div>
  <div class="msgbox error" if="!string.IsNullOrEmpty(error)">
    ${H(error)}
  </div>
</macro:WarningOrError>

SomeView.spark

  <use import="CommonMacros"/>
  ${WarningOrError()}

Import files are much like partial files, except they will not generate rendering code at the points where the <use import=""> occurs. Any text or markup in the imported file, other than the class-level declarations, can be used more or less like comment that won't add a single thing to the generated class or the html output. You can also import the same file from any number of views, partials, and layouts and it's contents will only be used once.

You may also place a _global.spark in a controller's view folder, or the layouts view folder, and that file will be automatically imported whenever a view or layout template is used from that location. Another file shared\\_global.spark can be created which will be imported once into every single view that's compiled.

Including files

Another way to manage content is with the <include href=""> element. The implementation of this element is based on a subset of the xinclude specification. See also O'Reilly Using XInclude and MSDN Combining XML Documents with XInclude.

The include and fallback elements are both supported. The include element may have href and parse attributes. The value href must be a relative path, may contain ".." style parent paths, and must include the target file's extension. The dotted parent paths may not go beyond the root of the IViewFolder.

<h2>Chapter One</h2>
<include href="../lib/chap01.xml"/>
<hr/>
<h3>Disclaimer</h3>
<include href="../lib/legal.txt" parse="text">
  <fallback>It appears the lawyers have nothing to say.</fallback>
</include>

You may also use explicit namespaces. Note: declaring namespaces with xmlns attibutes is all-or-nothing. If you declare the XInclude namespace in a file you must also declare the Spark namespace and use element prefixes.

<div class="shimmer" xmlns:s="http://sparkviewengine.com/" xmlns:xi="http://www.w3.org/2001/XInclude">
<p>Let's add a stylesheet to the header</p>
<s:content name="head">
  <style type="text/css">
    <xi:include href="../lib/effects.css" parse="text"/>
  </style>
</s:content>
</div>

When a file is including another file it happens before any significant processing takes place. As far as the Spark view engine is concerned the contents of the target file may as well have been copy-and-pasted right at the location of the include element. This can have some advantages, but be cautions about duplicate macro declarations or circular include references.

Precompiling Views

You can create a precompiled assembly with view classes at a high level with the a batch descriptor and a SparkViewFactory, or at a low level with an array of view descriptors and a SparkViewEngine.

If you provide a high-level batch descriptor to the asp.net mvc or castle monorail SparkViewFactory it will be converted into an array of descriptors based on which view files exist. Those will then be passed to the SparkViewEngine.

Precompiling on-startup with the Asp.Net Mvc SparkViewFactory

        public static void PrecompileViews(ControllerBuilder builder)
        {
            var controllerFactory = (SparkControllerFactory)builder.GetControllerFactory();
            var viewFactory = new SparkViewFactory(controllerFactory.Settings);
            var batch = new SparkBatchDescriptor();
            batch
                .For<HomeController>()
                .For<ProductsController>();
            viewFactory.Precompile(batch);
        }

A static method like this would be called from Global Application_Start. The tricky part is getting your hands on the instance of the view factory.

Precompiling on-startup with the Castle MonoRail SparkViewFactory

//todo - about the same. just need sample code

Precompiling on-startup with the low-level SparkViewEngine

If you create an instance of the SparkViewEngine you can call BatchCompilation directly. It takes the name of the target assembly, which should have the .dll extension, and a list of SparkViewDescriptor which have been filled in. The SparkViewEngine will need to have the ViewFolder property set.

            var engine = new SparkViewEngine(settings)
                             {
                                 ViewFolder = new FileSystemViewFolder(viewsLocation)
                             };
 
            engine.BatchCompilation(targetPath, Global.AllKnownDescriptors());

Using precompilation in a unit test

With Asp.Net Mvc the view factory is fairly easy to create when you need it. The tricky part can be making sure the path to the views folder is correct. You could pass in settings or assume that the spark section in the app.config in your test project is correct.

            var viewFactory = new SparkViewFactory(settings)
                                  {
                                      ViewSourceLoader = new FileSystemViewSourceLoader(@"..\..\..\NorthwindDemo\Views")
                                  };
 
            var batch = new SparkBatchDescriptor();
            batch.For<ProductsController>();
            viewFactory.Precompile(batch);

The return value of Precompile and BatchCompilation is the resulting loaded Assembly. You could continue to make assertions about the number of types in the file.

Precompiling bin-deployable assembly with installutil and with a post-build step

This one is probably the coolest one. Especially if you're developing a web app to deploy to a medium trust shared environment. You can find some examples of this in the Samples\AspNetMvc\PrecompiledViews project and Samples\DirectUsage\MediumTrustHosting project.

First: Add the following to the web.config. This will cause an exception to be thrown if your web app ever tries to compile a view at runtime, which will help avoid a situation where you work in development (because the view will generate on the fly) but not in production.

  <system.web>
    <trust level="Medium" />
    ...

Second: Add a new Installer Class item to your web project. It will have a grey designer surface. From the toolbox drag a new instance of either the MvcContrib.SparkViewEngine.Install.PrecompileInstaller or the Castle.MonoRail.Views.Spark.Install.PrecompileInstaller onto this surface.

(If it's not in the toolbox you may need to right-click one of the categories, say "Choose Items...", and Browse... to locate the assembly that contains the tool you want.)

Third: Select the precompileInstaller1 you just added. If you like you can provide a target assembly file name - by default it will add .Views.dll to your web app assembly name. Then choose the events in the properties (yellow lightning) and double-click "DescribeBatch". Provide the implementation, for example:

        private void precompileInstaller1_DescribeBatch(object sender, DescribeBatchEventArgs e)
        {
            e.Batch
                .For<HomeController>()
                .For<HomeController>().Layout("Ajax").Include("_Notification")
                .For<AccountController>();
        }

You could also add a static to Global and have this event handler call that static if you don't want to bury information like this in an event on a designer of an installer component. You could also then re-use that function from a unit test that compiles everything also.

Fourth and finally: Right-click the web project, choose properties, and change the Build Events. Add the following Post-build event command line:

%systemroot%\Microsoft.NET\Framework\v2.0.50727\installutil "$(TargetPath)"

Done! Now every time you hit Build, or F5-Debug/Run, or run unit tests from visual studio you'll automatically generate the precompiled views assembly in the bin directory. Plus, as icing on the cake, if there are any errors that occur in the installer they'll show up in the build output.

------ Build started: Project: PrecompiledViews, Configuration: Debug Any CPU ------
PrecompiledViews -> E:\Projects\spark\src\Samples\AspNetMvc\PrecompiledViews\bin\PrecompiledViews.dll
%systemroot%\Microsoft.NET\Framework\v2.0.50727\installutil "E:\Projects\spark\src\Samples\AspNetMvc\PrecompiledViews\bin\PrecompiledViews.dll"
Microsoft (R) .NET Framework Installation utility Version 2.0.50727.1433
Copyright (c) Microsoft Corporation.  All rights reserved.
...etc...
An exception occurred during the Install phase.
Spark.Compiler.CompilerException: Dynamic view compilation failed.
generated.cs(20,22): error CS0103: The name 'hi' does not exist in the current context

Loading a deployed precompiled assembly

When the app loads, usually in Global.Application_Start, you can load an existing precompiled view assembly instead of generating one. You would need to call SparkViewEngine's LoadCompilation(Assembly precompiled) method. It will enumerate all of the public types in the assembly to recreate and cache the appropriate SparkViewDescriptors. The array of descriptors which were loaded are returned.

        public static void LoadPrecompiledViews(SparkViewFactory factory)
        {
            factory.Engine.LoadBatchCompilation(Assembly.Load("MyWebApp.Views.dll"));
        }

As always, the tricky part can be getting your hands on an instance of the SparkViewFactory.

There is no dynamic recompilation of classes that are loaded in this way and changes to the spark files will have no effect. However the files must be available on disk at runtime. Sorry about that - but the different frameworks still needs to test for the view files' existence to know what view descriptor it should instantiate.

Configuring batch descriptors

The batch descriptor contains multiple entries and is built with a fluent interface. The target assembly name can be provided on the constructor, otherwise the assembly will be named after a guid and go to the temporary asp.net files directory.

Each call to .For<YourController>() starts a new rule entry. Within a rule you can call Layout, Include, and Exclude multiple times. Include and Exclude can have patterns that end with *. If the pattern ends with * it won't find things in "Shared". The other special case is the single character "*" which matches anything that doesn't start with an underscore.

batch
  .For<HomeController>
  .For<HomeController>.Layout("ajax").Include("_*")
  .For<ProductsController>.Include("Index").Include("List").Include("Detail")
  .For<AccountController>.Include("*").Exclude("TestMessage").Exclude("Blah");

If Include is not called for a rule the default is to include all views that don't start with a "_". If Layout is not called the default is to use the layout that would be used automatically appropriate for the framework in question. If you call .Layout multiple times it'll generate a class for each combination of layout and view. That's if you want to precompile several themes where the controller will specify the layout at runtime.

A call to layout can take an array of strings. This is not the same as calling Layout multiple times - rather it's to support MonoRail's ability to organize the layout into several layers.

View Locations

Understanding IViewFolder and IViewFile

The Spark library declares two interfaces which provide the ability to locate view files and load their contents. There are very few methods and several implementations of typical view locations are provided, so in most cases the most you will need to do is provide additional configuration.

Spark file patterns

Spark will locate files using a very conventional pattern. These patterns will not change no matter which view folders are configured.

For controller views, the name of the controller and the name of the view are combined, like home\index.spark. If that file does not exist the name of the view is looked for in the Shared folder, like shared\index.spark.

For master layout templates, the name of the master will looked for in the Layouts folder, like layouts\application.spark. If that file does not exist it too will be looked for in the Shared folder, like shared\application.spark.

These patters will be looked for inside the view folder implementations. The view folders will never alter that part of the Spark file pattern, and the patterns won't affect the location of the view folders. This is very different from the behavior of the WebForms view engine which allows you to provide an array of customized virtual paths.

Sorry, but it was simply impossible to give the view engine full control of the entire path and remain compatible with the different frameworks and existing usage. The customized virtual paths are also incompatible with some of the specialized view folder sources.

Default view folder locations

In a Castle MonoRail application the configured IViewSourceLoader is used by the default IViewFolder implementation. The root location of the default IViewSourceLoader is ~/Views and can be changed with the monorail/viewEngines/@viewPathRoot configuration attribute.

In an Asp.Net MVC applications a view folder based on hosting environment's VirtualPathProvider is used. The root location within this will be ~/Views.

The behavior of those abstractions can be controlled in ways that aren't documented here; custom VirtualPathProviders is a large topic all on it's own. Whatever you do to the framework to alter how files are loaded should work with Spark as expected.

What is documented on this page is how to bring in additional IViewFolder implementations to extend the way spark files can be located.

Adding a view folder to config

The <spark> config section accepts a <views> element which can be used to add additional view folder locations.

<spark>
  <views>
    <add name="{any-unique-name}" 
        folderType="FileSystem|EmbeddedResource|VirtualPathProvider|Custom"
        type="{name, assembly of IViewFolder type}"
        constuctor-param-names="values"
        subfolder="{optional subfolder to target}"/>
  </views>
</spark>

For example:

<spark>
  <views>
    <add name="stuff" folderType="EmbeddedResource" assembly="MyExtraStuff" resourcePath="MyExtraStuff.Views"/>
  </views>
</spark>

Understanding the subfolder parameter

The subfolder parameter will make the contents for a view folder appear at a specific location in the logical views directory. In other words you're adding files, but you're adding them at a subdirectory.

For example if you create a ~/Masters directory in your web app you could expose it in place with the following:

<spark>
  <views>
    <add name="morestuff" folderType="VirtualPathProvider" subfolder="layouts" virtualBaseDir="~/Masters"/>
  </views>
</spark>

Any path can be the target for adding additional files. If no subfolder is provided the contents are added at the root.

Iron Python

About Iron Python

Iron Python is a dynamic language runtime (DLR) based implementation of Python. Spark scripting is based on assemblies in the 2.0 Release Candidate 1 binary zip. Other good resources are the Python documentation and the Python site itself.

The support for the DLR is very Asp.Net MVC centric. If there's an interest in expanded Castle MonoRail support, please speak up in the discussion group.

Creating a new project

A standard Asp.Net MVC web application is always a great starting place. First, add references to the following assemblies:

And the following from the IronPython release which are available in the Spark bin\dependencies folder. If you have problems with the these files from Codeplex try the ones in from the Spark distribution. You have the "works on my machine" guarantee.

Yeah, I know. It's a lot of assemblies. You're going to love the next part though, adding the Spark view engine to an Asp.Net MVC web application has been simplified.

Add the following to your Global Application_Start method:

using Spark.Web.Mvc.Scripting;
...
protected void Application_Start(object sender, EventArgs e)
{
    SparkPythonEngineStarter.RegisterViewEngine();
}

This will create a view engine and add it to the engines collection.

The Spark engine starter also has several utility methods for more advanced initialization. You can, for example, provide a settings object from code. You can also use the ISparkServicesContainer to provide specific implementations of different services used by the Spark engine. For example:

// this example avoids adding <spark> to web.config
var settings = new SparkSettings();
 
// change settings, like
settings
    .SetDebug(true)
    .SetPageBaseType(typeof(MyBasePage));
 
var container = SparkPythonEngineStarter.CreateContainer(settings);
 
// change services, like
container.SetServiceBuilder<IViewActivatorFactory>(
    c => new MyViewActivatorFactory());
 
SparkPythonEngineStarter.RegisterViewEngine(
    ViewEngines.Engines, container);

Adding views

Spark views for IronPython look and act much like they would for csharp. They have a .spark extension and exist in the normal Views\<controllername>\<viewname>.spark path.

One of the most obvious differences would be the lack of a need to declare <viewdata/>. Other than that it's pretty darn straightforward. You can execute code inline with #statement and output information with ${expression} and all of the <content>, <macro>, support for underscored partials, imports, etc. is unchanged.

#import clr
<p>Methods available from the clr module</p>