Thursday, August 25, 2011

Blog moved!


I am now blogging at willcode4beer.net, so head over there for new content...


-Tim Tyrrell

Using a "Selection Strategy” to Simplify Your Code

(I wrote blog post this in April of 2010 but never published it. I am no longer an employee of Dovetail Software but at the time of this post I was working on their awesome Support Suite product with some amazing developers. I believe that this post has some good content, so I wanted to publish it before it got lost forever. I apologize for the terrible code formatting; Blogger just stinks at this.)

Very recently Chad Myers and I implemented this pattern in two different places for two very different situations in our Dovetail Support Suite codebase. One was with a “Flash Service”, where based on which entity was passed in, we would call a method on that entity and return the flashes to the caller. The other dealt with displaying values in a menu strip, and based on the entity type or interface, we would have different output returned to the strip.

Why Go To All This Trouble?

Using this pattern has the following benefits:
  1. Prevents us from having a lengthy conditional logic statements in the code, which is less maintainable and harder to read and test. (Very non-OOP procedural code!)
  2. Allows us to create new strategies without modifying the existing service, other strategies, and likely not even the StructureMap bootstrap methods
  3. Allows us to write unit tests for each strategy independently
  4. Adds an extensibility point: We have the defaults wired-up, but you can easily drop an assembly in a directory, have StructureMap scan it and put that at the front of the list.
I chose the display scenario that I mentioned above for my code example only because it handles a couple more selection possibilities that I can demonstrate for my object matching. I created a simple school domain model with an instructor, a course, and a facility. We wrote the code in a Test-Driven manner, but I am not going through that process in this post.

“The Code”

We start off with an interface for a service with a GetDisplayFor method. The idea is that we will pass an entity into the service, which will cycle through a list of strategies until one matches. The matched-strategy will know how to display the output and return it. Each entity has a base method called “EntityDescription” which would be called in other places in the application, but for this specific scenario we need to output something different, which is why this service exists.

public interface IDisplayService
{
  string GetDisplayFor(DomainEntity entity);
}

We then created a class called DisplayService that implements IDisplayService and takes in a collection of IDisplayStrategies into the constructor. We created a DefaultDisplayStrategy, it’s purpose is to be a “catch all” if no other strategies match.

public interface IDisplayStrategy
{
  bool Matches(DomainEntity entity);
  string GetEntityDescription(DomainEntity entity);
}

public class DefaultDisplayStrategy : IDisplayStrategy
{
public bool Matches(DomainEntity entity)
{
return true;
}

  public string GetEntityDescription(DomainEntity entity)
  {
return entity.EntityDescription;
}
}


Here is the code for the DisplayService:

public class DisplayService : IDisplayService
{
private readonly List<IDisplayStrategy> _strategies = new List<IDisplayStrategy>();


public DisplayService(IEnumerable<IDisplayStrategy> strategies)
{
_strategies.AddRange(strategies);
_strategies.Add(new IdentifierDisplayStrategy());
_strategies.Add(new DefaultDisplayStrategy());
}

public string GetDisplayFor(DomainEntity entity)
{
var strategy = FindStrategyFor(entity);
return strategy.GetEntityDescription(entity);
}

public IDisplayStrategy FindStrategyFor(DomainEntity entity)
{
return _strategies.First(s => s.Matches(entity));
}
}

The container will fire in any concrete implementations of IDisplayStrategy that we wired up with StructureMap (code listed later in this post) into the constructor, and since we care about ordering, we “new up” two strategies after those. In the very least, we want the”catch all” to be last.(We asked our teammate, Jeremy D.  Miller, if there is a way to order with StructureMap, and he confirmed there you cannot).

Strategy Unit Testing

As I stated earlier, since we don’t have some monolific conditional logic statement, we can more easily test each piece of our solution. Let’s look at the InstructorDisplayStrategy:

public class InstructorDisplayStrategy : IDisplayStrategy
{
public bool Matches(DomainEntity entity)
{
return entity.GetType() == typeof (Instructor);
}

public string GetEntityDescription(DomainEntity entity)
{
return "Professor " + ((Instructor) entity).Name;
}
}


To test this class, I am using some helpful testing extension methods included in the FubuMVC.Tests project. We basically just need to test what will match and what will not match with the Matches method, and then what our expected output will be on the GetEntityDescription method.

[TestFixture]
public class InstructorDisplayStrategyTester
{
private InstructorDisplayStrategy _strategy;

