Several months ago the Dovetail team realized that our various OSS projects and our main codebase were starting to show a lot of duplication of utilities and extension methods. At one time ReSharper could find 6 different versions of a Cache<TKey, TValue> utility that we had originally written for what became Fluent NHibernate and at least 4-5 versions of the pesky IEnumerable<T>.Each() extension method that we all add to our new projects on day 1 (because some ivory tower guy at Microsoft had a dogmatic objection to that very extension and refused to put it into the BCL where it belongs). Worse yet was the fact that I was finding myself needing to fix certain extension methods across several different projects. The obvious answer was to consolidate many of our commonly used utilities and extension methods into a common library which we called “FubuCore” in the greater FubuMVC project. I think there’s some potentially useful stuff in there that other teams could use or contribute to, so here’s to the start of a new series on the FubuCore library starting with the sleek, sexy ObjectConverter class that I use heavily in StoryTeller and our Dovetail code:
public interface IObjectConverter { // Given a string and a .Net type, read this string // and give me back a corresponding instance of that // type object FromString(string stringValue, Type type); T FromString<T>(string stringValue); bool CanBeParsed(Type type); }
The real class behind this deceptively simple interface can take a string value and return to you an instance of the type you requested. Pretty awesomely boring, right? The TypeDescriptor and TypeConverter classes built into the BCL already does some of this (and by default ObjectConverter delegates to TypeDescriptor to handle most simple types), but they’re somewhat clunky to use. No, the cool part about the underlying ObjectConverter object is that you can teach it how to convert a string to an entirely new type that you just created. I always thought the old fashioned class oriented approach that FitNesse took was clumsy and high ceremony, so I decided to just register a Lambda of type Func<string, T> for a given T like this method on ObjectConverter:
public void RegisterConverter<T>(Func<string, T> finder) { _froms[typeof(T)] = x => finder(x); }
The value of the Lambda approach is that you can add a small strategy to ObjectConverter inline without going to the trouble of defining another class that’ll live somewhere off to the side.
Putting ObjectConverter to Work
Let’s say that you have a value type in your system called Contact like this:
public class Contact { public string FirstName { get; set; } public string LastName { get; set; } }
And you want to represent this in string form as “FirstName LastName” like “Jeremy Miller.” You can quite happily teach ObjectConverter how to convert a string to a Contact object with the code from this unit test:
[Test] public void register_and_retrieve_a_new_type_of_complex_object() { var finder = new ObjectConverter(); finder.RegisterConverter<Contact>(text => { var parts = text.Split(' '); return new Contact(){ FirstName = parts[0], LastName = parts[1] }; }); var c = finder.FromString<Contact>("Jeremy Miller"); c.FirstName.ShouldEqual("Jeremy"); c.LastName.ShouldEqual("Miller"); }
Simple enough, eh? Now, how about if we want to get an array (or IEnumerable) of Contact objects from a string? No problem, by default ObjectConverter can take a comma delimited string and convert each value into a Contact like this:
[Test] public void how_about_getting_an_array_of_those_complex_objects() { // Same converter as before var finder = new ObjectConverter(); finder.RegisterConverter<Contact>(text => { var parts = text.Split(' '); return new Contact() { FirstName = parts[0], LastName = parts[1] }; }); // Now, let's pull an array of Contact's var contacts = finder.FromString<Contact[]>("Jeremy Miller, Rod Paddock, Chad Myers"); contacts.Select(x => x.LastName) .ShouldHaveTheSameElementsAs("Miller", "Paddock", "Myers"); }
Look at that, once I defined how to find a Contact from a string, ObjectConverter now knows how to handle an array (or IEnumerable / IList / List) of Contact’s as well. That little feature has been very helpful to me in creating better StoryTeller DSL’s.
“Finding” Objects
I originally wrote ObjectConverter (née ObjectFinder in older versions) as a utility internal to the new StoryTeller testing engine. I knew from my prior experiences with FitNesse that I’d need to frequently take test input data that would be stored as strings and convert that string to the real .Net types that the test code would need to do its job. I also knew that I’d need to allow users to “teach” StoryTeller how to convert (or find) new types of objects from the domain of the system under test so that StoryTeller could take a set of key/value pairs of strings and turn that into the correct arguments to call a method like this one from Dovetail test harness code:
// 'User' and 'Site' are both Entity types // in our system [FormatAs("User {user} is at Site {site}")] public void UserIsAtSite(User user, Site site) { user.Site = site; _system.Save(user); }
In the StoryTeller tests we can just specify the user name and the site name, but first we had to teach ObjectConverter how to find a user and site from a string with a function that:
- First tries to look up an existing User by the specified user name, and if it exists, return they existing user object
- If the user doesn’t already exist, create a new User with that user name and some default data, save it, and return the new User object
Using ObjectConverter within StoryTeller tests like this has done a world of good for us by making test input setup less laborious and making our tests much easier to understand and read because there’s less “noise” in the “Arrange” part of our StoryTeller tests.
Family of Types in One Swath
Instead of explicitly telling ObjectConverter how to deal with each specific type, you might be able to make your life easier by registering a single IObjectConverterFamily policy that can tell the ObjectConverter how to deal with several types at once. Let’s just look at the interface:
public interface IObjectConverterFamily { // ObjectConverter calls this method on unknown types // to ask an IObjectConverterFamily if it "knows" how // to convert a string into the given type bool Matches(Type type, IObjectConverter converter); // If Matches() returns true for a given type, // ObjectConverter asks this IObjectConverterFamily // for a converter Lambda and calls its // RegisterFinder() method behind the scenes to cache // the Lambda for later usage Func<string, object> CreateConverter(Type type, Cache<Type, Func<string, object>> converters); }
Deep in the guts of our proprietary (sounds special if you say it like that) rules engine we frequently need to reference an entity as part of a Json blob and find the correct entity later (think of rules like “send this case over to the urgent queue” where the Queue is an Entity in our Domain Model). Like many projects we use a Layer Supertype class for our Entity objects for all the basic properties like Id. Since all our Entities are identified the same way, we can tell an instance of ObjectConverter to convert a string to any type that inherits from Entity by looking up that entity in our Repository by Id in this code:
public class DomainEntityConverterFamily : IObjectConverterFamily { private readonly IRepository _repository; public DomainEntityConverterFamily(IRepository repository) { _repository = repository; } // Matches any type deriving from DomainEntity // CanBeCastTo<> is an extension method in FubuCore as well public bool Matches(Type type, IObjectConverter converter) { return type.CanBeCastTo<DomainEntity>(); } // In this case we find the correct object by looking it up by Id // from our repository public Func<string, object> CreateConverter(Type type, Cache<Type, Func<string, object>> converters) { return text => { var guid = new Guid(text); return _repository.Find(type, guid); }; } }
I can register the conversion family by just calling the RegisterConverterFamily<T>() method on ObjectConverter.
What else can ObjectConverter do?
Rather than make this blog post longer than its already absurd length, I’d invite you to simply look at the unit test class for ObjectConverter here. If you’re interested in that sort of thing, I’d put ObjectConverter forward as a somewhat advanced example of using first class functions as the primary unit of composition.