Ayende @ Rahien

Unnatural acts on source code

Misconceptions

I was explaining to a couple of team members about our stack (NHibernate, Castle, Boo, Rhino) and how the different pieces are hooked together.

When I got to talk to Boo, I explained that it is just like Brail, which we are using for the views. One of the guys then asked if Boo stands for Brail, Object Oriented

Commercial support for Castle Windsor, Castle MonoRail, NHibernate, etc

This post by Ben Scheirman is interesting. He points out that the MS.MVC stuff is targeted toward a different crowd than the one who is using MonoRail, it is targeted toward the corporate developers and the All-Microsoft Shops.

The question of support has been raised again, and it prompted this post. It seems that there isn't a lot of awareness that there are commercial support options for those tools.

  • Castle Stronghold is the obvious place for commercial support for Castle.
  • JBoss / RedHat are offering commercial support for NHibernate. JBoss no longer support NHibernate.
  • We!* (Hebrew site) is offering commercial support for NHibernate and Castle.
  • Mindscape is offering commercial support for Castle.

I am actually not very interested in getting support for those, so it is entirely possible (and likely) that I missed some. Most of the active members of the community are members of consultancies that are capable offering support, but those are the one that I am aware of.

* Full disclosure: I work there.

Multiple parents in Cascading Drop Downs in MonoRail

Apparently I am suffering from the Bug Duplication Syndrom. Shortly after posting about cascading drop downs in MonoRail, I realized that I have actually duplicated a bug that I have run into while using the cascading drop down extender in the ASP.Net Ajax toolkit. That is not a fixable bug, btw, and has to do with what happens when you have more than a single parent. You can see my post about this scenario here, but be aware that the solution in that post is vulnerable to race conditions, and I have finally declared that unpracticle to use.

At any rate, I have duplicate the inability to have multiply parents in my previous solution using MonoRail. And I decided that instead of fixing it in place, I'll show the steps neccesary to fix the problem. Basically, we want to update both the secondaries select and the tertiaries select, which means that our GetSecondaryProfressionsByPrimary.brailjs will now contains:

page.Replace( 'secondaries',
    Form.Select('secondaries',SecondaryProfessions,
        {@value: @Id, @text: @Name, @firstoption: res.Select})           
)
page.Replace( 'tertiaries',
    Form.Select('tertiaries',TertiaryProfessions,
        {@value: @Id, @text: @Name, @firstoption: res.Select})           
)
page.Call( @hookSecondariesChangeEvent );

And that the GetSecondaryProfressionsByPrimary action is now:

public void GetSecondaryProfressionsByPrimary(Guid id)
{
	PropertyBag["SecondaryProfessions"] =
		professionRepository.FindSecondariesByPrimaryIdOrAll(id);
	PropertyBag["TertiaryProfessions"] =
		professionRepository.FindTertiariesByPrimaryIdOrAll(id);
}

Oh, and that was it.

Cascading Drop Downs in MonoRail

I just had this need, and I decided to explore what I could do without referring to what other people has already done. There are probably better ways, but this one has tickled my fancy.

Anyway, the idea here is to display three levels of hierarchy in a profression. Here is the HTML:

<table>
	<tr>
		<td>
			${res.PrimaryProfession}
		</td>
		<td>
			${Form.Select("primaries",PrimaryProfessions, 
{@value: @Id, @text: @Name, @firstoption: res.Select}) } </td> <td> ${res.SecondaryProfession} </td> <td> ${Form.Select("secondaries",SecondaryProfessions,
{@value: @Id, @text: @Name, @firstoption: res.Select}) } </td> <td> ${res.TertiaryProfession} </td> <td> ${Form.Select("tertiaries",TertiaryProfessions,
{@value: @Id, @text: @Name, @firstoption: res.Select}) } </td> </tr> </table>

This is not very fancy, and the CSS guys would be all over me for using a table, but let us leave that aside. Basically, we render three select elements. Now, let us see the server side, shall we?

public void Index()
{
	PropertyBag["PrimaryProfessions"] =
		professionRepository.FindAllPrimaries();

	PropertyBag["SecondaryProfessions"] =
		professionRepository.FindAllSecondaries();

	PropertyBag["TertiaryProfessions"] =
		professionRepository.FindAllTertiaries();
}

The ProfressionRepository uses Repository<T> internally, I want to avoid using Repository<T> directly in my controllers. Now, we need to actually respond to change events on the client side:

$j(document).ready(function()
{
	$j('#primaries').change(function()
	{
		$j.ajax({ 
			url:'GetSecondaryProfressionsByPrimary.ashx',
			data: { id: $j('#primaries').val() },
			dataType: 'script'
		});
	});
	hookSecondariesChangeEvent();
});

function hookSecondariesChangeEvent()
{
	$j('#secondaries').change(function()
	{
		$j.ajax({ 
			url:'GetTertiaryProfressionsBySecondary.ashx',
			data: { id: $j('#secondaries').val() },
			dataType: 'script'
		});
	});
}

This is done using jQuery, to hook the events and issue Ajax requests to the server when they are done. Note that I am registering the seconderies change event in a separate function, it will be soon clear why.

Now, how does the server handles this?

public void GetSecondaryProfressionsByPrimary(Guid id)
{
	PropertyBag["SecondaryProfessions"] =
		professionRepository.FindSecondariesByPrimaryIdOrAll(id);
}

public void GetTertiaryProfressionsBySecondary(Guid id)
{
	PropertyBag["TertiaryProfessions"] =
		professionRepository.FindTertiariesBySecondaryIdOrAll(id);
}

So we just pass the parameters to the repository, which does all the work for us, in this case, getting the children if the id is valid, or all if it isn't. Now let us move to the relevant views, shall we? We are using BrailJS here, so here is GetSecondaryProfressionsByPrimary.brailjs:

page.Replace( 'secondaries', 
	Form.Select('secondaries',SecondaryProfessions, 
		{@value: @Id, @text: @Name, @firstoption: res.Select})			
)
page.Call( @hookSecondariesChangeEvent );

As you can see, I ask the page to replace the element secondaries with the newly rendered select element. I then acll the hookSecondariesChangeEvent function, since I actually replace the entire element, and not just its content. I am doing this because I don't feel like extracting the part that renders just the options from the Form.Select() method, but I will probably will the next time I need it.

The GetTertiaryProfressionsBySecondary.brailjs is actually simpler:

page.Replace( 'tertiaries', 
	Form.Select('tertiaries',TertiaryProfessions, 
		{@value: @Id, @text: @Name, @firstoption: res.Select})			
)

Again, there are probably betters way, but this is the one I just came up with.

Handling javascript localization in Mono Rail

I run into the issue of having to alert the user of some error, and it brought home the fact that English & Hebrew are not easy to mix. In fact, code such as this is consider a very bad sign, usually code that you don't really want to touch, see or believe in:

image

So, we need some sort of a way to externalize those strings, even if the only language that this application is going to use is Hebrew, simply because of the pain of mixing the two together.

After thinking about it for a while, I decided to create this view (JavascriptResources/index.brail):

<%
for de in controller.Resources:
	resourceName = de.Key
%>
	var ${resourceName} = {
	<% for item in GetParameter(resourceName): %>
		${item.Key}: '${item.Value.Replace("'","\\'") }',
	<% end %>
		Empty: ''
	};
<%
end
%>

And here is the definition of the controller:

[Resource("Processes", "MyApp.Resources.Javascript.Processes")]
[Resource("Errors", "MyApp.Resources.Javascript.Errors")]
[Resource("Messages", "MyApp.Resources.Javascript.Messages")]
public class JavascriptResourcesController : Controller
{
	public void Index()
	{
		
	}
}

But what the hell does this do, anyway? Not much, actually, but it will iterate over all the registered resources for the controller, and output something that looks like this for each of those resources:

var Processes = {
 	EmploymentType: 'Employment Type',
	Details: 'Details',
	Dates: 'The dates',
	Empty: ''
};

The nice thing is that I can now write: alert(Errors.RecursiveError);*. As a side affect (as I said, the application is just in Hebrew so I don't care about that much), I also get the usual benefits of localization.

* You have to read Hebrew to get the joke, I am afraid.

Redefining If

I had time today to sit on several Brail bugs. I keep getting more and more and more amazed by the power that Boo is giving me.

In order to fix some of those bugs, I had to literally change the meaning of the if statement. (I defined my own nullable propagator, which I had to implement deeply into the language).

Damn, I love this language.

Oh, and FYI, if 2+2 == 4 will not do the expected thing anymore :-) 

Refactoring MonoRail Views

imageAs I mentioned, I build a very quick & dirty solution to display a collection of scheduled task descriptions. The end result looked like this:

image

This works, and it should start producing value as of next week, but I didn't really like the way I built it.

Here is the original view code:

<table cellspacing="0" cellpadding="0">
	<thead>
		<tr>
			<th>
				Name:</th>
			<th>
				Occurances</th>
			<th>
				&nbsp;</th>
		</tr>
	</thead>
	<tbody>
		<% for task in tasks: %>
		<tr>
			<td>
			${Text.PascalCaseToWord(task.Name)}
			</td>
			<td>
			Every ${task.OccuranceEvery}
			</td>
			<td>
			${Html.LinkTo("Execute", "ScheduledTasks", "Execute", task.FullName)}
			</td>
		</tr>
		<% end %>
	</tbody>
</table>

This work, it is simple and easy to understand, but it still bothered me. So I replaced it with this:

<% component SmartGrid, {@source: tasks}  %>

Well, that was much shorter, but the result was this...

image

I am cropping things, because it is a fairly long picture, but it should be clear that this is not a really nice UI to use.

This was my second attempt;

<% component SmartGrid, {@source: tasks, @columns: [@Name, @OccuranceEvery] }  %>

And it produced this:

image

Better, but not really that much, let us try to have nicer names there, shall we?

<% 
component SmartGrid, {@source: tasks, @columns: [@Name, @OccuranceEvery] }:  
	section Name:
	%>
	<td>${Text.PascalCaseToWord(value)}</td>
	<%
	end
end
%>

And this produced:

image

That is much better on the name side, but we still have the "Occurance Every" column to fix...

<% 
component SmartGrid, {@source: tasks, @columns: [@Name, @OccuranceEvery] }:  
	section OccuranceEveryHeader:
	%>
	<th>Occurances</th>
	<%
	end
	section Name:
	%>
	<td>${Text.PascalCaseToWord(value)}</td>
	<%
	end
	section OccuranceEvery:
	%>
	<td>Every ${value}</td>
	<%
	end
end
%>

With the result being:

image

One last thing that we have left is the additional column at the end, we can manage it like this:

<% 
component SmartGrid, {@source: tasks, @columns: [@Name, @OccuranceEvery] }:  
	section OccuranceEveryHeader:
	%>
	<th>Occurances</th>
	<%
	end
	section MoreHeader:
	%>
	<th></th>
	<%
	end
	section Name:
	%>
	<td>${Text.PascalCaseToWord(value)}</td>
	<%
	end
	section OccuranceEvery:
	%>
	<td>Every ${value}</td>
	<%
	end
	section More:
	%>
	<td>${Html.LinkTo("Execute", "ScheduledTasks", "Execute", item.FullName)}</td>
	<%
	end
end
%>

So here is the final result:

image

Now that I did that, I am looking at both pieces of code and wondering:

  • What is the fuss about, anyway?
  • Which of those versions is more readable?

Granted, this is a fairly specialized case, but in terms of LoC, the second approach is actually longer, and the "major" benefit here is that I get less HTML in the view, but that is not a really major consideration.

The SmartGrid would produce a pager if needed, but that about it with regards to the differences in their abilities.

Brail: Null propagation

Here is a small, but interesting tidbit. Yesterday I have finally sat down and documented a lot of the changes that I made in Brail recently, I was surprised to see how much I had to document:

  • Auto Imports
  • Strongly typed variables
  • Sections
  • The "?variableName" syntax
  • Symbols
  • Null Propagation

The last part is what I want talk about now. Brail is a .Net language, which means that something like this:

output user.Parent.Name

will raise NullReferenceException if the user's parent is null. That can be somewhat of a pain in many scenarios. Brail now has a better syntax for this:

output ?user.Parent.Name

 will ignore any null values that it encounter in the way. It is will either output the parent's name or nothing at all.

The small print:

This will work only for variables that you get from the controller via the PropertyBag or Flash. You can't use it on variables that you define in the view. However, that is rare enough that I don't think that it is going to be a problem.

Note that I didn't document the DSL support in Brail yet, that is going to wait until it is stabilized a bit.

Brail: DSL Support Information

Harris has more details about the internal implementation of Brail's DSL Support

Following the same line of thoughts, Alex has a similar implementation using C# 3.0. Personally, I think that Alex's implementation is cool, but it looks really similar to LISP, and that sort of scares me. I am currently reading about ANTLR, so it actually makes sense, in a weird way, except that I keep looking for the lower level implementation.

And as long as I am busy in link aggregation, Tom Opgenorth has posted a guide to Binsor, including a Getting Started example!

DSL Support for Brail

First things first, Harris Boyce III has done all the work, my sum contribution to this feature has included some wild cheering from the side lines.

That said, this is one cool feature. Let us explore it.

There are a lot of people who consider anything resembling <html> tags to be a mess, associate them with ASP Classic mistakes, and reject them out of hand. I think that this is a mistake, but I gave up changing the whole world overnight, now I have busy formulating three steps plans that takes a week...

At any rate, consider this output:

<html>
	<body>
		<table>
			<tr>
				<th>Names</th>
			</tr>
			<tr>
				<td>Ayende</td>
			</tr>
			<tr>
				<td>Rahien</td>
			</tr>
		</table>
	</body>
</html>

How would we generate this output using Brail? The classic approach is:

<html>
	<body>
		<?brail
                       component Grid, {"source": names}:
                         	section header:
                                    output "<tr><th>Names</th><tr>"
                               	end
                             	section item:
                        	    output "<tr><td>${item}</td><tr>"
                           	end
                        end
?> </body> </html>

This works, but it not really Jump-Up-And-Down-All-Excited code. And, of course, some people faint dead away from having to deal with raw HTML, "Give me my components or give me death!"

Now, here is the DSL way of doing it:

<?brail
dsl Html:
	body:
		component GridComponent, {"source" : names}:
			section header:
				tr:
					th:
						text "Names"
					end
				end
			end 
			
			section item:
				td:
					text item
				end
			end
		end
	end
end
?>

And this baby produces the same output as the previous one.

Now, you are probably aware that I am not really fond of raising the abstraction level needlessly, so why is this a good thing? Well, because it means that you can now do stuff like this:

dsl.Register(atom)
dsl:
   feed:
       title:
           div:
               text "some content"
           end
       end
   end
end

And it will be able to produce semantically correct ATOM feed. (To be exact, it would be if we had an AtomExtension implementation to the new IDslLanguageExtension interface, which we currently don't have).

There are some quirks in the implementation, but it is a very cool direction to go to.

For future reference: Generating XML from Brail

Right now Brail has no 1st class support for generating XML, like RoR's rxml. I just run into BooML, and it looks like a really great way to handle this issue. MonoRail doesn't (yet) has the notion of multiply DSLs, just standard templates and JS generation. Looks like that is the way I would go when I next need to support XML with MonoRail.

I knew there was a reason why I liked Boo so much :-)

Tags:

Published at

Brail's Extension Methods

