Development Blog

 Tuesday, March 27, 2007

Update: Hamilton committed my patch, so if you're running off trunk you won't need to do this anymore. Thanks Hamilton!

Currently MonoRail uses ResourceSets wrapped up in an implementation of IResource to provide Resources to views. Unfortunately, ResourceSets do not cascade when it comes to resource resolution.  ResourceManager.GetResourceSet does have a flag called tryParents, but all that does is try less and less specific cultures until it finds a match. In other words, if you have two resource files:
Foo.ko.resx: defines A, B
Foo.resx: defines A, B, C

If you locale is ko-KR, calling RseourceManager.GetResourceSet will yield a ResourceSet that maps to Foo.ko.resx, so asking for B will work, but asking for C will not. A ResourceSet only knows about itself.

ResourceManagers on the other hand, are perfectly capable of handling this cascade, which is quite necessary in at least our localized app, as we do not want to have to define strings in all languages for everything. So with ResourceManagers, asking for A, B, and C all behave as expected, preferring the most specified culture and cascading down as necessary.

Below is an implementation of an IResourceFactory that spits out wrapped ResourceManagers instead of ResourceSets.

To use it you'll need to add this to your web.config:

<monorail>
  <services>
    <service id="ResourceFactory" type="Eleutian.Shared.MonoRail.ResourceManagerFactory, Eleutian.Shared" />
  </services>
</monorail>

Source

by Aaron on Tuesday, March 27, 2007 4:13:12 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, March 24, 2007

If you're reading this, you're probably interested in some of the stuff we're doing, which means we're probably interested in you. We're currently looking for Software Developers and Testers in Seattle. If you'd like to join our team, drop us an email with your resume to jobs@eleutian.com.

by Aaron on Saturday, March 24, 2007 4:11:15 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback

I'm sure you've all heard that the best developers are lazy and/or dumb. I certainly agree with that. Also, you know that writing repetitive code and repeatedly following tedious steps is bad and a waste of time.

OK, so we know that those things are bad... but how do you identify them? Often tedious steps and repetitive code is taught as the way you do things... so you just do them in that way without asking questions. Other times, we just don't know that there is an easier way. We're developers--we're creative, smart, inventive, and we should be able to tell when something can and should be done in a better way. 

Let me give you an example of some code I came across some time ago:

Public userstring As String = "U000000"

Private Function GetUserIDStr(ByVal userid As Integer) As String
  'Returns String:
  '   User Info FileName
  Dim source As String = userid.ToString()
  'Dim destination As Char() = {"U"c, "0"c, "0"c, "0"c, "0"c, "0"c, "0"c}
  Dim destination As String = Me.userstring
  Dim sourcelen As Integer = source.Length()
  Dim destindex As Integer = 7 - sourcelen
  source.CopyTo(0, destination, destindex, sourcelen)
  Return destination
End Function

Obviously there are several things... off... with this code. Let's discuss some of them. The first is the original Dim destination that is now commented out. This is an example of someone partially applying what I'm talking about. They realized that typing out that array was unnecessarily complicated so they did something about it. That's great, that's what we need to do. Unfortunately, they stopped there. I don't think we should stop there. We should look at the code, say, wow, this isn't really doing anything but formatting a number into a string. There has to be a better way to do this! In this case there certainly is... all of that code can be replaced with something along the lines of:

Return String.Format('U{0:D06}', userid)

Which is easier to read? Write? Maintain? The answer is obvious... even if you didn't know about String.Format, you should know when you're writing that code that there has to be a better way. Google is your friend.

What if there isn't a better way? Can you think of a better way? Ask yourself these questions:

  • Are you going to be doing this more than a few times?
  • Are other people on your team going to be doing it?
  • Are you looking for a break from your normal work?

If the answer to any of those questions is yes, take some time to write or come up with a better way. You're a developer. You develop. Remember, you can write software for yourself and your teams that will make writing software more enjoyable and faster. And then, when you're done, share it! Share it with the world, make development easier for everyone.

I'll give you another example. I've already blogged about it, but it's relevant to this post so I'll rehash here. In order to localize a string in MonoRail you must do the following:

  1. Create a new resx file.
  2. Add a Resource attribute to your controller mapping the resx to a key in the PropertyBag.
  3. For each string you want to localize, add a key/value pair to the resx file.
  4. Replace the strings in your view template with your resource key.

Hm. Every controller? Every string? Talk about context switching. Not to mention the fact that you can't see the actual English string in your view template, so any modifications to the English string require the same context switching. It didn't take me long to decide that was far too tedious for myself and my team.