  [SetUp]
public void Setup()
{
_strategy = new InstructorDisplayStrategy();
}

[Test]
public void should_match_on_instructor_entity()
{
_strategy.Matches(new Instructor()).ShouldBeTrue();
}

[Test]
public void should_not_match_on_non_instructor_objects()
{
_strategy.Matches(new Course()).ShouldBeFalse();
  }
 
[Test]
  public void should_return_description_and_appended_title_for_the_instructor()
{
var course = new Instructor() { Name = "Skippy" };
_strategy.GetEntityDescription(course).ShouldEqual("Professor Skippy");
}
}


The DisplayService fires through the DisplayStrategies and executes the Matches method on each. This one above just tries to match types. The one below tries to match by Interface (“is” is like “as”, except it does not actually try to cast to the provided type, more info here)

public class IdentifierDisplayStrategy : IDisplayStrategy
{
public bool Matches(DomainEntity entity)
{
return entity is IHaveIdentifier;
}
 
public string GetEntityDescription(DomainEntity entity)
{
var identifierEntity = entity as IHaveIdentifier;
return identifierEntity.Identifier + " " + entity.EntityDescription;
}
}

Very familiar looking test:

[TestFixture]
public class IdentifierDisplayStrategyTester
{
private IdentifierDisplayStrategy _strategy;
[SetUp]
public void Setup()
{
_strategy = new IdentifierDisplayStrategy();
}

[Test]
public void should_match_on_identifier_object()
{
_strategy.Matches(new Course()).ShouldBeTrue();
}

[Test]
public void should_not_match_on_non_identifier_object()
{
_strategy.Matches(new DomainEntity()).ShouldBeFalse();
}

[Test]
public void should_return_description_with_identifier_for_the_course()
{
var course = new Course() {Identifier = "1005", Name = "Skippy"};
_strategy.GetEntityDescription(course).ShouldEqual("1005 Skippy");
}
}

I may as well post the last bit of code for the lazy folks that don’t want to download the source. Here is the unit test for the DisplayService. (Again, you will notice some assertion and mocking shorthand through the use of the helpful extension methods.) We are testing the ordering, that the default is called if no others match, and basically that the service’s methods behave how we intended (this statement is slightly untrue because this unit test file was were we started and drove out the functionality of the service with the tests below).

[TestFixture]
public class DisplayServiceTester
{
private DisplayService _displayService;
private IDisplayStrategy _strategy;
private IDisplayStrategy _strategy2;

[SetUp]
public void Setup()
{
_strategy = MockRepository.GenerateStub<IDisplayStrategy>();
_strategy2 = MockRepository.GenerateStub<IDisplayStrategy>();
_displayService = new DisplayService(new[] { _strategy, _strategy2 });
}

[Test]
public void should_return_default_empty_entity_description_if_no_strategy_matched()
{
_displayService.GetDisplayFor(new DomainEntity()).ShouldEqual("");
}

[Test]
public void should_return_default_strategy_no_matter_what()
{
_displayService.FindStrategyFor(new DomainEntity()).ShouldBeOfType<DefaultDisplayStrategy>();
  }

  [Test]
public void should_match_a_strategy_by_matches_method()
{
_strategy.Stub(x => x.Matches(null)).IgnoreArguments().Return(true);
_displayService.FindStrategyFor(new DomainEntity()).ShouldBeTheSameAs(_strategy);
}

[Test]
public void should_match_first_strategy()
{
_strategy.Stub(x => x.Matches(null)).IgnoreArguments().Return(true);
_displayService.FindStrategyFor(new DomainEntity());
_strategy2.AssertWasNotCalled(s => s.Matches(null), o => o.IgnoreArguments());
}

[Test]
public void should_check_all_strategies_for_matches()
{
_strategy2.Stub(x => x.Matches(null)).IgnoreArguments().Return(true);
_displayService.FindStrategyFor(new DomainEntity()).ShouldBeTheSameAs(_strategy2);
  }

  [Test]
  public void should_return_display_from_matched_strategy()
{
string name = "Skippy";
_strategy.Stub(x => x.Matches(null)).IgnoreArguments().Return(true);
   _strategy.Stub(x => x.GetEntityDescription(null)).IgnoreArguments().Return(name);
   _displayService.GetDisplayFor(new DomainEntity()).ShouldEqual(name);
  }

