Monday, December 31, 2007

Rhino Mocks Callbacks with Lambda expressions

I recently wanted to write a test for this piece of code using Rhino Mocks:

public void Register(string username, string password, string posterName)
{
    User user = new User()
    {
        Name = username,
        Password = password,
        Role = Role.Poster
    };

    userRepository.Save(user);

    Poster poster = new Poster()
    {
        Name = posterName,
        ContactName = username,
        PosterUsers = new System.Collections.Generic.List<PosterUser>() { new PosterUser() { User = user } }
    };

    posterRepository.Save(poster);
}

As you can see, it simply takes some parameters: username, password, posterName and then constructs a User object which it passes to the Save method of a userRepository and a Poster object which is passed to the Save method of a posterRepository. Both userRepository and posterRepository are created using Inversion of Control. Here's my initial attempt at a unit test for the Register method:

[Test]
public void ReigsterShouldSaveNewUserAndPoster()
{
    string username = "freddy";
    string password = "fr0dd1";
    string posterName = "the works";

    using (mocks.Record())
    {
        // how do get the user object that should be passed to userRepository ???
        //userRepository.Save(????);

        //posterRepository.Save(????);
    }

    using (mocks.Playback())
    {
        posterLoginController.Register(username, password, posterName);
        
        // here I need to assert that the correct User and Poster objects were passed to the 
        // repository mocks
    }
}

I couldn't see how to get the object that was passed to my mock instances of userRepository and posterRepository. I needed some way of capturing the arguments to the mock objects so that I could assert they were correct in the playback part of the test.

Alternatively, I briefly considered using abstract factories to create my user and repository:

public void Register(string username, string password, string posterName)
{
    User user = userFactory.Create(username, password, Role.Poster);

    userRepository.Save(user);

    Poster poster = posterFactory.Create(posterName, username, user);

    posterRepository.Save(poster);
}

Then I could have written the tests like this:

[Test]
public void ReigsterShouldSaveNewUserAndPoster()
{
    string username = "freddy";
    string password = "fr0dd1";
    string posterName = "the works";

    using (mocks.Record())
    {
        User user = new User();
        Expect.Call(userFactory.Create(username, password, Role.Poster)).Return(user);
        userRepository.Save(user);

        Poster poster = new Poster();
        Expect.Call(posterFactory.Create(posterName, username, user)).Return(poster);
        posterRepository.Save(poster);
    }

    using (mocks.Playback())
    {
        posterLoginController.Register(username, password, posterName);
    }
}

It makes for a very clean test and it's forced me to separate out the concerns of creating new instances of User and Poster and the co-ordination of creating and saving them which is what the controller method 'Register' is really about. But in this case I just felt it was overkill to create abstract factories just to make my tests neat.

A bit more digging in the Rhino Mocks documentation revealed Callbacks. They allow you to create a delegate to do custom validation of the mocked method call. It effectively allows you to override Rhino Mocks' usual processing and plug in your own. The delegate has to have the same input parameters as the method it's validating and return a boolean; true if your test passes; false if it fails.

It's quite easy to use callbacks with the Linq Func delegate and Lambda expressions, although it took me a few attempts to work it all out. Here's the test:

[Test]
public void ReigsterShouldSaveNewUserAndPoster()
{
    string username = "freddy";
    string password = "fr0dd1";
    string posterName = "the works";

    User user = null;
    Poster poster = null;

    using (mocks.Record())
    {
        userRepository.Save(null);
        LastCall.Callback(new Func<User, bool>( u => { user = u; return true; }));

        posterRepository.Save(null);
        LastCall.Callback(new Func<Poster, bool>(p => { poster = p; return true; }));
    }

    using (mocks.Playback())
    {
        posterLoginController.Register(username, password, posterName);

        Assert.AreEqual(username, user.Name);
        Assert.AreEqual(password, user.Password);
        
        Assert.AreEqual(posterName, poster.Name);
        Assert.AreEqual(user, poster.PosterUsers[0].User);
    }
}

It's quite awkward syntax and it's not at all obvious how you'd do this from the Rhino Mocks API. I don't like the LastCall syntax either, but from this blog post it looks like that something that Oren is working on. There also Moq, which is a new mocking framework that fully leverages the new C# language features. Definitely something to keep an eye on.