On Castle Dev, Chris has asked the following question, how can you build the common "empty" template with Brail? NVelocity makes it very easy:

#foreach $item in $items

#each

      $item

#nodata

      No Data!

#end

But Brail makes it more cumbersome. There are solutions using view components, but they are too much, all too often. So, can we find an elegant solution to the problem?

As it turn out, yes (otherwise I probably wouldn't write this post :-) ). Brail is based on Boo, and Boo has extension methods... Another concept that Brail has is "Common Scripts", a set of helper methods that can be used across all the views in the application. Here is the script that I put in /Views/CommonScripts/extensions.brail:

[Boo.Lang.ExtensionAttribute]

static def IsEmpty(val as object) as bool:

      if val isa System.Collections.ICollection:

            return cast(System.Collections.ICollection,val).Count == 0

      end

      return true

end

Now you have extended all the objects in the application, so we can use it like this:

<%

    for item in items:

        output item

    end

    output "No Data" if items.IsEmpty

%>

I like that :-) The notion can be extended to more complex issues, naturally, such as ToXml extension method, etc. In fact, you can even forward calls to C# code, so that makes it even more powerful (I assume (and suggest against) that you aren't going to write a lot of code in the extensions).

Hibernating Rhinos - Episode #2 - Select * From MonoRail.Customers

I have just finished putting together the second episode of Hibernating Rhinos. This one tooks several days and a lot of effort to produce. The download page is here, where you can also download the first episode, talking about Rhino Mocks.

The screencast is basically taking implementing similar functionality in both Web Forms and MonoRail, while I talk about the differences between the two approaches. I spent quite a bit of time explaining most of what I do when I am writing MonoRail code, so I hope it would be clear. This episode is not meant to be an introduction to MonoRail, it is merely a show & tell episode.

I am also afraid that I fell into the common trap of Web Froms vs. MonoRail comparision, and I focused quite a bit of my time on the UI layer, instead of focusing more on the controllers side of things, which is more important.

  • The overall length: 1:14:32
  • Download size: 60Mb
  • The big sigh at the beginning? That was the fourth time that I recorded this episode!
  • I am showing code from 09:25 onward.
  • Fun part that you shouldn’t miss:
    It should fails... It doesn't fails... It should fails... It doesn't fails...
  • From 05:10 to 08:00  - Web Froms Rant (come to think about it, there are a lot of those J )

Am I talking too fast? Am I making any sense?

Errata: in talking about the view engines, there is a section that might looks like I am taking credit for building Boo, just to clarify, I am an avid user of Boo, but I can't claim any credit for it. What I meant was that I built Brail, which uses Boo.

Building View Components For MonoRail

While WebForms and MonoRail share the ability to refactor common UI elements into reusable components, the design decisions that should be considered for each of those is quite different. Build view components for MonoRail is much simpler than building controls for web forms, for one :-)

I talked in the past about the mechanics of implementing View Components, now I want to talk about several best practices that may be useful for developers building view components for MonoRail. For this discussion, I am going to give examples from the grid component for MonoRail, whose implementation can be found here and here. In what seems like the traditional manner for MonoRail code, the grid components comes in two flavors, GridComponent, that contains the basic functionality, and SmartGridComponent, that contains a lot more smarts and assumptions. There is nothing particularily interesting in this decision, it is just a way to split the responsabilities in a clear manner.

View Components are all about the UI, and I am going to talk about general components, not something specific for a single page/use-case. A lot of what I want to cover is about allow easy extensability and re-use of the component, which is not an issue if you are building it for a single use case only.

In order to understand view components, we first need to understand a bit about the view engines in MonoRail. The view engines (broadly) have the following concepts:

  • Template / View - will generate the HTML for a request. Similar to an aspx page.
  • Sub template / Sub view - is called from a parent template/view in order to handle some of the processing. Similar to an ascx control. However, a sub view is also a fully fledged view, so you can use it to render a page in one scenario, and embed it in another page in a second scenario (quite useful, by the way).
  • View Component - responsible to handle the rendering of a piece of HTML, similar to a server control. May contains sections (which can be required or optional).
  • Section - Resides inside a view components, allow to pass the view component a template that it can render at will. The best analogy from the web forms world are templates (<ItemTemplate> in a repeater, for instance).

Now that we have covered the concepts, let us move into the implementation. A grid is very common is web applications, and it is a very simple to build. You have a data source, and you simply want to generate the headers and rows tags accordingly.

A rudimentary implementation of that would give us a repeater, which can be as simple as:

public class RepeaterComponent : ViewComponent

{

      public override bool SupportsSection(string name)

      {

            return "item".Equals(name, StringComparison.CurrentCultureIgnoreCase);

      }

 

      public override void Initialize()

      {

            if(Context.HasSection("item")==false)

            {

                  throw new ArgumentException("RepeaterComponent must have an 'item' section");

            }

      }

 

      public override void Render()

      {

            IEnumerable source = ComponentParams["source"] as IEnumerable;

            if (source == null)

                  throw new ArgumentException(
                     
"RepeaterComponent must have an enumerable 'source' parameter.");

            foreach (object item in source)

            {

                  PropertyBag["item"] = item;

                  Context.RenderSection("item");

            }

      }

}

You can use this view component like this:

<%

component RepeaterComponent, {@source: customers}:

      section item:

            output item.CompantName

      end

end

%>

This should give you an idea about how the interaction between the view and the view component works. We pass arguments to the view component in the declaration, and we pass sections in the body. The view component can then access them via the ComponentParams variable and the Context, respectively. Note the foreach inside the Render() method, we assign the the variable "item" to the PropertyBag, which would expose it to the section, and then we render the section, which can now access the "item" variable that we just assigned.

I would never write such a component, because this is much simpler to understand, and does the same thing:

<%

for item in customers:

      output item.CompantName

end

%>

We need the repeater in WebForms, because there is no way to iterate over a parameter, nor easy way to pass data between the markup and the code.

The overall structure of a grid is similar to this:

<table> <!-- table start -->

      <tr><!-- header -->

            <th>Header 1</th>

            <th>Header 2</th>

      </tr>

      <tr><!-- data rows -->

            <td>column 1</td>

            <td>column 2</td>

      </tr>

</table><!-- table end -->

<!-- pagination -->

<b>first</b> | <b>prev</b> | <b>next</b> | <b>last</b>

I am sure that you already know of this, so why am I boring you with this? Well, in order to build a reusable piece of code, you need to allow customization of this basic structure. While you could build a component that would generate everything except the data rows, that would be of very limit use. Off the top of my head, I want to be able to control cell spacing and padding, the border and padding, the table CSS class, etc...

You can pass parameters to the component to control the rendering of the control, and indeed, this is the way that the GridView is using. On last count, the GridView has 85 properties, most of which can be used to control the output of the GridView. Trying to build that is going to take a long time, and it is going to be a very complex task. That doesn't really fit the MonoRail (or Castle in general) philosophy.

