Software Development07 Dec 2011 12:43 am

When using version control and working on a branch other than the master, one should periodically pull any changes made to master into the working branch. If you are using Git, the preferred way to do this is with the rebase command rather than merge. (Here is a good explanation of the difference between the two and why rebase is usually preferred.)

However, rebase sometimes does not work as you would like. I hit a good example of this this evening and wanted to document it. The scenario is that of working on my own working branch of a rails project where we had just started using RubyMine. I had created my working branch just prior to switching to RubyMine. To my chagrin, I discovered that RubyMine had created a bunch of files to manage its view of the project in a folder named .idea, and I had committed them in my branch.

In the meantime another developer discovered the same issue and coincidentally added the .idea directory to .gitignore on master. The natural thing to do, I thought, was to rebase my branch on master. I was promptly told that I could not do this. I failed to capture the exact error message, but basically the problem was that rebase was attempting to replay the addition of files to the .idea directory on top of a git repository that now called for the .idea directory to be ignored.

Because I only had a few commits on my branch, I decided the quick solution would be to create a new working branch based on the current master and then cherry-pick my commits from the old working branch onto the new one.

My first cherry-pick command resulted in a “commit not found error.” I thought this was very odd, but in examining the contents of the commit I was attempting to cherry-pick, it turns out that it only contained files in the .idea directory. So it seems a better error message would have been “nothing found in the commit.”

Seeing as I could safely omit that commit, I went on to cherry-pick the remaining ones. Each failed initially due to merge errors, because each attempted to modify files under .idea. To solve this I had to execute a git rm -f <file> command for each such file to resolve the conflict.

After resolving the conflict, I followed the onscreen instructions that were given at the point that the cherry-pick failed. Namely to run the commit -c <hash> command specifying the hash of the commit I had cherry-picked.

The -c option creates a new commit with the same message and author as the one referenced by the hash. I had not seen this option before and as it turns out I really wanted to use the capitalized -C version. Both do the same thing but the lowercase version throws you into vi to edit the commit message where as the uppercase one uses the commit message as-is.

Software Development20 Nov 2011 12:40 pm

In a recent design session, we were discussing the API for an event bus that supports asynchronous RPC. A colleague of mine proposed a fluent interface. A simplified version of it looked roughly like this:

bus.for(event).replyTo(handler).withATimeOutOf(500).Send();

The proposal lead to some debate because some members of the team liked the idea and others preferred the more traditional approach of using overloaded methods as follows.

bus.Send(event, replyHandler);
bus.Send(event, replyHandler, timeout);

In the end it was agreed that the traditional API was essential, and if some of the team wanted a fluent API, one could be written as a wrapper around it.

I think this was the right call and the point of this post is to explain why. One of the reasons that was given against the fluent interface was that they are very hard to extend if you do not have the ability to modify the original source. In short, they violate the Open/Closed Principle, one of the core principles of SOLID object oriented design.

Lets take a look at why.

Here is a pseudo implementation of the fluent API.

class Builder
{
    Builder for(IEvent event) { ... ; return this }
    Builder withReplyTo(IHandler handler) { ... ; return this }
    Builder withATimeOutOf(int milliseconds) { ... ; return this }
    void send() { ... }
}

Omitted is the operative code that would collect the various arguments and ultimately invoke the base API in Send(). What remains is just what is needed to do the fluent call chaining, namely returning an instance of the builder itself. It is this return value that violates OCP.

Suppose a third party wants to add a second event handler to be invoked in case of a timeout. We will presume the base Bus class is otherwise well factored and adheres to OCP. Because of this, adding a new Send overload to the traditional API can be achieved by subclassing the base implementation. Invoking the new method will look like this.

bus.Send(event, replyHanlder, timeout, timeoutHandler);

But what happens if we try to extend our fluent API by subclassing the Builder?

class NewBuilder : Builder 
{
    NewBuilder andHandleTimeoutsWith(ITimeoutHanlder handler)
    { 
        ...
        return this;
    }
}