Friday, December 07, 2007

RESTful Web Services by Leonard Richardson & Sam Ruby

restfull_web_services

I've had some experience creating web services. I've been involved in a couple of major SOA projects and even had a go at writing my own web service test tool: WsdlWorks, although I've sadly not had enough time to finish it off. So I've been soaked in SOAP (sorry) for quite some time, especially the Microsoft way of doing it. I've been aware of an alternative web service universe, REST, for a while too, but I dismissed it for lacking tooling and widespread adoption. However, in response to a comment on my 'REST as Betamax' blog, I went out and bought RESTful Web Services by Richardson & Ruby. Yes, it's one of those books, you know, the ones that totally kick you out of your rut. After reading it I've had my mind comprehensively changed. OK, so I'm of weak and easily lead by a good argument, but R&R's arguments for REST are pretty compelling. The core goodness is the linkability of RESTful services; the way that every resource (think object) has an individual URL which allows true hyper-linking of machine readable information. It's the restoration of HTTP to it's throne as the core of the World Wide Web. They show that SOAP is not only needless complexity, but actually a step backward.

As for lack of tooling, I was impressed by how nicely Ruby on Rails supports REST and it leads to the obvious conclusion that the new MVC Framework will make an excellent REST platform, especially with its powerful URL Routing engine. However, for the time being 99% of MS shops will not embrace REST no matter how powerful the arguments are for it. If they're keeping up to date they'll be writing RPC based web services with WCF, if not, they'll still be annotating stuff with [WebMethod]. But, if you hired me and gave me a free hand, I'd definitely go with REST.

Reading this book has just reinforced my view that Microsoft spends most of its time playing catchup with the rest of the software world. Thinking about it, none of the books that have had the most influence on the way I write software mention Microsoft tools.

Exposing generic methods to frameworks that don't understand generics.

How do you wrap a generic method like this:

public string WriteHtml<T>(string target, T source)

so that frameworks which don't understand generics can understand it, like this:

public string WriteHtml(string target, object source)

I've been writing a web application with Monorail. It's been loads of fun, and the speed at which I've been able to get a fully featured application up and running is awesome. I'll be blogging more about this soon, but today I just want to make a quick 'howto' about exposing generic APIs to frameworks that don't understand generics.

The problem is this: Monorail's default view engine is NVelocity, a port of the java Velocity template engine. I've found it pretty nice so far. To use it you just add stuff into a 'PropertyBag' in your controller and then reference in your view using the $parameterName syntax. You can invoke methods on your types and indeed Monorail comes with a nice collection of helpers to use within the view. I wanted to create a new helper to display a tree of objects (in this case a location hierarchy) in a combo box like this:

LocationSelect

So in my favorite TDD style I quickly got a class (HierarchicalSelect) up and running that took any object of type t with a property of IList<t> and spat out the HTML for my desired combo box. I then referenced my new helper in my template like this:

$hierarchicalSelect.WriteHtml("location.id", $location)

But it didn't work. Unhappily, NVelocity doesn't throw, it simply outputs your placeholder when it can't bind. I eventually worked out that the reason it didn't like WriteHtml() was because it's an generic method:

public string WriteHtml<T>(string target, T source)

What I needed was a way of exposing WriteHtml with the source argument typed as object rather than T. It turned out to be quite an effort. This is the obvious way:

public string WriteHtml(string target, object source)
{
    return WriteHtml(target, source);
}

But of course it doesn't work because T becomes System.Object rather than the actual type of 'source'.

Instead you have to use reflection to create a MethodInfo for WriteHtml<T> and then create a typed version before invoking it.

public string WriteHtml(string target, object source)
{
    Type sourceType = source.GetType();
    
    // get our unambiguous generic method
    MethodInfo writeHtmlMethod = typeof(HierarchicalSelect).GetMethod("WriteHtmlUnambiguous", 
        BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance);

    // create a typed version
    MethodInfo typedWriteHtmlMethod = writeHtmlMethod.MakeGenericMethod(sourceType);
    
    // invoke it.
    return (string)typedWriteHtmlMethod.Invoke(this, new object[] { target, source });
}