The Reflector output of the GridView goes well beyond 3,500 lines of code. I have built full blown systems that had less lines of code than that. (Just to compare, the entire Rhino Mocks code base now stands at about 7,900 Lines.

Let us take a look at the Render() method of the GridComponent, shall we?

public override void Render()

{

      IEnumerable source = ComponentParams["source"] as IEnumerable;

      if (source == null)

      {

            throw new ViewComponentException(

                  "The grid requires an IEnumerable parameter named 'source' ");

      }

 

      ShowStartTable();

      ShowHeader(source);

 

      ShowRows(source);

 

      ShowFooter();

      ShowEndTable();

 

      IPaginatedPage page = source as IPaginatedPage;

      if (page != null)

      {

            ShowPagination(page);

      }

}

We can see that we are following the same structure as above, but with methods name. Remember all the options I wanted to set on the <table> tag alone? How do I handle it with this component? I certainly can't give it up, and I most certainly don't want to start writing > 3,000 lines of code to cover each eventuallity.

Well, it turns out that there is another way to express what I want the <table> tag to look like. Are you ready?  Sure that you are ready?

Well, it turns out that I can expression what I want the <table> tag to look like using a brand new concept called HTML. Here is how I can do this:

<%

component SmartGridComponent, {@source: customers}:

      section tableStart:

%>

      <table cellpadding="2" cellspacing="0" style="border: dashed 2px red">

<%

      end

end

%>

This also shows inline HTML inside a section, but this isn't as important as the concept. We express what we want in the native language. We don't need some translation layer in between. This turn out to simplify quite a bit of my life.

How does the ShowStartTable() method looks like?

private void ShowStartTable()

{

      if (Context.HasSection("tablestart"))

      {

            Context.RenderSection("tablestart");

      }

      else

      {

            RenderText("<table id='grid'>");

      }

}

This approach, sensible defaults with the ease of overriding them, means that it is:

  • Extremely easy to build components (a few examples)
  • Very easy to use components

Gaining Some Smarts

Everything I said so far is possible in WebForms, it is just alien to the way Microsoft positioned WebForms. Now let us move from the realms of the obvious to realm of really cool stuff. We now move from talking about GridComponent to talking about SmartGridComponent. SmartGridComponent inherits from GridComponent and handles a lot of the details of renderring the UI. Mostly, it moves the level that we need to handle from the entire grid to single properties of the object. Think about it like turning AutoGenerateColumns to true in the GridView, except that it is not like that at all ;-)

A View Component is free to define what sections it can support, and there is not compile time limitation. Using this tidbit, we can start doing more interesting things with convention over configuration. Let us say that I want to display a list of customers, and I want to customize the header of the customer id column in some manner. I can do it like this:

<%

component SmartGridComponent, {@source: customers}:

      section customerIdHeader:

            output "<th>Id</th>"

      end

end

%>

By specifying a section with the property name postfixed with "Header", I can override the renderring of this column with my own code. This is a simple example of replacing the text, but I could put anything there at all, including complex HTML or calling a sub view or another view compoennt.

Under the same principal, I can override the rendering of the column itself:

<%

component SmartGridComponent, {@source: customers}:

      section customerId:

            output "<td><b>${item}</b></td>"

      end

end

%>

Again, this just shows the customer id in bold, but anything it possible here. Let us see what we need to do to make this happen, shall we?

Here is a small section from the code that is responsible for rendering the headers of the grid:

foreach (PropertyInfo property in this.properties)

{

      string overrideSection = property.Name + "Header";

      if (Context.HasSection(overrideSection))

      {

            Context.RenderSection(overrideSection);

            continue;

      }

      RenderText("<th class='grid_header'>");

      RenderText(SplitPascalCase(property.Name));

      RenderText("</th>");

}

We check the existance of an overriding section, and defer to it if it exists. The same goes for the rendering the column itself:

foreach (PropertyInfo property in properties)

{

      if (Context.HasSection(property.Name))

      {

            PropertyBag["item"] = property.GetValue(item, null);

            Context.RenderSection(property.Name);

            continue;

      }

      RenderText("<td>");

      object val = property.GetValue(item, null) ?? "";

      RenderText(val.ToString());

      RenderText("</td>");

}

The technical details are very simple, which is very good, but the power and flexibility that they bring is quite amazing.

The combination of convention over configuration, and the ease of overriding the defaults is a key stregth to building complex UI easily. It is important to note that when we actually build the UI, we never really leave the realm of HTML, what we write is very close to what would be sent to the browser. This give a lot more control over the final output, but not at the expense of having to deal with additional complexity.

I would like to end with a quote (via Avery):

Simplicity is about subtracting the obvious, and adding the meaningful.

 

Brail: Complex Expressions

The technical details are going to be of interest to a limited number of people, but it comes down to a design choice that I made when I first wrote Brail. It has to do with how the text is outputted into the compiler, and some tricky bits that can happen there because Boo's compiler understand that "this is ${code}" and can turn it into the correct string, based on the value of the variable "code".

That was fine until I realized that I can do more than a simple variable substitution in there. You can call anything that returns a value, which is very valuable for code such as:

${Ajax.InstallScripts()}

The problem started when the code inside the expression got even more complicated. No longer a single method call, but method calls that wanted to pass string parameters (with " in the middle), or maybe they wanted to use inline expressions as well. I always work-around those issues, but today I got a piece of code that was too pretty to give up on, so I fixed the Brail pre-parser (thrice, until I got it right) so it would output the correct code to the compiler.

What this basically boils down to is that this is now valid code:

${Html.LinkToAttributed( title ,@customers, @list, {
      @onclick: "paginate(${pageIndex});" 
    }
)}

If you are interested, the output of this is:

<

a href="/customers/list.rails" onclick="paginate(5);">first </a>

I like it.

Having Fun with SmartGridComponent

I am preparing to my Dev Teach talks, and I wanted to show a cool view component. Here is a sample of how it looks like now:

<%

component SmartGridComponent, {

      @source : customers,

      @displayAddress: false,

      @displayFax: false,

      @displayPhone: false

}

%>

I want to ignore some of the properties of the customer entity. This demonstrate both symbols in Brail (hmm, sounds like something else altogether :-) ) and using Convention over Configuration for this.

And here is a bit more complex example, showing how to display just the items that we want, in the order that we want:

<%

component SmartGridComponent, {

       @source : customers,

       @columns: [

              @customerId,

              @companyName,

              @contactName,

              @contactTitle

       ]

}

%>

And here is how I can override spesific behavior for the grid:

<%

component SmartGridComponent, {

       @source : customers,

       @columns: [

              @customerId,

              @companyName,

              @contactName,

              @contactTitle

       ]}:

       section customerIdHeader:

              output "<th>Id</th>"

       end

       section customerID:

       %>

       <td>${Html.LinkTo(item, "Customers", "ListOrder", item)}</td>

       <%

       end

end

%>

Here is how the last one looks like, by the way:

(Image from clipboard).png

The reason for Ajax Generators

In my last post, I show how to use the Ajax Generators, but I didn't really explain why it is good to learn yet another way to do Ajax. Here is the example that I used:

page.ReplaceHtml('paginatedGrid', {@partial : 'faq/grid' })
page.VisualEffect('Highlight', 'paginatedGrid')

This will send the following to the client:

try 
{
 Element.update("paginatedGrid","gridContent");
 new Effect.Highlight('paginatedGrid', {});
}
catch(e)
{
 alert('JS error ' + e.toString());
 alert('Generated content: \nElement.update(\"paginatedGrid\",\"gridContent\");\nnew Effect.Highlight(\'paginatedGrid\', {});\n');
}

(Replace gridContent with a lot of HTML, of course). Okay, so I get nicer error handling if I have an error (always a good thing), but I don't see the value of it. Let us consider the case where we don't have something as simple as straight rendering to the client, shall we?

A good example will be in a shopping cart, if the total price of the items reach above a certain threshold, the user should get free shipping. Now that we have a conditional, the code isn't this simple to write. Add about 15 lines of JS for each condition, and you are in a world of hurt.

This is where the generators come real handy, you aren't manipulating text, but objects, which means that you get to take advantage of advance programming language constructs like conditionals and looping. Let us see how we can implement the shopping cart functionality. We will start with the logic, which in this case is sitting in the controller (probably not a good idea for non-demo scenarios):

private string[] products = { "Milk", "Honey", "Pie" };

 

public void Index()

{

       PropertyBag["cart"] = Cart;

       PropertyBag["products"] = products;

}

 

public void AddItem(int id)