It probably took me 2-3 days overall to write both the ASP.NET preprocessor and adapt it to MonoRail/Brail. Will I ever get 2-3 whole days back? Who knows. Will my entire team in total? More likely. Does it save us countless context switches, speed up our development, and make localization trivial? Absolutely. So was it worth it? Absolutely.

And before you ask, yes I plan on taking my own advice and sharing it... eventually. Oh and keep in mind, this sort of thinking can and should be applied to everything you do, including but not limited to code, tools, and process.

by Aaron on Saturday, March 24, 2007 3:46:12 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, March 22, 2007

In a previous post I talked about how we use automatically generated interfaces to wrap our PropertyBag's. You can also use this technique in your ViewComponents to clean up that code quite a lot. We used to have code like this:

public class SomeComponent : ViewComponent
{
  public override void Initialize()
  {
    base.Initialize();
    User user = (User)this.ComponentParams["User"];
    float value = this.Service.CalculateThatValue(user);
    this.ComponentParams["SomeCalculatedValue"] = value;
  }
}

Now we can make an interface:

public interface ISomeComponentView
{
  User User { get; set; }
  float SomeCalculatedValue { get; set; }
}

And ask the code we wrote for the PropertyBag to give us a hand in cleaning things up:

public class SomeComponent : ViewComponent
{
  public override void Initialize()
  {
    base.Initialize();
    ISomeComponentView view = this.ViewFactory.ResolveView<ISomeComponentView>(this.ComponentParams);
    float value = this.Service.CalculateThatValue(view.User);
    view.SomeCalculatedValue = value;
  }
}

Much better! If you're looking for an implementation of that ViewFactory service, Lee Henson tossed one together shortly after our post. He's also added some other, pretty nitfy features since then. Like the ability to specify a prefix with an attribute for the generated dictionary keys. As he mentions in his post, you can use it for anything (and probably should) that uses strings as keys into an IDictionary (Session, Flash, etc..). I'm planning on checking it in to CastleContrib soon, until then, grab it off the list. Thanks Lee!

by Jacob on Thursday, March 22, 2007 9:21:27 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, March 16, 2007

Last week, Aaron and I were hanging out with a bunch of the Microsoft MVP's that were up in Seattle for the MVP Summit. We had an absolute blast and got to meet some excellent developers. It was a great opportunity to get a sense for how others in our situation were handling similar problems and to share things that we have all learned along the way. We had a few beers at the Party with Palermo event and the following day a few of the guys dropped by the Eleutian Seattle offices and we threw together a podcast with Scott Hanselman. It's a very quick introduction to MonoRail for those that aren't familiar. We go on to discuss testability and IoC in the context of the Castle project. It was a great chance to expose MonoRail and the concepts behind it to a wider audience.

In no particular order, here are some blogs if you're interested in some good reading on various areas of software development:

  • Scott Bellware - Development practices, C# 3.0, Ruby, and more. His company, Dovetail, is looking into starting development using MonoRail. They are hiring.
  • Jeremy Miller - Agile, especially TDD, IoC, etc...  Jeremy wrote StructureMap, an IoC framework. So, he definitely knows the why behind a lot of the agile practices.
  • Scott Hanselman - Anything and everything development and technology. I always find something new here. I'm sure you will also.

I'm glad to see some of the news that came out of the summit. I'm sure one of us will be back to expound on them once we've done a little more research and tinkering.

by Jacob on Friday, March 16, 2007 10:56:57 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, March 01, 2007

I needed the ability to execute a task from an MsBuild project and have MsBuild keep going and not wait around on the process to exit. I was hoping to find an option on the Exec task for this but that search turned up nothing. After scowering a bit I couldn't find one somebody else wrote, so here we go:

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;

using Microsoft.Build.Tasks;

namespace AsyncExec
{
  public class AsyncExec : Exec
  {
    #region Task Members
    protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands)
    {
      Process process = new Process();
      process.StartInfo = GetProcessStartInfo(pathToTool, commandLineCommands);
      process.Start();
      return 0;
    }
    #endregion