// need to provide this so that GetMethod can unambiguously find it.
private string WriteHtmlUnambiguous<T>(string target, T source)
{
    return WriteHtml<T>(target, source, null);
}

Just for your entertainment. Here's the full code for HierarchicalSelect:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using System.Web;
using System.Web.UI;
using System.Linq;

namespace Hadlow.PropertyFinder.HtmlHelpers
{
    public class HierarchicalSelect
    {
        // NVelocity doesn't support generic methods :(
        // we have to take an object argument and then construct one of the generic methods first
        // before calling it.
        public string WriteHtml(string target, object source)
        {
            Type sourceType = source.GetType();
            
            // get our unambiguous generic method
            MethodInfo writeHtmlMethod = typeof(HierarchicalSelect).GetMethod("WriteHtmlUnambiguous", 
                BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance);

            // create a typed version
            MethodInfo typedWriteHtmlMethod = writeHtmlMethod.MakeGenericMethod(sourceType);
            
            // invoke it.
            return (string)typedWriteHtmlMethod.Invoke(this, new object[] { target, source });
        }

        // need to provide this so that GetMethod can unambiguously find it.
        private string WriteHtmlUnambiguous<T>(string target, T source)
        {
            return WriteHtml<T>(target, source, null);
        }

        public string WriteHtml<T>(string target, T source)
        {
            return WriteHtml<T>(target, source, null);
        }

        public string WriteHtml<T>(string target, T source, IDictionary attributes)
        {
            string id = CreateHtmlId(target);
            string name = target;

            StringBuilder sb = new StringBuilder();
            StringWriter sbWriter = new StringWriter(sb);
            HtmlTextWriter writer = new HtmlTextWriter(sbWriter);

            writer.WriteBeginTag("select");
            writer.WriteAttribute("id", id);
            writer.WriteAttribute("name", name);
            writer.Write(" ");
            writer.Write(GetAttributes(attributes));
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.WriteLine();

            if (source != null)
            {
                HierarchicalThing<T> root = new HierarchicalThing<T>(source);
                WriteOptions(writer, root, 0);
            }

            writer.WriteEndTag("select");

            return sbWriter.ToString();
        }

        private void WriteOptions<T>(
            HtmlTextWriter writer, 
            HierarchicalThing<T> item, 
            int level)
        {
            writer.WriteBeginTag("option");

            //if (item.IsSelected)
            //{
            //    writer.Write(" selected=\"selected\"");
            //}

            writer.WriteAttribute("value", HttpUtility.HtmlEncode(item.Id));
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.Write(GetLevelString(level) + HttpUtility.HtmlEncode(item.ToString()));
            writer.WriteEndTag("option");
            writer.WriteLine();

            level++;
            foreach (HierarchicalThing<T> child in item.Children)
            {
                WriteOptions(writer, child, level);
            }
        }