{

       PropertyBag["alreadyHadfreeShipping"] = Cart.Count > 4;

       Cart.Add(products[id]);

       PropertyBag["produdct"] = products[id];

       PropertyBag["freeShipping"] = Cart.Count > 4;

       RenderView("AddItem.brailjs");

}

 

public void ClearAllItems()

{

       PropertyBag["alreadyHadfreeShipping"] = Cart.Count > 4;

       InitCart();

       RenderView("ClearAllItems.brailjs");

}

Cart is a property that exposed an ArrayList saved to the user session. You can see that we are passing the decisions to the views, to act upon. There is a business rules that says that if you buy more than 4 items you get free shipping, and that is what is going on here. No dealing with the UI at all.

Now, let us see the views, we will start with the main one, index.brail

<?brail import Boo.Lang.Builtins ?>

<h2>Demo shopping cart:</h2>

<ul>

       <?brail for i in range(products.Length):?>

              <li>

                      ${ajax.LinkToRemote(products[i],'addItem.rails?id='+i,{})}

              </li>

       <?brail end ?>

</ul>

<div id="freeShipping" style="display: none;">

       <b>You are elegible for free shipping!</b>

</div>

<h2>Purchased Products:</h2>

<p>

       ${ajax.LinkToRemote('Clear','clearAllItems.rails',{})}

</p>

<ul id="products">

       <?brail for product in cart: ?>

              <li>${product}</li>

       <?brail end ?>

</ul>

The first line shows an interesting trick, by default Brail removes the builtin namespace, because common names such as list and date exist there. Here, I want to use the range function, so I just import it and continue as usual. Beyond that, there is nNothing particulary interesting about this view, I think.

Let us see the addItem.brailjs file first, it handles updating the page to reflect the new item in the cart:

page.InsertHtml('bottom', 'products',"<li>${produdct}</li>")

if freeShipping:

      page.Show('freeShipping')

     

      if not alreadyHadfreeShipping:     

            page.VisualEffect('Highlight','freeShipping')

      end

end

As you can see, it contains conditional logic, but this logic is strictly UI focused. If the user is applicable for free shipping show it, if this is the first time, highlight the fact that they got a free shipping, so they will notice.

I would like to see similar functionality done the other way, but I do not think that I would care to write it...

Using generators gives you code that is highly maintainable in the future, and doesn't move business logic to the UI or UI logic to the business logic like often happen (by neccesaity) using other methods.

MonoRail & Ajax Generators

I mentioned that MonoRail recently aquired Ajax Generators. The generators a are different from normal templates, because they do not generate html, but rather modify the page that was already rendered. It will be easier to exaplain with an example. Let us take the simple example of paging a grid without a full post back, shall we?  By the way, this turned out to be a lot more focused at the WebForms than I intended, more on that later.

Here is the backend of this demo:

public void Index(bool isAjax)

{

       PropertyBag["subjects"] = PaginationHelper.CreateCachedPagination(

              this, Action,15,delegate

              {

                     return new List<Subject>(Repository<Subject>.FindAll()).ToArray();

              });

       if(isAjax)

              RenderView("index.brailjs");

}

Simple, get the items from the database and store it in the cache, as well as passing it to the view. For now, please ignore the last two lines...

