Monorail #2 : Layouts & Rescues

LAYOUTS

UPDATED: Used the Code Snippet plug-in

Layouts are like master pages or template pages in Adobe DreamWeaver.  They allow you to template your site.  I have a default.vm under my ../Views/layouts folder and the way to make a controller utilize the layout is to add a layout attribute to the class definition:

   1: using System;
   2: using Castle.MonoRail.Framework;
   3:  
   4: namespace JasonMeridth.Controllers
   5: {
   6:     [Layout("default"), Rescue("generalerror")]
   7:     public class ExampleController : SmartDispatcherController
   8:     {


As you probably saw in my last post, it wasn’t just a simple “hello world” text on a white background. 
The reason I had a design was due to the Layout attribute on the ExampleController class. 
Layouts are simply NVelocity files (or whichever viewing engine you chose) placed in the /Views/layouts folder.  In my case it is the default.vm file:

   1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
   2: <!--
   3:  
   4:     terrafirma1.0 by nodethirtythree design
   5:     http://www.nodethirtythree.com
   6:  
   7: -->
   8: <html>
   9: <head>
  10: <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
  11: <title>JasonMeridth 1.0</title>
  12: <meta name="keywords" content="Jason Meridth, San Antonio, Alamo Coders, Los Techies" />
  13: <meta name="description" content="The personal webpage of Jason Meridth" />
  14: <link rel="stylesheet" href="$siteRoot/Content/css/default.css" />
  15: </head>
  16: <body>
  17:  
  18: <div id="outer">
  19:  
  20:     <div id="upbg"></div>
  21:  
  22:     <div id="inner">
  23:  
  24:         <div id="header">
  25:             <img src="../Content/images/lucas_header.JPG" />
  26:             <h1><span>JasonMeridth</span>.com<sup>1.0</sup></h1>
  27:         </div>
  28:     
  29:         #component(MenuComponent)    
  30:  
  31:         <div id="primarycontent">
  32:         
  33:             
  34:             <!-- primary content start -->
  35:             $childContent
  36:             <!-- primary content end -->
  37:     
  38:         </div>
  39:         
  40:         <div id="secondarycontent">
  41:  
  42:             <!-- secondary content start -->
  43:         
  44:             <h3>About Me</h3>
  45:             <div class="content">
  46:                 <img src="../Content/images/lucas_and_i_laptop.JPG" class="picB" alt="" />
  47:                 <p>
  48:                     I'm a full-time computer programmer currently working on web software development for a Mortgage Company.&nbsp; This is my personal site and my testing grounds for new technologies.&nbsp; Hence, the use of the <a href="http://www.castleproject.org/monorail/index.html" target="_blank">Castle Monorail</a> technology. I practice <a href="http://en.wikipedia.org/wiki/Test-driven_development" target="_blank">Test-driven development</a> and mainly Agile/Scrum methodologies.</p>
  49:             </div>
  50:             <h3>Links</h3>
  51:             <div class="content">
  52:                 <ul class="linklist">
  53:                     <li class="first"><a href="http://www.lostechies.com" target="_blank">Los Techies</a></li>
  54:                     <li><a href="http://www.alamocoders.net" target="_blank">Alamo Coders</a></li>
  55:                 </ul>
  56:             </div>
  57:             <!-- secondary content end -->
  58:  
  59:         </div>
  60:     
  61:         <div id="footer">
  62:         
  63:             &copy; JasonMeridth.com. All rights reserved. Design by <a href="http://www.nodethirtythree.com/">NodeThirtyThree</a>.
  64:         
  65:         </div>
  66:  
  67:     </div>
  68:  
  69: </div>
  70:  
  71: </body>
  72: </html>

To ignore:
#component(MenuComponent) – This is a ViewCompoent, that is next post.

To pay attention to: – If you are a web developer, I assume you know CSS and divs.  The section to notice is the $childContent item in the primaryContent div.
This is where I can display certain items, even nested layouts from the controller.

Content of the ExampleController:

   1: using System;
   2: using Castle.MonoRail.Framework;
   3:  
   4: namespace JasonMeridth.Controllers
   5: {
   6:     [Layout("default")]
   7:     public class ExampleController : SmartDispatcherController
   8:     {
   9:         public void HelloWorld()
  10:         {
  11:             PropertyBag["message"] = "Hello World";
  12:             RenderView("helloworld");
  13:         }
  14:     }
  15: }
There are 2 ways to prevent layout rendering even if you have the Layout attribute present.  First, is the CancelLayout method.
   1: using System;
   2: using Castle.MonoRail.Framework;
   3:  
   4: namespace JasonMeridth.Controllers
   5: {
   6:     [Layout("default")]
   7:     public class ExampleController : SmartDispatcherController
   8:     {
   9:         public void HelloWorld()
  10:         {
  11:             PropertyBag["message"] = "Hello World";
  12:             CancelLayout();
  13:             RenderView("helloworld");
  14:         }
  15:     }
  16: }

Second is passing true to the second parameter of the overloaded RenderView method.  That parameter is the skipLayout parameter.
   1: using System;
   2: using Castle.MonoRail.Framework;
   3:  
   4: namespace JasonMeridth.Controllers
   5: {
   6:     [Layout("default")]
   7:     public class ExampleController : SmartDispatcherController
   8:     {
   9:         public void HelloWorld()
  10:         {
  11:             PropertyBag["message"] = "Hello World";
  12:             RenderView("helloworld", true);
  13:         }
  14:     }
  15: }

The reason I might use these methods is when validation occurs on a form's input and you want to provide a list of required fields that were not inputted correctly, you could give it in a basic white background, HTML text page, with a link back to the original layout.  Old school but effective.  The list would be provided from the controller and some validation service, so unit testing is available on what would be rendered to the user. :)

RESCUES

   1: using System;
   2: using Castle.MonoRail.Framework;
   3:  
   4: namespace JasonMeridth.Controllers
   5: {
   6:     [Layout("default"), Rescue("generalerror", typeof(Exception))]
   7:     public class ExampleController : SmartDispatcherController
   8:     {
   9:         public void HelloWorld()
  10:         {
  11:             PropertyBag["message"] = "Hello World";
  12:             RenderView("helloworld");
  13:         }
  14:     }
  15: }

Contents of the generalerror.vm file:
   1: <h2>GENERAL ERROR RESCUE</h2>
   2:  
   3: <h2>Unexpected error happened</h2>
   4:  
   5: <p> This is the rescue page. See the exception details below </p>
   6:  
   7: <p>
   8: #set($exception = $context.LastException)
   9: #showexception($exception)
  10: </p>
  11:  
  12: #macro(showexception $exc)
  13:     <p> Type: <b>$exc.GetType().Name</b></p>
  14:  
  15:     <p>Message:
  16:     $exc.Message</p>
  17:  
  18:     #if($exc.StackTrace)
  19:         StackTrace:
  20:         $exc.StackTrace
  21:     #end
  22:  
  23:     #if($exc.InnerException)
  24:         <b>Inner exception:</b>
  25:         #set($inner = $exc.InnerException)
  26:         #showexception($inner)
  27:     #end
  28: #end

As you can tell, in the file the sample application creator created a macro called "showexception".   Pay attention to the $context.LastException part.  MonoRail will populate this property on the current context so that the view can display the exception information.
They use this to display exceptions and their inner exceptions to the user.To get this controller to throw an exception I create an action (method) that will explicitly throw an exception:
   1: using System;
   2: using Castle.MonoRail.Framework;
   3:  
   4: namespace JasonMeridth.Controllers
   5: {
   6:     [Layout("default"), Rescue("generalerror", typeof(Exception))]
   7:     public class ExampleController : SmartDispatcherController
   8:     {
   9:         public void HelloWorld()
  10:         {
  11:             PropertyBag["message"] = "Hello World";
  12:             RenderView("helloworld");
  13:         }
  14:  
  15:         public void ExceptionExample()
  16:         {
  17:             throw new Exception("I'm throwing an exception...");
  18:         }
  19:     }
  20: }
To see the general error NVelocity view, I type the following into your url: http://jasonmeridth.com/Example/ExceptionExample.rails and I see the following:
image 
You will also notice the Rescue attribute and string name provided, "generalerror".  This is the layout/page that will be rendered if the page comes across any exceptions.  
I currently have the generalerror.vm provided from the
Sample application on the Castle Monorail documentation site (Visual Studio 2005).
If add a rescue attribute to the ExceptionExample action (medhod) it's rescue file will be used instead of the rescue file, generalerror.vm, for the controller.  
No matter the exception type dictated on the controller, the action takes president. I've added the anotherrescue.vm file to the resuces folder and added the attribute.
image 
   1: using System;
   2: using Castle.MonoRail.Framework;
   3:  
   4: namespace JasonMeridth.Controllers
   5: {
   6:     [Layout("default"), Rescue("generalerror", typeof(Exception))]  <---- WILL NOT BE USED
   7:     public class ExampleController : SmartDispatcherController
   8:     {
   9:         public void HelloWorld()
  10:         {
  11:             PropertyBag["message"] = "Hello World";
  12:             RenderView("helloworld");
  13:         }
  14:  
  15:         [Rescue("anotherrescue")]  <---- HAS PRESIDENCE
  16:         public void ExceptionExample()
  17:         {
  18:             throw new Exception("I'm throwing an exception...");
  19:         }
  20:     }
  21: }

If we flip the rescue attributes and throw a regular Exception and make the generalerror.vm relate to SqlExceptions, then the controller will take president because the exception will get out of the action and to the controller.
To avoid the, "What did you just write?!":

The controller has a designated rescue file:
1. If your action has a rescue attribute with no specific exception type, and if that action throws an exception, the action's rescue file will be used.
2. If your action has a rescue attribute with a specific exception type, and that action throws THAT exception type, the action's rescue file will be used.
3. If your action has a rescue attribute with a specific exception type, and that action throws A DIFFERENT exception type, the controller's resuce file will be used if it is designated with no type or with that specific exception type.

The controller has NO designated rescue file:
1. If your action has a rescue attribute with no specific exception type, and if that action throws an exception, the action’s rescue file will be used.
2. If your action has a rescue attribute with a specific exception type, and that action throws THAT exception type, the action’s rescue file will be used.
3. If your action has a rescue attribute with a specific exception type, and that action throws A DIFFERENT exception type, an unhandled exception will be shown (the famous yellow and peach ASP.NET error page) [you can have a base exception block or something similar to prevent this, of course].

NEXT POST: View Components

Prior Posts:
Monorail #0:Controllers
Monorail #1:Reasons, Setup, and First Output

kick it on DotNetKicks.com

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jason Meridth

Continuously learning software developer trying to not let best be the enemy of better
This entry was posted in castle, monorail. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

3 Responses to Monorail #2 : Layouts & Rescues

  1. Joe Ocampo says:

    Put a link from your previous post to this one and from this post back to your previous post.

  2. LDWebb says:

    I don’t want to post this message

    I am running into some issues with presidence of rescues at the controller level and wanted to define certain rescues at the action level, I was reviewing your article, which has very important details, but the text is cut off in sections. The sections that I am interested in are “The controller has a designated rescue file:” and “The controller has NO designated rescue file:”. I was wondering if I could get a soft copy of this article in word or text format.