    #region Methods
    protected virtual ProcessStartInfo GetProcessStartInfo(string executable, string arguments)
    {
      if (arguments.Length > 0x7d00)
      {
        this.Log.LogWarningWithCodeFromResources("ToolTask.CommandTooLong", new object[] { base.GetType().Name });
      }
      ProcessStartInfo startInfo = new ProcessStartInfo(executable, arguments);
      startInfo.WindowStyle = ProcessWindowStyle.Hidden;
      startInfo.CreateNoWindow = true;
      startInfo.UseShellExecute = true;
      string workingDirectory = this.GetWorkingDirectory();
      if (workingDirectory != null)
      {
        startInfo.WorkingDirectory = workingDirectory;
      }
      StringDictionary environmentOverride = this.EnvironmentOverride;
      if (environmentOverride != null)
      {
        foreach (DictionaryEntry entry in environmentOverride)
        {
          startInfo.EnvironmentVariables.Remove(entry.Key.ToString());
          startInfo.EnvironmentVariables.Add(entry.Key.ToString(), entry.Value.ToString());
        }
      }
      return startInfo;
    }
    #endregion
  }
}

Don't fret over the WindowStyle and CreateNoWindow flags, Exec works by spawning cmd.exe with a batch file that it generates with our command. Without those flags, the command prompt window flashes by.

Obviously, very rough stuff and I make no promises of it's abilities. It worked for the simple example below and that's all I wanted. Ideally, the task would ensure the executable being started exists so we could at least fail if that's the case. But, for now this works for us. It's only used in interactive situations to spawn NCoverExplorer and things like thing. We can already easily spawn the HTML reports by using:

<Exec Command="$(ArtifactsDirectory)\mbunit-tests.html" />

But that just doesn't work when you need to run an application and not care about when it's closed:

<AsyncExec Command="$(ToolsDirectory)\NCoverExplorer\NCoverExplorer.exe $(ArtifactsDirectory)\coverage.xml" />

So here's the binaries in case somebody else is curious as well as the UsingTask.

<UsingTask AssemblyFile="AsyncExec.dll" TaskName="AsyncExec.AsyncExec" />

Obviously, if something like this is already easily possible and this was a half hour wasted, let me know. Especially if it's possible "out-of-the-box". I'm always glad to have one less dependency.

Source and Binaries

source | tools | msbuild
by Jacob on Thursday, March 01, 2007 12:52:18 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Tuesday, February 27, 2007

Martin Fowler has a great post entitled Mocks Aren't Stubs. Go ahead and read/skim through that if you haven't. Of the two categories of TDDers he defines, I am definitely a mockist. That is, I almost always "use a mock for any object with interesting behavior". I do use Mocks and Stubs, but those stubs are generally just Mocks set up to be stubs (as it's easier than stubbing a class myself.) That said, I do have a few ground rules for when I write tests with Rhino Mocks that sort fall into the whole one assertion per test topic and the mocks vs. stubs thing. Here they are:

  1. Name TestMethods MethodOrPropertyName_Scenario_ExpectedResult. I stole this from someone, but I can't remember who. If you know what post that was, let me know so I can link it. An example is: CalculateCost_WhenProductIsNull_ThrowsException. I just think this makes your intent very clear and readable.
  2. Never use _mocks.Ordered() unless you are actually testing the order in which something is called... and if you are, test only that!
  3. Unless you're testing that something gets called once and only once (expensive method? caching?), use Repeat.Any(). Who cares if it gets called again?
  4. Prefer DynamicMock over CreateMock (standard mocks). Here's an example. You're testing that your controller sets FirstName on a view. You write that test and implement the feature. Two days later you need to add EmailAddress to that view, so you write your test to ensure that it is added. You implement it and run your tests... now, if you used a standard mock, your original FirstName test would fail! With a DynamicMock however, you have no tests to "fix" (tests should rarely need to be fixed because you added something to the class that has no bearing on the originally tested scenario)
  5. Don't use VerifyAll. What!?!? That's right, half the time I use Verify(someMock) and the other half I don't even Verify anything (as I can verify the result of a method called). Your tests should be complete enough that it shouldn't matter if ObjectA called ObjectB as long as ObjectA gives you the correct result.

Remember, Verification, Ordering, setting a fixed # of repeats, requiring every method/property to be "expected", etc, are all Assertions. I'm not steadfast on the whole one assertion per test thing, as sometimes a few assertions make sense. As long as you at least think about what your test would be named if you put every assertion in the method name of the test, I think that a lot of these rules would make more sense.... Would you rather diagnose a break in SomeMethod_WhenSomeObjectIsGreen_CallsFooTwiceThenCallsBarWithResultOfFoo
OnceThenSetsColorAndShapeAndResultOfFoo() or SomeMethod_WhenSomeObjectIsGreen_SetsColor()? :)

by Aaron on Tuesday, February 27, 2007 12:13:04 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Sunday, February 25, 2007