  [Test]
  public void should_only_call_display_on_matched_method_and_no_others()
{
_strategy.Stub(x => x.Matches(null)).IgnoreArguments().Return(true);
    _strategy.Stub(x => x.GetEntityDescription(null)).IgnoreArguments().Return(null);
_displayService.GetDisplayFor(new DomainEntity());
_strategy2.AssertWasNotCalled(x => x.GetEntityDescription(null), o => o.IgnoreArguments());
}
}


Usage in the Controller

The controller takes the display service into the constructor and grabs the implementation from the container. All the controller method does is call the GetDisplayFor method on the service and passes in an object that subclasses DomainEntity. The service returns a string, the controller plugs it into the view model and then returns it to the view. (I am using FubuMVC for my example, so don’t be thrown off by the input view model and the output view model)

1: public class HomeController
2: {
3: private readonly IDisplayService _displayService;
4:  
5: public HomeController(IDisplayService displayService)
6: {
7: _displayService = displayService;
8: }
9: 
10: public HomeViewModel Home(HomeInputModel input)
11: {
12: var course = _displayService.GetDisplayFor(new Course() { Name = "Lance", Identifier = "1005"});
13: return new HomeViewModel { Text = course };
14: }
15: }
16: 
17: public class HomeInputModel
18: {
19: }
20: 
21: public class HomeViewModel
22: {
23: public string Text { get; set; }
24: }



Wiring Up the Container

In the global.asax Application_Start we wire up the following:


1: ObjectFactory.Initialize(x =>
   2: {
   3:     x.Scan(y =>
   4:                {
   5:                    y.AssemblyContainingType<IDisplayService>();
   6:                    y.WithDefaultConventions();
   7:                });
   8:     x.For<IDisplayStrategy>().Use<InstructorDisplayStrategy>();
   9: }

Why only the one concrete class? If you recall, we have the last resort DefaultDisplayStrategy and the IdentifierDisplayStrategy “newed up” inside the DisplayService construction because ordering is important. Why not just “new up” the last strategy in the constructor as well? Because this is just a small example and the beginning of many more strategies that will be added, also as mentioned above, this is an extensibility point.

If ordering did not matter, we could remove the object instantiations from the DisplayService constructor and let StructureMap wire it all up like this(which is also an example that could be used for the extensibility point scanning):


1: ObjectFactory.Initialize(x =>
2: x.Scan(y => {
3: y.AssemblyContainingType<IDisplayService>();
4: y.WithDefaultConventions();
5: y.AddAllTypesOf<IDisplayStrategy>();
6: })
7: );


Conclusion

There you have it. Hopefully this post will help you to solve similar problems in the future.

Code posted here.


~ Tim Tyrrell

Monday, January 11, 2010

“Hello World” with FubuMVC (without the training wheels)

My previous post was a FubuMVC super quick-start which explained how to utilize some very helpful code that has been added into the Fubu source to allow you to get up and running fast. While this should suit most initial situations, there probably will be a point where you need to add your own conventions. I will walk you through creating your own bootstrapping code which will also explain how the “training wheels” from the previous post work. I am going to borrow heavily from Chad Myers’ explanation’s which he posted on the FubuMVC Google Group, because I could not have said it better.

  1. (Starting from the previous posts step #11) Add a “Global.asax” file and delete all the generated methods except “Application_Start”. In “Application_Start” add the following:

       1: public class Global : System.Web.HttpApplication {
       2:     protected void Application_Start(object sender, EventArgs e) {
       3:         var routeCollection = RouteTable.Routes;
       4:         FubuStructureMapBootstrapper.Bootstrap(routeCollection);
       5:     }
       6: }

  2. Create a FubuStructureMapBootstrapper.cs class that implements the IBootstrapper interface from StructureMap and then implement it’s members. This class manages the starting of Fubu and StructureMap together.

       1: public class FubuStructureMapBootstrapper : IBootstrapper {
       2:     private readonly RouteCollection _routes;
       3:  
       4:     private FubuStructureMapBootstrapper(RouteCollection routes) {
       5:         _routes = routes;
       6:     }
       7:  
       8:     public void BootstrapStructureMap() {
       9:         UrlContext.Reset();
      10:  
      11:         ObjectFactory.Initialize(x => { });
      12:  
      13:         BootstrapFubu(ObjectFactory.Container, _routes);
      14:     }
      15:  
      16:     public static void BootstrapFubu(IContainer container, RouteCollection routes) {
      17:         var bootstrapper = new StructureMapBootstrapper(container, new HelloWorldFubuRegistry());
      18:         bootstrapper.Bootstrap(routes);
      19:     }
      20:  
      21:     public static void Bootstrap(RouteCollection routes) {
      22:         new FubuStructureMapBootstrapper(routes).BootstrapStructureMap();
      23:     }
      24: }
    
    

  3. Create the HelloWorldFubuRegistry class that is passed into the StructureMapBootstrapper constructor along with the container. This is where all the actual Fubu conventions are located.

       1: public class HelloWorldFubuRegistry : FubuRegistry {
       2:     public HelloWorldFubuRegistry() {
       3:         IncludeDiagnostics(true);
       4:  
       5:         Applies.ToThisAssembly();
       6:  
       7:         Actions
       8:             .IncludeTypesNamed(x => x.EndsWith("Controller"));
       9:  
      10:         Routes
      11:             .IgnoreControllerNamespaceEntirely();
      12:  
      13:         Views.TryToAttach(x => {
      14:                               x.by_ViewModel_and_Namespace_and_MethodName();
      15:                               x.by_ViewModel_and_Namespace();
      16:                               x.by_ViewModel();
      17:                           });
      18:     }
      19: }
    

  4. IncludeDiagnostics(true) – Enable the diagnostics to be accessible through the /_fubu and /?fubudebug=true querystring options
  5. Applies.ToThisAssembly() – Tells Fubu to perform the following registration steps by scanning this assembly
  6. Actions.IncludeTypesNamex(x => x.EndsWith(“Controller”)) – Tells Fubu how to identify which classes containing action methods (i.e. HomeController)
  7. Routes.IgnoreControllerNamespareEntirely()  -- By default, Fubu will create a route for each action like /namespace/controllerbasename/action. Normally you wouldn't want /fubumvc/helloworld/controllers/home/home/home, you'd just want /home/home or /home/index or whatever. "IgnoreControllerNamespaceEntirely" does that. There are numerous variations of how to arrive at the route name here. Otherwise, you can specify your own conventions and manage the process.
  8. Views.TryToAttach --  This is a fall-through for matching actions-that-should-render-views (as opposed to actions that should render JSON) to their corresponding views.  It goes as follows:

      1. Try to match a view that is strongly typed (i.e. IFubuView where TModel is the return/output type of the Action), *AND* is in the same Namespace as the Controller/Action class *AND* is named the same as the Action/Method name (i.e. Home() -> Home.aspx)

      2. Try to match on the model *AND* the Namespace

      3. If all else fails, try to find a view anywhere in this assembly that matches the model type (Meaning that you don’t need to have the controllers and views in the same folder)

  9. Just in case you missed it, perform the same web.config tweaking like in the previous post.
I added this to my personal Google Code repo folder here. I anticipate that this, or something like it, will probably be moved to the Fubu code or wiki in the near future.

-Tim Tyrrell

Friday, January 8, 2010

“Hello World” with FubuMVC (Super Quick Start)

I started at Dovetail Software near the end of last year and the guys were gearing up to strip out the “proto-fubu” framework from their current project and rewrite the existing FubuMVC code; which is now complete. This week Brandon and I messed around and created a sample application to get more familiar with the framework. These are the steps that we used to get a quick sample up and running (made *much* easier by Chad and Josh last night).

  1. Get FubuMVC source from the “reboot” branch using something like TortoiseSVN at http://fubumvc.googlecode.com/svn/branches/reboot/ (soon to be Git). A sample application very similar to what is listed below is in the src/FubuMVC.HelloWorld folder
  2. Get Ruby to build it by navigating to the base FubuMVC folder and run a “rake” command (I am using “Ruby-186-27”, but any should work) or just open the solution and compile it.
  3. Create an ASP.NET Web Application project. Create a “lib” folder on the file system next to the newly created project
  4. Grab all the files (not just the three DLL's below) from the %yourFubufolder%\build folder if you raked, or %yourFubufolder%\src\FubuMVC.Container.StructureMap\bin\Debug folder if you built it through Visual Studio, copy them to a “lib” folder mentioned above, and then add three references to your project
    1. StructureMap.dll
    2. FubuMVC.Core.dll
    3. FubuMVC.StructureMap.dll
  5. Also add a reference to “System.Web.Routing” if you want to make the squiggle’s in your web.config go away
  6. Delete the “Default.aspx” file and the App_Data folder
  7. Create a Controllers Folder and then add a Home folder underneath it
  8. Add a Home.aspx Web Form page into the Home folder, have it inherit from the FubuPage<TViewModel> class instead of the Page class (FubuPage inherits from Page) and create an output view model to plug into that spot
       1: using FubuMVC.Core.View;
       2:  
       3: namespace FubuMVC.HelloWorld.Controllers.Home
       4: {
       5:     public class Home : FubuPage<HomeViewModel>
       6:     {
       7:     }
       8: }

  9. Add a HomeContoller.cs class to the Home folder and add a method that returns the previously created output view model, HomeViewModel and accepts a new input model
       1: namespace FubuMVC.HelloWorld.Controllers.Home
       2: {
       3:     public class HomeController
       4:     {
       5:         public HomeViewModel Home(HomeInputModel model)
       6:         {
       7:             return new HomeViewModel {Text = "Hello, world."};
       8:         }
       9:     }
      10:  
      11:     public class HomeViewModel
      12:     {
      13:         public string Text { get; set; }
      14:     }
      15:  
      16:     public class HomeInputModel
      17:     {
      18:     }
      19: }

  10. Get the text out of the returned Model and display it on the view (I yanked out the server tags, although that is not necessary)

  11.    1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Home.aspx.cs" Inherits="FubuMVC.HelloWorld.Controllers.Home.Home" %>
       2:  
       3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
       4:  
       5: <html xmlns="http://www.w3.org/1999/xhtml" >
       6: <head>
       7:     <title></title>
       8: </head>
       9: <body>
      10:     <div>
      11:         <%= Model.Text %>
      12:     </div>
      13: </body>
      14: </html>



  12. Add a Global.asax file to the project, but instead of inheriting from HttpApplication, inherit from FubuStructureMapApplication:

       1: using FubuMVC.StructureMap.Bootstrap;
       2:  
       3: namespace FubuMVC.HelloWorld
       4: {
       5:     public class Global : FubuStructureMapApplication
       6:     {
       7:     }
       8: }

  13. If you want to turn on the diagnostics from Jeremy’s article, switch the web.config debug="true"and the diagnostics will be enabled. Otherwise you can set it yourself below.

  14.    1: using FubuMVC.StructureMap.Bootstrap;
       2:  
       3: namespace FubuMVC.HelloWorld
       4: {
       5:     public class Global : FubuStructureMapApplication
       6:     {
       7:         public Global()
       8:         {
       9:             EnableDiagnostics = true;
      10:         }
      11:     }
      12: }

  15. Lastly, we took out the machete on the web.config file. Although, we did add some references to System.Web.Routing inside of it.
       1: <?xml version="1.0"?>
       2: <configuration>
       3:   <configSections/>
       4:   <appSettings/>
       5:   <connectionStrings/>
       6:   <system.web>
       7:     <compilation debug="false">
       8:       <assemblies>
       9:         <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
      10:         <add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      11:       </assemblies>
      12:     </compilation>
      13:     <authentication mode="None" />
      14:     <pages>
      15:       <controls/>
      16:     </pages>
      17:     <httpHandlers/>
      18:     <httpModules>
      19:       <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      20:     </httpModules>
      21:   </system.web>
      22:   <system.codedom>
      23:     <compilers>
      24:       <compiler language="c#;cs;csharp" extension=".cs" warningLevel="4"
      25:                 type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      26:         <providerOption name="CompilerVersion" value="v3.5"/>
      27:         <providerOption name="WarnAsError" value="false"/>
      28:       </compiler>
      29:     </compilers>
      30:   </system.codedom>
      31:   <system.webServer>
      32:     <validation validateIntegratedModeConfiguration="false"/>
      33:     <modules>
      34:       <remove name="UrlRoutingModule" />
      35:       <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      36:     </modules>
      37:     <handlers>
      38:       <add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
      39:     </handlers>
      40:   </system.webServer>
      41: </configuration>

  16. Add the project path to new application in IIS (or Cassini) and hit the URL: http://localhost/%yourapp%/home/home

image


Screenshot of final folder structure and references:

image

Code location: An application very similar to this is located in the reboot branch of FubuMVC, called FubuMVC.HelloWorld in the src folder here

Let me know if I missed anything or if this does not work. A lot will be done in the next couple of weeks to get Fubu rocking, and I plan on blogging as much as possible along the way. As you probably heard, we are starting up a posse: http://wiki.fubumvc.com/TODO

-Tim Tyrrell