        private string GetLevelString(int level)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < level; i++)
            {
                sb.Append("    ");
            }
            return sb.ToString();
        }

        private string CreateHtmlId(string name)
        {
            StringBuilder sb = new StringBuilder(name.Length);

            bool canUseUnderline = false;

            foreach (char c in name.ToCharArray())
            {
                switch (c)
                {
                    case '.':
                    case '[':
                    case ']':
                        if (canUseUnderline)
                        {
                            sb.Append('_');
                            canUseUnderline = false;
                        }
                        break;
                    default:
                        canUseUnderline = true;
                        sb.Append(c);
                        break;
                }

            }

            return sb.ToString();
        }

        private string GetAttributes(IDictionary attributes)
        {
            if (attributes == null || attributes.Count == 0) return string.Empty;

            StringBuilder contents = new StringBuilder();

            foreach (DictionaryEntry entry in attributes)
            {
                if (entry.Value == null || entry.Value.ToString() == string.Empty)
                {
                    contents.Append(entry.Key);
                }
                else
                {
                    contents.AppendFormat("{0}=\"{1}\"", entry.Key, entry.Value);
                }
                contents.Append(' ');
            }

            return contents.ToString();
        }

        /// <summary>
        /// Represents a hierarchial object with one property that has a type of 
        /// IList<sametype>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        private class HierarchicalThing<T>
        {
            readonly T source;
            readonly PropertyInfo childrenProperty;
            readonly string id;

            public string Id
            {
                get { return id; }
            }

            public HierarchicalThing(T source)
            {
                this.source = source;
                Type sourceType = source.GetType();

                childrenProperty = FindChildProperty(sourceType);
                id = FindIdValue(source);

                if (childrenProperty == null)
                {
                    throw new ApplicationException("The source object must have a property that " +
                        "represents it's children. This property much have a type of IList<source type>. " +
                        "No such property was found in type: " + sourceType.Name);
                }
            }

            private string FindIdValue(T source)
            {
                return source.GetType().GetProperties().First(p =>
                    p.GetCustomAttributes(true).Any(a =>
                        a is Castle.ActiveRecord.PrimaryKeyAttribute)
                        ).GetValue(source, null).ToString();
            }

            private PropertyInfo FindChildProperty(Type sourceType)
            {
                return sourceType.GetProperties().First(p => p.PropertyType == typeof(IList<T>));
            }

            public HierarchicalThing<T>[] Children
            {
                get
                {
                    List<HierarchicalThing<T>> children = new List<HierarchicalThing<T>>();
                    IList<T> childValues = (IList<T>)childrenProperty.GetValue(source, null);
                    foreach (T childValue in childValues)
                    {
                        children.Add(new HierarchicalThing<T>(childValue));
                    }
                    return children.ToArray();
                }
            }

            public override string ToString()
            {
                return source.ToString();
            }
        }
    }
}

Sunday, November 25, 2007

Developer Developer Developer Day 6

I had a great time at DDD6 on Saturday. The nicest thing for me was getting to meet and talk to some of the most highly regarded people in the UK developer community like Dave Sussman, Dave Verwer, James Winters and Paul Lockwood. There were also a few familiar faces from Brighton there: Simon Harriyot (of Sussex Geek Dinners fame), Jane Dallaway from Madgex and Steve Mason from Cubeworks.

My talk 'Why do I need an Inversion of Control Container' got off to a bad start when I discovered that there was no way of attaching my Macbook's DVI plug to the projector. I should have known there would be some bad voodoo bringing a Mac into the heart of Microsoft, or thinking again, maybe I just should have brought the right plug. Duh and double duh! I was saved by Phil Winstanley who very kindly jumped in and lent me his laptop and helped me get up and running, thanks Phil! Ten minutes of transferring files and installing Testdriven.net later,everything worked and I was able to start. I must admit the panic meant that I was more than a bit flustered, I missed a whole load of good points I wanted to make and my tendency to talk too fast was probably attenuated somewhat.

Luckily it was an excellent crowd with some great questions. By the end of the talk I was really enjoying myself. Thanks to everyone who came up to talk afterwards, I really appreciated your feedback, especially the lady who pointed out that I hadn't said where you can get the Castle Windsor download from. Duh again! In order to rectify that oversight, you need to install the entire Castle Project which you can get here:

http://www.castleproject.org/castle/download.html

Here are the slides and demo code. The code requires the castle project and NUnit to work.

http://static.mikehadlow.com/Mike.IocDemo.zip

There should also be a video at some point; watch this space.

Tuesday, November 20, 2007

Hammett on Microsoft's MVC Framework

Here's an interesting post by Hammett, the leader of the Castle Project and writer of the Monorail MVC framework. He was invited up to Redmond to play with the MS MVC Framework. His attitude is very mature; he says that he won't bother with Monorail 2.0 if everything he wants from an MVC framework is supplied by MS. There's obviously a great deal of mutual respect going on. I wonder how long before Hammett's working for The Beast?

Installing Visual Studio 2008

Visual Studio 2008 is now available to MSDN subscribers. I downloaded it over night and have just started the install on Vista Ultimate. It's taking a long time and is currently stuck on installing Framework 3.5 with the progress bar at 0% for more than ten minutes.

... Later, it's done. But that was the longest install I've experienced for a long long time! And I have to restart Vista.

Thursday, November 15, 2007

Ian Cooper on MVC

Ian Cooper (of the London .NET User Group fame) has a very thoughtful series of posts on Model View Controller architecture:

Part 1

Part 2

Part 3