Now, let us a look at our view, shall we? There is a layout that I will not touch now (if you don't understand MonoRail terms, think about it as a MasterPage), but let us focus on the action's view index.brail:

<script type='text/javascript'>

       function error(e)

       {

              alert(e);

       }

       function paging(index)

      {

           var url = '/faq/index.rails';

           var pars = 'page=' + index +'&isAjax=true';       

           new Ajax.Request(url,{method: 'get', evalScripts: true, parameters: pars, onException: error});       

   

       }

</script>

<h2>Subjects for questions:</h2>

<div id='paginatedGrid'>

       <?brail OutputSubView('grid') ?>

</div>

What we have here is merely a bit of javascript and a div to put the gird in. We also use OutputSubView for the grid (again, if WebForms terms, this is something like a UserControl). The most complex part is the grid.brail view itself:

<?brail

component GridComponent, {'source':subjects}:

       section header:

?>

       <th id='header'>Id</th>

       <th id='header'>Name</th>

       <th id='header'>Browse</th>

       <?brail

       end

       section item:

?>

       <tr id='item'>

              <td>${item.Id}</td>

              <td>${item.Name}</td>

              <td>${ HtmlHelper.LinkTo('Browse','faq','showQuestions', item.Id) }</td>

       </tr>

<?brail

       end

       section alternateItem:

?>

       <tr id='alternateItem'>

              <td>${item.Id}</td>

              <td>${item.Name}</td>

              <td>${ HtmlHelper.LinkTo('Browse','faq','showQuestions', item.Id) }</td>

       </tr>

 <?brail

       end

       section link:

?>

       <a href='/faq/index.rails?page=${pageIndex}'

          onclick='paging(${pageIndex});return false;'>${title}</a>

       <?brail

       end

end

 ?>

The GridComponent is somewhere in the middle between GridView and Repeater, since it can do almost everything on its own, but let you override (for isntnace, alternateItem) parts of the rendering in place. Please pay some attention to the last section, the link. This is a pagination link that can be used to override the default behavior of moving to the next page. I am overriding it with a

Now that we have established the page, we can play with it a bit, and see that it is working great. Except that there is still the last piece of the puzzle, paging without refreshing the full page.

Go check the javascript in index.brail, notice that it is making an Ajax request to /faq/index.rails ? And that it is passing isAjax=true? No go and check the Index() method, and look at the last two lines. If this is an Ajax request, we use a different view for the output, index.brailjs;

page.ReplaceHtml('paginatedGrid', {@partial : 'faq/grid' })
page.VisualEffect('Highlight', 'paginatedGrid')

Now, what is going on here... this certianly doesn't look like any ajax framework I have ever seen...

Remember that I spoke about the ajax generators? This is it. You may think about is as a small DSL for generating the javascript to modify the page. What we have here is a call to ReplaceHtml, which will replace the content of the paginatedGrid element with something else, usually a string. But, in this case, we pass in a @partial (@symbol is identical to :symbol in Ruby, but this is a temporary syntax at the moment, I want to get rid of the {} ), which means that we ask to replace the element with the results of running the grid template.  The second line is just to provide some feedback to the user that something has changed. So, in essense, it took 4 lines of code to

But why invent a whole new syntax just to use ajax? Isn't Javascript enough? More on that next post...

Brail "new" feature, component sections...

I call it a "new" feature because it is something that the NVelocity view engine supports for quite some time. The basic idea is to allow this type of code:

<?brail

component BasicGridComponent, {"source": contacts}:

  section header:

?>

    <th>EMail</th>

    <th>Phone</th>

<?brail

  end

  section item:

?>

    <td>${item.Email}</td>

    <td>${item.Phone}</td>

<?brail

  end

end

?>

This feature open up some very interesting possibilities with View Components...

Brail on C#

For the last two weeks or so I have been stealing little pieces of time to an annoying chore. Moving Brail's engine from Boo to C#.

The reasons for this move are very compelling, allowing more people to take an active role in Brail's development. I am not always avialable, and there is always the bus factor :-). In general, I consider it a good thing to lower the barrier to participate in any open source project. Working with Boo isn't harder (I would say the other way around, actually) than working with C#, but a lot more people know C#, and are comportable using it.

So, as of few hours ago, Brail engine is written using C#. Of course, this is a significant change, so I would like to get as much feedback as possible.

Note: This doesn't change the views themselves, those will work as is, with no modification needed. The change affected the langauge the view engine was coded with, and that is all.

Have fun...

Tags:

Published at

Building Applications Using Castle RC2: Part II

Last time I left we had a working data access layer, based on Active Record, as well as a scaffolding for editing users. We used TDD to drive the functionality of the application, and I intend to keep doing it this way while developing the functionality of the site itself.

Before we start by building the initial list of tests, let us consider what we want to achieve...

We want to have a forum system, so diplaying the forums, adding and browsing messages is very important, but managing users / forums is not a high priority, we can leave the users management on the scaffolding, and build the scaffolding for forums as well. Here is what we need to do to build the scaffolding for Forum:

[Layout("default"), Rescue("generalerror"), Scaffolding(typeof(Forum))]

public class ForumController : SmartDispatcherController

{

}

And now we can access it at /forum/list.rails

One thing that I forgot to do before is to add the ActiveRecord HttpModule, which goes into the <httpModules> section in <system.web> element, like this:

<add name="active-record"

        type="Castle.ActiveRecord.Framework.SessionScopeWebModule, Castle.ActiveRecord" />

Now, let us see what kind of scenarios we want to support:

  • The homepage should display a list of all the forums, by name and id, with links to the forum page.
  • There should be a text box where I can enter a forum id and go directly to it.
  • A forum should display ten root messages, and be pagable.
  • Can add a message to forum.
  • Can reply to message.
  • Messages appear in hierarchy.

When building tests for a MonoRail application, we have two options, we can do normal tests, which involve testing the data sent from the controller to the view, or integration tests, where we assert on the HTML output from the view. Let us start with the first test, asserting that the homepage controller pass the view a list of all forums. We start by building the test data in the test setup. But in order to do this, we need to setup Active Record.

In the previous installment, we already setup Active Record for the MonoRail application, but now we are calling Active Record from the tests, and that is in a different AppDomain.

A word about the way MonoRail tests work.
A MonoRail test basically host the ASP.Net runtime in a different AppDomain, which means that anything that goes between your test and the application is cross AppDomain boundaries.
This is why we need to setup Active Record in both the tests and the MonoRail application, they are on different app domains.
One word of caution, right now we have setup the MonoRail application so that it would recreate the database on startup, but we setup the database on test setup. This means that we need to remove the ActiveRecordStart.CreateSchema() from Application_OnStart.

I decided to do the setup in a test base class, like this:

public class AbstractMRWithARTestCase : AbstractMRTestCase

{

       [SetUp]

       public override void Initialize()

       {

              ActiveRecordStarter.ResetInitializationFlag();

              IConfigurationSource source = ActiveRecordSectionHandler.Instance;

              ActiveRecordStarter.Initialize(System.Reflection.Assembly.Load("Castle.Forums.Model"), source);

              ActiveRecordStarter.CreateSchema();

              base.Initialize();

       }

}

This initialize the framework, and then rebuild the database, so the tests are isolated.

Now, let us see the real setup code:

[SetUp]
public override void Initialize()
{
       base.Initialize();

       User user = new User("Ayende", "Ayende@example.org");

       user.SetPassword("Scott/Tiger");

       user.Create();

 

       Forum forum1 = new Forum("My Forum 1", user);

       forum1.Create();

 

       Forum forum2 = new Forum("My Forum 2", user);

       forum2.Create();

}

Now, after all this, we can start creating the first real test, it goes like this:

[Test]

public void IndexAction_PassAllForumsToView()

{

       DoGet("home/index.rails");

 

       AssertPropertyBagContains("forums");

       Forum[] forums = (Forum[])Response.PropertyBag["forums"];

       Assert.AreEqual(2, forums.Length);

 

}

What does this do? Well, it asserts that the controller has passed two forums to the view. Running this test now will fail with this erro:

Entry forums was not on PropertyBag

We have a red light! Let us make it green. We can make this test pass using this code:

public void Index()

{

       PropertyBag["forums"] = Forum.FindAll();

}

We run the code, and this time it is failing with another error, complaining that Forum is not a serializable type. This is an annoying side affect of the need to run ASP.Net in another AppDomain, everything that goes between the domains has to be serializable. This is an annoying limitation, and we are thinking about ways to fix this, but currently there isn't a good way to do this, since the ASP.Net runtime creates its own AppDomain, and that is hard to change. So, we need to put [Serializable] on Forum, User and Message classes. I usually don't like artificial test requirements, but this isn't an onerous one, and there are valid reasons to want to do this anyway.

After making the model entities serializable, the test pass! Of course, if we go to the page, it doesn't show anything, we need another test, one that would assert that the view is displaying the fourms:

[Test]

public void IndexView_DisplayAllForums()

{

       DoGet("home/index.rails");

      

       AssertReplyContains("My Forum 1");

       AssertReplyContains("My Forum 2");

}

Run and see it fails... Let us make this work. Go to the view, which is on the Castle.Forums.Web project, "Views\Home\index.boo". We need to output the forums out, so let us do it simple as:

<h2>Forums List</h2>

<ul>

      <?brail for forum in forums: ?>

      <li>${forum.Id} - ${forum.Name}</li>

      <?brail end ?>

</ul>

Run the test again to make sure that it passes, and let us move on. We don't have a list to each forum's page, but this is because it doesn't exists yet. So let us create it. I think that /forum/1/index.rails is a good place to put it, so let us create a ForumControllerTestCase class, for testing the new functionality. In the setup (which I will not show) we merely create a user, a forum and 50 messages (each with 2 reply messages). The root messages has names like Root 1 and Root 30, and their children names like Child 1.3 or Child 30.2, this is just so we can assert on them with ease.

Let us start by writing the test:

[Test]

public void ForumController_PassAllRootMessagesToView()

{

       DoGet(string.Format("forum/{0}/index.rails", forumId));

       AssertSuccess();

      

       AssertPropertyBagContains("messages");

       Message[] msgs = (Message[])Response.PropertyBag["messages"];

       Assert.AreEqual(50,  msgs.Length);

}

Very similar to what we did before. Notice that forumId is the id of the forum we create previously. Now, to get the to pass we need to do quite a bit.

First, let us add an index method to ForumController:

public void Index(int forumId)

{

       Forum forum = Forum.Find(forumId);

       PropertyBag["messages"] = Message.FindAll(Expression.Eq("Forum", forum),

              Expression.IsNull("RootParent"));

}

Get the forum, and then get all the message for the forum, very simple. Note that this method is called by the MonoRail framework, and it has a parameter. This parameter is parsed by MonoRail from the request parameters (both query string and forms parameters are considered).

Considerring that the URL that we want to use has the format of "/forum/1/index.rails", how can MonoRail understand that it should use the 1 as the parameter to the index? We can tell it so with the use of routing rules. A routing rule is merely a small piece of regex that transfrom one representation of the request URL to another.

You can configure routing using the <monorail>/<routing> element, by adding a rule:

<rule>

       <pattern>/forum/(\d+)/(.*)$</pattern>

       <replace><![CDATA[ /forum/$2?forumId=$1 ]]></replace>

</rule>

We are simple replacing every reference to /forum/number/page with /forum/page?id=number. Now, MonoRail can translate the "folder" into the correct parameter. We are not done yet, we still need to create a view to show a forum's listing. We need to create a a subdirectory under views "Forum", which will contain all the forum's views. Then, add an empty file called index.boo to it.

Now the test passes, but we have nothing in the view. Also, notice that we aren't doing any paging yet.

Here is a test that assert that the page shows something, and that it is using paging:

[Test]

public void ForumsView_ShowsOnlyFirstRootMessages()

{

       DoGet(string.Format("forum/{0}/index.rails", forumId));

      

       AssertReplyContains("Root 1");

       AssertReplyContains("Root 9");

 

       AssertReplyDoesNotContain("Root 11");//that should be paged

}

MonoRail has a feature called CachedPagination, which we are about to make use of. It means that we are going to need to change the controller code, and since the pagination is not serializable, so we need to restructure our tests first. Let us start with the code this time:

public void Index(int forumId)

{

       string paginationCacheKey = string.Format("forum.{0}.messages", forumId);

       DataObtentionDelegate getMessageForForum = delegate

       {

              Forum forum = Forum.Find(forumId);

              return Message.FindAll(Expression.Eq("Forum", forum), Expression.IsNull("RootParent"));

       };

       IPaginatedPage cachedPagination = PaginationHelper.CreateCachedPagination(paginationCacheKey,10
                                                        getMessageForForum);

       PropertyBag["messages"] =  cachedPagination;

       Flash["messages.TotalItems"] = cachedPagination.TotalItems;

}

What is going on? We create a cache key for the results, this should help reduce the amount of queries that we have in a big site, not really neccecary in our case, but it is so elegant that I can't help it ;-) We pass a delegate to the method, which will be called when there is a cache miss to get the data. Then we put the resulting pagination and the total item count into it. Notice that I don't need to specify which page I was working with, that is the responsability of the PaginationHelper.

I put the total count into the Flash so I could still test that I am getting the results from the controller. Since pagination is not serializble, I can no longer access PropertyBag, it can't pass the AppDomain boundary. The first test now looks like this:

[Test]

public void ForumController_PassAllRootMessagesToView()

{

       DoGet(string.Format("forum/{0}/index.rails", forumId));

       AssertSuccess();

 

       AssertFlashContains("messages.TotalItems");

       int msgs = (int)Response.Flash["messages.TotalItems"];

       Assert.AreEqual(50,  msgs);

}

Moving on to writing the view itself, it looks like this:

<h2>Messages:</h2>

<table>

      <tr>

            <td>Title:</td>

            <td>Author:</td>

      </tr>

      <?brail for msg in messages: ?>

      <tr>

            <td>

                  ${msg.Title}

            </td>

            <td>

                  ${msg.Author.Name}

            </td>

      </tr>

      <?brail end ?>

</table>

Now, when we run the tests, everything pass. But we need to hanle paging as well. Currently we are just showing the first 10 rows. Here is a test that says we can page:

[Test]

public void ForumsView_HasPaging_AndChangeDataOnPaging()

{

       DoGet(string.Format("forum/{0}/index.rails", forumId));

      

       //has next link

       AssertReplyContains("<a href=\"/forum/index.rails?page=2&amp;forumId=1&amp;\" >next</a>");

 

       DoGet(string.Format("forum/{0}/index.rails", forumId), "page=3");

      

       AssertReplyContains("Root 20");

       AssertReplyContains("Root 29");

 

       AssertReplyDoesNotContain("Root 19");

       AssertReplyDoesNotContain("Root 31");

}

This is fairly brute force way of doing this, but I can't really this of a better way. Also, notice the way of passing query strings using DoGet(), if you try to concat strings to make a URL, it won't work. Anyway, we need paging, we are likely going to want to do this in more than one view since paging is a cross cutting concerns. We are going to utilize the CommonScripts functionality of Brail to get this. Add a subfolder called CommonScripts to the Views folder, and create a file called Paging.boo:

import Castle.MonoRail.Framework.Helpers

import System.Text

import System.Collections

 

def Pagination(paginationHelper as PaginationHelper,

      items as IPaginatedPage,

      htmlAttributes as IDictionary,

      querySting as IDictionary):

      output = StringBuilder()

      output.Append("""<div class="pagination">

            <table width="100%"

                     border="0">

                  <tr>

                        <td>Showing ${items.FirstItem} - ${items.LastItem} of ${items.TotalItems}</td>

                        <td align="right">""")

      if items.HasFirst:

            output.Append( paginationHelper.CreatePageLink( 1, "first",htmlAttributes, querySting )  )

      else:

            output.Append( "first" )

      end

      output.Append( " | " )

      if items.HasPrevious:

            output.Append( paginationHelper.CreatePageLink( items.PreviousIndex, "prev",htmlAttributes, querySting )  )

      else:

            output.Append( "prev" )

      end

      output.Append( " | " )

      if items.HasNext:

            output.Append( paginationHelper.CreatePageLink( items.NextIndex, "next",htmlAttributes, querySting ) )

      else:

            output.Append( "next" )

      end

      output.Append(  " | " )

      if items.HasLast:

            output.Append(  paginationHelper.CreatePageLink( items.LastIndex, "last",htmlAttributes, querySting ) )

      else:

            output.Append(  "last" )

      end

      output.Append( """</td>

            </tr>

      </table>

</div>""")

      return output.ToString()

end

This looks like a lot of code, but it isn't doing much, actually. Just outputing the pagination html. Notice that we can specify the query string and the html attributes. Now, we can just append this at the bottom of the views:

<?brail

output Pagination( PaginationHelper, messages, null, { "forumId" : forumId} )

?>

Now run the tests, assure that they are passing, and then browse to the site and see what your easy work has gained you :-)