Roy Osherove brings up several good points with regards to the preached design methodology of old and the new testable design some of us have come to know and love lately.

I think that TDD really helps to eliminate a large number of these issues. If you test everything yourself, then inherently your API is testable. Unfortunately, as Jonathan Cogley points out, TDD is not yet mainstream.

A nice side effect of having a testable API or application is that often times your code is also extensible. The thing that blocks it from being fully testable and extensible are those darn sealed and internal keywords, and of course, not making things virtual that really should be. Even open source projects are guilty of not having testable or extensible enough APIs. Yes, Castle MonoRail provides AbstractMRTestCase, but that actually uses ASP.NET and is way more heavy than I like.

Here is our base class for Controller Tests. This allows you to unit test nearly every aspect of your controller, without ever hitting ASP.NET. Notice the lovely and necessary use of reflection? In order to mock the context, you'll want to call InitializeController and pass it the controller you're testing, but be sure to do it in your _mocks.Unordered block (we almost ALWAYS use mocks.Unordered... it makes tests more readable and ensures that you're tests aren't too tightly coupled with your implementation, but that's the subject for another post.)

  public class EleutianControllerTests
  {
    #region Constants
    private const string ApplicationPhysicalPath = "Q:\\PhysicalPath";
    #endregion

    #region Member Data
    protected string _virtualDirectory = String.Empty;
    protected MockRepository _mocks;
    protected IControllerServices _controllerServices;
    protected IRailsEngineContext _context;
    protected IRequest _request;
    protected IResponse _response;
    protected IServerUtility _serverUtility;
    protected IDictionary _session;
    protected Flash _flash;
    protected NameValueCollection _parameters;
    #endregion

    #region Test Setup and Teardown Methods
    [SetUp]
    public virtual void Setup()
    {
      _mocks = new MockRepository();
      _context = _mocks.CreateMock<IRailsEngineContext>();
      _request = _mocks.DynamicMock<IRequest>();
      _response = _mocks.DynamicMock<IResponse>();
      _serverUtility = _mocks.DynamicMock<IServerUtility>();
      _session = _mocks.DynamicMock<IDictionary>();
      _flash = new Flash();
      _parameters = new NameValueCollection();
    }

    protected void InitializeController(Controller controller, string areaName, string controllerName, string actionName)
    {
      BindingFlags bindingFlags = BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic |
                                  BindingFlags.InvokeMethod;

      MethodInfo method = controller.GetType().GetMethod("InitializeControllerState", bindingFlags);
      method.Invoke(controller, new object[] { areaName, controllerName, actionName });

      FieldInfo field = controller.GetType().GetField("context", bindingFlags);
      field.SetValue(controller, _context);

      field = controller.GetType().GetField("serviceProvider", bindingFlags);
      field.SetValue(controller, _context);

      InitializeRailsContext(areaName, controllerName, actionName);
    }

    protected void InitializeRailsContext(string areaName, string controllerName, string actionName)
    {
      Expect.Call(_context.UrlInfo).Return(
        new UrlInfo("eleutian.com", "www", _virtualDirectory, "http", 80,
                    Path.Combine(Path.Combine(areaName, controllerName), actionName), areaName, controllerName,
                    actionName, "rails")).Repeat.Any();
      Expect.Call(_context.GetService(typeof(IUrlBuilder))).Return(new DefaultUrlBuilder()).Repeat.Any();
      Expect.Call(_context.Server).Return(_serverUtility).Repeat.Any();
      Expect.Call(_context.Flash).Return(_flash).Repeat.Any();
      Expect.Call(_context.ApplicationPath).Return("/").Repeat.Any();
      Expect.Call(_context.Request).Return(_request).Repeat.Any();
      Expect.Call(_context.Response).Return(_response).Repeat.Any();
      Expect.Call(_context.ApplicationPhysicalPath).Return(ApplicationPhysicalPath).Repeat.Any();
      Expect.Call(_request.Params).Return(_parameters).Repeat.Any();
      Expect.Call(_context.Session).Return(_session).Repeat.Any();
    }
    #endregion
  }

Oh, and back to the original point of the post. Please, please don't mark things internal or sealed unless you have a very good reason to do it. And no, I don't think that "We can't afford to support it" is a good enough reason. Also, "We're afraid the user might break something" is DEFINITELY not a good enough reason. We know what we're getting into when we extend APIs... we're all developers here.

by Aaron on Sunday, February 25, 2007 4:16:47 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [4]  |  Trackback