He's also doing a session on Monorail at DDD6. Which should be most interesting.

It's funny, but Microsoft's announcement of their MVC framework has raised more interest in Monorail that it could ever have on it's own. Previously it was a well regarded, but slightly obscure web development framework, but now it's got the 'copied by Microsoft' endorsement, everyone's talking about it.

Sunday, November 11, 2007

An important new .NET language: LOLCODE

I've just come across this fantastic new langauge: LOLCODE. I'm probably a little too old to ever be an expert at it because it's designed specifically for people who have grown up with texting; people who can succinctly express their ideas in abbreviated form. In true Code Rant 'love of fibonacci' fashion, here's a fib sample from John Lam's blog:

HAI
CAN HAS STDIO?
I HAS A FISH ITZ "Yummy"
VISIBLE FISH
VISIBLE "HAI WORLD!"

I HAS A FIB
I HAS A A ITZ 1
I HAS A B ITZ 0

GIMMEH FIB

IM IN YR LOOP
 VISIBLE B

 IZ FIB SMALR 1?
 YARLY
   GTFO
 NOWAI
   VISIBLE "NOWAI"
 KTHX

 I HAS A TEMP ITZ A UP B
 LOL A R B
 LOL B R TEMP

 NERFZ FIB!!
KTHX
KTHXBYE

No, it's not a joke (well not entirely), this is a real .NET language written with the DLR.

The MVC framework and new C# 3.0 features

You can't get hold of the MVC framework itself yet, but Scott Hanselman has posted the demo code used to demonstrate it at DevConnections and the PNPSummit. Browsing the routing demo code initially left me quite confused until I realized that it was using a couple of new C# 3.0 features: object initializers and anonymous types to initialize the routing engine.

using System;
using System.Web.Mvc;

namespace HelloWorld {
    public class Global : System.Web.HttpApplication {
        protected void Application_Start(object sender, EventArgs e) {

            // Custom "pretty" route
            RouteTable.Routes.Add(new Route {
                Url = "products/[category]",
                Defaults = new {
                    controller = "sample",
                    action = "ShowProducts",
                    category = "beverages"
                },
                RouteHandler = typeof(MvcRouteHandler)
            });

            // Default route
            RouteTable.Routes.Add(new Route {
                Url = "[controller]/[action]/[id]",
                Defaults = new { action = "Index", id = (string)null },
                RouteHandler = typeof(MvcRouteHandler)
            });
        }
    }
}

It initially looks like named arguments, but in fact it's creating a new anonymous type with the named properties. But how does the Route object parse its Defaults property? Does it have to use reflection to find the properties of the Defaults type, or am I missing something?

Friday, November 09, 2007

T-SQL Fibonacci and the power of 'With'

It's amazing how things can pass you by. I only found out about the recursive power of the T-SQL 'with' statement today. With it you can easily write a Fibonacci sequence generator:

with fib(a, b) as
(
 select 0, 1
 union all
 select b, a+b from fib where b < 100
)
select a from fib

Here's the output:

a
-----------
0
1
1
2
3
5
8
13
21
34
55
89

It really comes into its own when you've recursive structures such as a tree in your schema. Take this extremely typical example: a hierarchy of locations. Each record has a primary key 'id' and a foreign key 'parentId' that references another record in the same table.

locationTable

Here are some records:

locationRecords

Say I wanted to output all the locations with their level in the hierarchy. Easy:

with locationLevel(id, [name], [level]) as
(
 select id, [name], 0 from location where parentId is null
 union all
 select location.id, location.[name], locationLevel.[level]+1
 from location
 join locationLevel on location.parentId = locationLevel.id
)
select * from locationLevel

id          name                 level
----------- -------------------- -----------
0           World                0
1           UK                   1
2           France               1
5           Paris                2
6           Rouen                2
3           London               2
4           Brighton             2

Or when I search for a particular location, I want to find all its ancestors:

with locations(id, parentId, [name], targetName) as
(
 select id, parentId, [name], [name] from location
 union all
 select location.id, location.parentId, location.[name], locations.[targetName]
 from location
 join locations on location.id = locations.parentId
)
select id, [name] from locations where targetName = 'Rouen'

id          name
----------- --------------------
6           Rouen
2           France
0           World