I'm not going to write more tests, by this time, you should be able to do this yourself, and they clutter the tutorial.
By this point, I think that I have shown how easy it is to write tests for MonoRail.

Now that we have a way of showing the forum's messages, we can add a link from the home page to the forum's page, and then we are done with the first scenario, this can be done as simply as:

<li>${forum.Id} - <a href="/forum/${forum.Id}/index.rails">${forum.Name}</a>

Now we finished with the first scenario, the second one calls for a text box that allows a quick access to the forum's page by its id. It turns out that we have to change only the view to make this happen, just adding this to /home/index.boo:

<div style="text-align: right;">

      <form action="/forum/index.rails">

            Go to forum #

            <input type="text" name="forumId"/>

            <input type="submit" value="Go!"/>

      </form>

</div>

How this works? Remember that the ForumController.Index() method accept an integer? This simply sends it to it. MonoRail doesn't care if how we pass the value, it does the Right Thing :-D

But, this raise the question of input validation. We now have input from the user, and they are very good in putting the wrong data just to spite me.

Looking at this, I can see three edge-cases:

  • Entering an id that doesn't belong to a forum.
  • Not entering anything.
  • Entering a non-numeric value.

Let us protect ourself from the first case:

public void Index(int forumId)

{

       Forum forum = Forum.TryFind(forumId);

       if(forum==null)

       {

              PropertyBag["ForumId"] = forumId;

              RenderView("NoSuchForum");

              return;

       }

       string paginationCacheKey = string.Format("forum.{0}.messages", forumId);

       DataObtentionDelegate getMessageForForum = delegate

       {

              return Message.FindAll(Expression.Eq("Forum", forum), Expression.IsNull("RootParent"));

       };

       IPaginatedPage cachedPagination = PaginationHelper.CreateCachedPagination(paginationCacheKey,10,

                                                      getMessageForForum);

       PropertyBag["messages"] =  cachedPagination;

       Flash["messages.TotalItems"] = cachedPagination.TotalItems;

}

We simply add a check to ensure that the forum exists. In both cases, we show a view that simply says: (Views\Forum\NoSuchForum.boo)

<h2>No Such Forum</h2>

<p>

Could not find a forum with id ${ForumId}

</p>

This still doesn't handle the last case, where the user pass a non-numeric data. How would we handle this? We can try to catch an error from the framework when it is trying to convert the value to integer, and handle that, but that is not an elegant solution in my opinion. A much more elegant solution is to think about it this way, an invalid integer value is a string, and MonoRail actually supports overloading, so... here is how we handle the other two cases:

public void Index(string forumId)

{

       if (string.IsNullOrEmpty(forumId))

              PropertyBag["ForumId"] = "null / empty";

       else

              PropertyBag["ForumId"] = forumId;

       RenderView("NoSuchForum");

}

Now MonoRail will do the right thing, and call our Index(int) if the value can be converted to integer, and call Index(string) if it can't. We moved our error handling outside the method, not it is much easier to read and work with. Also, we are reusing the UI, the NoSuchForum view is being used by two actions on the controller. That is something that is usually harder to do (in terms of the setup require to make the code reusable), isn't it?