Now we see the problem with the base class methods returning Builder. From our subclass, even if the return value is actually an instance of MyBuilder, it is returned by the inherited members as Builder. In a statically typed language, the new method is not accessible without a very un-fluent explicit cast.

A Generic Solution?

In a simple API, if the original designers are prescient enough, one might get around this by using generics. Suppose the orignal builder had been written generically.

class Builder<T> where T : Builder
{
    T for(IEvent event) { ... ; return this }
    T andReplyTo(IHandler handler) { ... ; return this }
    T for(int milliseconds) { ... ; return this }
    void send() { ... }
}

As can be seen, we still need the non-generic Builder so that the generic one can be instantiated in terms of it. If we would normally access the fluent API like so: bus.GetFluentBus<Builder>(), we could then access our extended API like this: bus.GetFluentBus<MyBuilder>().

This approach gives us a builder that complies with OCP. The down side is we have muddied up our GetFluentBus method with a generic argument just to allow for what is likely to be an edge case. But supporting edge cases is what extensibility is all about.

A bigger problem is that the generic approach only works well for a simple, single-builder API. Most fluent interfaces use a set of builder classes and/or interfaces in order to limit the methods available at any point in the call chain to those that make sense. In this scenario, genericizing the API gets ugly. Every builder will need to be generic not only with respect to itself but with respect to every builder that it returns directly or indirectly, and our GetFluentBus method is going need generic arguments for all of them. Consequently, generics are not a good general purpose solution.

Conclusion

Fluent APIs do have an appeal and a legitimate place in software design. However, they are not extensible and therefore should never be the only means of doing something. Rather they need to be seen as an augmentation and approached with the understanding that their purpose is to make the common use cases fluent for those who want the option.

.Net Development03 Aug 2011 07:43 pm

This is one of those pieces of functionality which I have been in search of for a long time. When writing integration tests and other automated tests, you frequently want to test with different application configurations. Unfortunately, switching App.Config files on the fly is not something that the .Net framework supports. There are some alternative approaches to this problem which rely on dependency injection or using AppDomains. Depending on what you are trying to accomplish, these are not always simple or satisfactory techniques. This is especially the case when performing integration tests with third party components that rely on app.config settings and you want to test out alternatives of those settings.

But today, I found this great solution provided by Daniel Hilgarth on StackOverflow. It is something of a hack in that it relies on mucking with the internals of how AppDomains work. I don’t think I would recommend it for production code, but for automated testing, its just what the doctor ordered.

Here is my slightly modified version of the code, as well as a usage example. Please take a look at the example in the article as well, you may like it better.

using System;
using System.Configuration;
using System.Linq;
using System.Reflection;
 
public class AlternateAppConfig : IDisposable
{
    private readonly string oldConfig =
        AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE").ToString();
 
    private bool disposedValue;
 
    public AlternateAppConfig(string path)
    {
        AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", path);
        ResetConfigMechanism();
    }
 
    public void Dispose()
    {
        if (!disposedValue)
        {
            AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE",oldConfig);
            ResetConfigMechanism();
 
 
            disposedValue = true;
        }
        GC.SuppressFinalize(this);
    }
 
    private static void ResetConfigMechanism()
    {
        typeof(ConfigurationManager)
            .GetField("s_initState", BindingFlags.NonPublic |
                                        BindingFlags.Static)
            .SetValue(null, 0);
 
        typeof(ConfigurationManager)
            .GetField("s_configSystem", BindingFlags.NonPublic |
                                        BindingFlags.Static)
            .SetValue(null, null);
 
        typeof(ConfigurationManager)
            .Assembly.GetTypes()
            .Where(x => x.FullName ==
                        "System.Configuration.ClientConfigPaths")
            .First()
            .GetField("s_current", BindingFlags.NonPublic |
                                    BindingFlags.Static)
            .SetValue(null, null);
    }
}

And the usage:

[Test]
public void Application_should_behave_differently_with_alternate_config()
{
    using (new AlternateAppConfig(@"Path\to\Alternate.App.Config")))
    {
        //Your test goes here
    }
}
//The original configuration is restored upon disposal of the alternate.

Happy testing!

Next Page »