A word of caution, this behavior is currently dependant on source code orderring, which I consider a bug. This mean that the Index(int) method should come before Index(string) method for it to be considered.

Forth scenario, we need to be able to add messages to a forum. Let us extend the forum/index view to handle this. I think that I want to use a drop of ajax for this, so we need to add this to Views\Layouts\default.boo, somewhere on the <head>:

${AjaxHelper.GetJavascriptFunctions()}

This makes sure that prototype is setup appropriately. Now, we can just append this at the end:

<p>

      ${ AjaxHelper.LinkToFunction('Post new message...', '$(newMsg).toggle()') }

</p>

<div id="newMsg"  class="postNewMsg" style="display: none;">

      <form method="POST">

            <table>

                  <tr>

                        <td>Title:</td>

                        <td><input name="msg.Title" type="text"/>

                        </td>

                  </tr>

                  <tr>

                        <td>Author Id:</td>

                        <td><input name="msg.Author.Id" type="text"/>

                        </td>

                  </tr>

                  <tr>

                        <td>Content:</td>

                        <td></td>

                  </tr>

                  <tr>

                        <td colspan="2">

                              <textarea name="msg.Content" rows="10" cols="50"></textarea>

                        </td>

                  </tr>

            </table>

            <input type="submit" value="Post new message"/>

      </form>

</div>

This just add a link that when press, will show the add new post form. I am asking the user for its ID, and that isn't really nice, but I don't want to get into authentication, that is not the purpose of this post. Here is the code that handles the actual saving of the message:

public void PostMsg([DataBind("msg")] Message msg)

{

       msg.Create();

       string paginationCacheKey = string.Format("forum.{0}.messages", msg.Forum.Id);

       Context.Cache.Remove(paginationCacheKey);

       CancelView();

       Response.Redirect(string.Format("/Forum/Index.rails?forumId={0}", msg.Forum.Id));

}

How did it do that? I hear you asking. Well, it is utilizing a very smart behavior on the side of MonoRail. If you look closely, you'll notice that I naming the input fields in the form (and the query string) with an object notation. "msg.Forum.Id", for instnace. Then, all I need to do is to tell MonoRail how it show build this type for me.

Note that you do need to ensure that the values are correct. Trying to input "abc" in the Author Id will fail, but those checks are fairly easy to do. And it puts you on the "we can see the results" path a lot quicker.

After creating the new message, I clear the cache for this forum, and redirect to the main page. So, we have only two more scenarios to go (That is assuming that you are still reading this, of course).

Let us start by showing how we can display hierarchical data. We need to get all the root messages, and all their children, and so on. But right now we don't have a way to get from a Message to its children, or from a root message to all its hierarchy. Seems like we have a problem in the model (let us call a meeting).

After thinking about it, I decided that the following properties need to be added:

[HasMany(typeof(Message), "Parent", "Message")]

public ISet Children

{

       get { return children; }

       set { children = value; }

}

 

[HasMany(typeof(Message), "RootParent", "Message")]

public ISet Hierarchy

{

       get { return hierarchy; }

       set { hierarchy = value; }

}

And, the Parent needs to be modified slightly:

[BelongsTo]

public Message Parent

{

       get { return parent; }

       set

       {

              parent = value;

              if(parent==null)

                     return;

              RootParent = parent.RootParent ?? parent;

       }

}

This is done to ensure that the RootParent is set properly in databinding scenarios. Now we can build the controller's action:

public void MessageDetails(int messageId)

{

      

       SimpleQuery<Message> simpleQuery = new SimpleQuery<Message>(

                     @"from Message msg left join fetch msg.Hierarchy where msg.Id = ?",

               messageId);

       Message[] messages = simpleQuery.Execute();

       if(messages.Length==0)

       {

              PropertyBag["MessageId"] = messageId;

              RenderView("NoSuchMsg");

              return;

       }

       PropertyBag["message"] = messages[0];

}

Now you may begin to see what is the value in the RootParent, it allows us to pull all the message hierarchy efficently. We get it from the database, check that the value exists in the database, and then populate the PropertyBag with the value. Now, to the view, which is a little complicated:

<?brail

import System.Text

import System.Collections

 

def Messages(messages as ICollection):

      output "<ul>"

      for msg in messages:

?>

            <li>Title: ${msg.Title}<br/>

            Author:  ${msg.Author.Name}<br/>

            ${msg.Content}</li><br/>"""

 

<?brail output AjaxHelper.LinkToFunction('Post reply...', "$('newMsg.${msg.Id}').toggle()") ?>

<div id="newMsg.${msg.Id}"

      class="postNewMsg"

      style="display: none;">

      <form method="POST"

             action="/forum/postMsg.rails?msg.Forum.Id=${message.Forum.Id}&msg.Parent.Id=${msg.Id}">

            <table>

                  <tr>

                        <td>Title:</td>

                        <td><input name="msg.Title"

                                      type="text"/>

                        </td>

                  </tr>

                  <tr>

                        <td>Author Id:</td>

                        <td><input name="msg.Author.Id"

                                      type="text"/>

                        </td>

                  </tr>

                  <tr>

                        <td>Content:</td>

                        <td></td>

                  </tr>

                  <tr>

                        <td colspan="2">

                              <textarea name="msg.Content" rows="10" cols="50"></textarea>

                        </td>

                  </tr>

            </table>

            <input type="submit" value="Post new message"/>

      </form>

</div>

<?brail

            if msg.Children.Count >0:

                  Messages(msg.Children)

            end

      end

      output "</ul>"

end

?>

<h2>Message ${message.Id}</h2>

Title: ${message.Title}<br/>

Author:  ${message.Author.Name}<br/>

${message.Content}

 

<?brail  Messages( message.Children ) ?>

Most of this is HTML, though. What is happening is that we output the first message, and then recursively all its children, with a reply link that will open in place. This seems to the end of it, we have finished implementing all the scenarios.

Wow, that took a lot of time to write. I usually manage to finish those in a few hours, but this took several days. I hope that I managed to show you a bit of how you can use Castle to make your life easier.
This is not a real application, it is not even a pretty demo. I focused mainly on try to shed some light into what Castle can do for you. Of the top of my head, this application need a lot more UI, a bit more error handling, but seems to be it.
It is already well structured and it is well tested. Very good points to start with. :-)

What I find exciting, part II

I am not sure if I should feel this way, but I just finished writing a paged table in MonoRail, and that brought a complete sense of satisfaction. No, it isn't a major techincal challange. :-) It is just that I know,all the up and all the way down, what is going on, and I wholly approve. More and more I find how important it is to control as much as possible of the stack as possible.

If I try to constract the code required to handle the full "get from the database, show paged view on form" functionaly in ASP.Net WebForms + DataSet (the "default" ASP.Net architecture) vs. MonoRail + Active Record, I find that I get about the same line count, but the functionality offered is greater (custom paging doesn't require a chicken, for instnace), and there isn't magic stuff going on that I can't get into (view state, data binding).

The amount of flexibility that I have when working with the MR + AR stack is far greater. Moreover, I find that I don't feel that I am hacking when I am using MonoRail. All too often I need to do something a bit out of line in WebForms, and then I find that I need to write hackish code (e.Cells[12].FindControl("lnkDelete").Enabled = UserHasPermission; is hackish, and not maintainable).

The mere fact that I can look at code that does paging and say: "You know, this is really cool." speaks for itself, I think.

Tags:

Published at

Brail Code Formatting


I mentioned before that I tend to edit Brail's source in #Develop, since it has native support for Boo. I finally decided that just using Boo's color scheme is not enough, and created one specific for Brail.
This scheme is merely HTML + Boo, where code sections are marked with <?brail ?>

The coloring schema is attached to this message.

Brail.zip (3.53 KB)

Now if only it were as simple to do this for Visual Studio....
Tags:

Published at