code comments edit

UPDATE: Added a followup part 2 to this post on the topic of granular control.

white plug We have all experienced the trouble caused by plugins that break when an application that hosts the plugins get upgraded. This seems to happen everytime I upgrade Firefox or Reflector.

On a certain level, this is the inevitable result of balancing stability with innovation and improvements. But I believe it is possible to insulate your plugin architecture from versioning so that such changes happen very infrequently. In designing a plugin architecture for Subtext, I hope to avoid breaking existing plugins during upgrades except for major version upgrades. Even then I would hope to avoid breaking changes unless absolutely necessary.

I am not going to focus on how to build a plugin architecture that dynamically loads plugins. There are many examples of that out there. The focus of this post is how to design plugins for change.

Common Plugin Design Pattern

A common plugin design defines a base interface for all plugins. This interface typically has an initialization method that takes in a parameter which represents the application via an interface. This might be a reference to the actual application or some other instance that can represent the application to the plugin on the application’s behalf (such as an application context).

public interface IPlugin
{
   void Initialize(IApplicationHost host);
}

public interface IApplicationHost
{
   //To be determined.
}

This plugin interface not only provides the application with a means to initialize the plugin, but it also serves as a marker interface which helps the application find it and determine that it is a plugin.

For application with simple plugin needs, this plugin interface might also have a method that provides a service to the application. For example, suppose we are building a SPAM filtering plugin. We might add a method to the interface like so:

public interface IPlugin
{
   void Initialize(IApplicationHost host);

   bool IsSpam(IMessage message);
}

Now we can write an actual plugin class that implements this interface.

public class KillSpamPlugin : IPlugin
{
    public void Initialize(IApplicationHost host)
    {
    }

    public bool IsSpam(IMessage message)
    {
        //It is all spam to me!
        return true;
    }
}

For applications that will have many different plugins, it is common to have multiple plugin interfaces that all inherit from IPlugin such as ISpamFilterPlugin, ISendMessagePlugin, etc…

Problems with this approach

This approach is not resilient to change. The application and plugin interface is tightly coupled. Should we want to add a new operation to the application that this plugin can handle, we would have to add a new method to the interface. This would break any plugins that have been already been written to the interface. We would like to be able to add new features to the application without having to change the plugin interface.

Immutable Interfaces

When discussing interfaces, you often hear that an interface is an invariant contract. This is true when considering code that implements the interface. Adding a method to an interface in truth creates a new interface. Any existing classes that implemented the old interface are broken by changing the interface.

As an example, consider our plugin example above. Suppose IPlugin is compiled in its own assembly. We also compile KillSpamPlugin in the assembly KillSpamPlugin, which references the IPlugin assembly. Now in our host application, we try and load our plugin. The following example is for demonstration purposes only.

string pluginType  = "KillSpamPlugin, KillSpamPlugin";
Type t = Type.GetType(pluginType);
ConstructorInfo ctor = t.GetConstructor(Type.EmptyTypes);
IPlugin plugin = (IPlugin)ctor.Invoke(null);

This works just fine. Now add a method to IPlugin and just compile that assembly. When you run this client code, you get a System.TypeLoadException.

A Loophole In Invariant Interfaces?

However in some cases this invariance does not apply to the client code that references an object via an interface. In this case, there is a bit of room for change. Specifically, you can add new methods and properties to that interface without breaking the client code. Of course the code that implements the interface has to be recompiled, but at least you do not have to recompile the client.

In the above example, did you notice that we didn’ have to recompile the application when we changed the IPlugin interface? This is true for two reasons. First, the application does not reference the new method added to the IPlugin interface. If you had changed the existing signature, there would have been problems. Second, the application doesn’t implement the interface, so changing it doesn’t require the application to be rebuilt.

A better approach.

So how can we apply this to our plugin design? First, we need to look at our goal. In this case, we want to isolate changes in the application from the plugin. In particular, we want to make it so that the plugin interface does not have to change, but allow the application interface to change.

We can accomplish this by creating a looser coupling between the application and the plugin interface. One means of doing this is with events. So rather than having the plugin define various methods that the application can call, we return to the first plugin definition above which only has one method, Initialize which takes in an instance of IApplicationHost. IApplicationHost looks like the following:

public interface IApplicationHost
{
    event EventHandler<CommentArgs> CommentReceived;
}
    
//For Demonstration purposes only.
public class CommentArgs : EventArgs
{
    public bool IsSpam;
}

Now if we wish to write a spam plugin, it might look like this:

public class KillSpamPlugin
{
    public void Initialize(IApplicationHost host)
    {
        host.CommentReceived 
               += new EventHandler<CommentArgs>(OnReceived);
    }

    void OnReceived(object sender, CommentArgs e)
    {
        //It is still all spam to me!
        e.IsSpam = true;
    }
}

Now, the application knows very little about a plugin other than it has a single method. Rather than the application calling methods on the plugin, plugins simply choose which application events it wishes to respond to.

This is the loose coupling we hoped to achieve. The benefit of this approach is that the plugin interface pretty much never needs to change, yet we can change the application without breaking existing plugins. Specifically, we are free to add new events to the IApplicationHost interface without problems. Existing plugins will ignore these new events while new plugins can take advantage of them.

Of course it is still possible to break existing plugins with changes to the application. By tracking dependencies, we can see that the plugin references both IApplicationHost and CommentArgs classes. Any changes to the signature for an existing property or method in these classes could break an existing plugin.

Event Overload

One danger of this approach is that if your application is highly extensible, IApplicationHost could end up with a laundry list of events. One way around that is to categorize events into groups via properties of the IApplicationHost. Here is an example of how that can be done:

public interface IApplicationHost
{
    UIEventSource UIEvents { get; }
    MessageEventSource MessageEvents { get; }
    SecurityEventSource SecurityEvents { get; }
}

public class UIEventSource
{
    event EventHandler PageLoad;
}

public class SecurityEventSource
{
    event EventHandler UserAuthenticating;
    event EventHandler UserAuthenticated;
}

public class MessageEventSource
{
    event EventHandler Receiving;
    event EventHandler Received;
    event EventHandler Sending;
    event EventHandler Sent;
}    

In the above example, I group events into event source classes. This way, the IApplicationHost interface stays a bit more uncluttered.

Caveats and Summary

So in the end, having the plugins respond to application events gives the application the luxury of not having to know much about the plugin interfaces. This insulates existing plugins from breaking when the application changes because there is less need for the plugin interface to change often. Note that I did not cover dealing with strongly typed assemblies. In that scenario, you may have to take the additional step of publishing publisher policies to redirect the version of the application interface that the plugin expects.

personal comments edit

Lightning StormWe just had a short lived but crazy loud lightning storm. I was up late working on Subtext because I couldn’t sleep when I started hearing the loud clap of thunder. The eerie part is that between the thunder claps, it was relatively silent outside until the next thunder clap.

I counted less than a second between some of the lightning flashes and the resultant thunder, meaning the bolts were pretty damn close. The immense loudness was awesome and set off all the car alarms in the neighborhood as well as a building alarm. Perhaps the school or church nearby. I was busy trying to shut down my computer.

As the lightning and thunder subsided, the rain started to come down really hard for about 15 minutes. Then nothing. Strangely enough, my dog didn’t make a sound. I wonder if she slept through the whole thing.

personal comments edit

Movie Poster Watched a free screening of this last night in Downtown Los Angeles as part of a film festival. Prior to the screening, Jon Bon Jovi and Richie Sambora played some live music and Al Gore was on hand to introduce the movie. Mayor Villaraigosa was also there oddly suited in a tux and comically referring to Richie as Richie Santora.

The basic premise of the documentary is that humans are affecting the Earth’s climate, this is a very bad thing, we can do something about it, and we don’t have to destroy the economy to do it.

This particular premise is also the subject of much controversy, which Al addresses in the documentary. He points out that there is an overwhelming scientific consensus that humans are affecting the Earth’s climate change. A study of 928 peer reviewed abstracts in scientific journals between 1993 and 2003 on the topic of climate change showed absolutely none disagreed with the consensus. The controversy, in large part has been hyped up by the popular media as well as the minority nay-sayers.

The movie also points out that the idea of humans affecting the climate is not so farfetched and in fact has a precedent. Consider the hole in the ozone layer that was a big worry back in the 80s. The EPA states that ozone deplection was caused by human activities.

Recent headlines are showing the effects of global warmings.

The key point to stress is that Al Gore does not believe that we have to sacrifice our economy or jobs in order to become more ecologically friendly. In fact, he believes it will lead to more jobs. As a case in point, Japan’s auto industry has the most stringent auto emission standards and highest eco ranking and they are kicking the butt of Ford and GM in profits.

One of the most revealing points (as well as embarassing for the U.S.) is when he shows that the U.S. auto emission standards are significantly below China! China! Since when is China the example to the U.S. on clean air?

At the end, Al points out that turning around the negative effects of human activities can be done, and there is a precedent. We don’t worry much about ozone depletion today because the U.S. took the lead in creating treaties to ban CFC production.

This is a worthwhile movie to see whatever your political affiliations, because it is an issue that will eventually affect everyone regardless of political affiliations. Republicans aren’t immune to the weather. If you are skeptical that we are causing global warming, then see it anyways and refute the claims.

code, tdd comments edit

Rhino I am working on some code using the Model View Presenter pattern for an article I am writing. I am using an event based approach based on the work that Fowler did. For the sake of this discussion, here is an example of a simplified view interface.

public interface IView
{
    event EventHandler Load;
}

In the spirit of TDD I follow this up with the shell of my Presenter class

public class Presenter
{
    public Presenter(IView view)
    {
        throw new NotImplementedException("Not implemented.");
    }
}

And this is where I reached my first dilemma. What is the best way to write my first unit test to test that the presenter class attaches itself to the view’s events? Well I could write a stub class that implements the interface and add a method to the stub that will raise the event. In this example, that would be quite easy, but in the real world, the interface might have multiple properties or methods and why bother going through the trouble to implement them all just to test one event? This is where a mock testing framework such as Rhino Mocks comes into play.

[Test]
public void VerifyAttachesToViewEvents()
{
    MockRepository mocks = new MockRepository();
    IView viewMock = (IView)mocks.CreateMock(typeof(IView));
    viewMock.Load += null;
    LastCall.IgnoreArguments();
    mocks.ReplayAll();
    new Presenter(viewMock);
    mocks.VerifyAll();   
}

The second line of code creates a dynamic proxy that implements the IView interface. In the third line, I set an expectation that the Load event will be attached to. The line afterwards tells Rhino Mocks to ignore the arguments in the last call. In other words, the Rhino Mocks will expect the that the Load event will be attached, but don’t worry about which method delegate gets attached to the event. Without that line, the test would expect that null is attached to the load event, which we do not want.

Finally we call ReplayAll(). I kinda think this is a misnomer since what it really is doing, as far as I know, is telling the mock framework that we are done setting all our expectations and we are going to actually conduct the test now. Up until this method call, every method, property, or event set on the mock instance is telling the mock that we are going to call that particular member. If one of the expected members is not invoked, the test has failed.

So finally after setting all these expectations, I create an instance of Presenter which is the code being tested. I then ask the mock framework to verify that all our expectations were met. Of course this test fails, which is good, since I haven’t yet implemented the presenter. Implementing the presenter is pretty straightforward.

public class Presenter
{
    IView view;
    public Presenter(IView view)
    {
        this.view = view;
        this.view.Load += new EventHandler(view_Load);
    }

    void view_Load(object sender, EventArgs e)
    {
        throw new Exception("Not implemented.");
    }
}

Now my test passes. But wait! It gets better. Now suppose I want to write a new test to test that the presenter handles the Load event. How do I raise the Load event on my mock IView instance? Rhino Mocks provides a way. First I will add a boolean property to the Presenter class named EventLoaded and then write the following test. This will allow me to know whether or not the event was raised. This is a contrived example of course. In a real project, you probably have some other condition you could test to verify that an event was raised.

I then write my test.

[Test]
public void VerifyLoadEventHandled()
{
    MockRepository mocks = new MockRepository();
    IView viewMock = (IView)mocks.CreateMock(typeof(IView));
    viewMock.Load += null;
    IEventRaiser loadRaiser 
         = LastCall.IgnoreArguments().GetEventRaiser();
    mocks.ReplayAll();
    Presenter presenter = new Presenter(viewMock);
    loadRaiser.Raise(viewMock, EventArgs.Empty);
    mocks.VerifyAll();
    Assert.IsTrue(presenter.EventLoaded);
}

This test looks similar to the last test, but note the bolded line (fourth line). This line creates an event raiser for the Load event (ignoring arguments to the event of course). Now I can use that later to raise the event after I create the presenter. Running this test fails, as expected. We have to finish the implementation of the Presenter class as follows:

public class Presenter
{
    IView view;
    public Presenter(IView view)
    {
        this.view = view;
        this.view.Load += new EventHandler(view_Load);
    }

    public bool EventLoaded
    {
        get { return this.eventLoaded; }
        set { this.eventLoaded = value; }
    }

    bool eventLoaded;

    void view_Load(object sender, EventArgs e)
    {
        this.eventLoaded = true;
    }
}

Now when I run the test, it succeeds. Pretty dang nifty. Many thanks to Ayende for clearing up some confusion I had with the Rhino Mocks documentation surrounding events.

blogging comments edit

Via a reader, I learned about iBox which is similar to Lightbox JS but with more features. It boasts the following benefits:

  • Small size. Total javascript is under 11kb.
  • Versatile. Handles images, external HTML pages, inline DIV elements.
  • Self contained. The script does not require any external libraries.
  • Support for non-JS users, which isn’t hugely important to me.

Check out the iBox Demo.

personal comments edit

Greg Duncan is now my hero for this find. This just proves that I am still unable to let go of the 80s.

Unfortunately, some of the flagship songs of certain groups are missing in place of other videos. For example, instead of Pour Some Sugar from Def Leppard we get Photograph. Likewise where is Loveshack from the B52s?

Some notable videos (both good and bad):

There are too many to list.

blogging comments edit

This is the blog of a chap who is riding a teched out motorcycle computer (which he calls his “motocompy”) from San Diego to my homestate Alaska. I helped him get his Subtext blog setup so he can blog the entire trip.

His rig is tricked out with Wi-F, a webcam, a mini-ITX system with DVD Player, a power inverter, GPS, a 7” touch-screen, etc… stored in a tank bag attached to his bike via magnets.

Check out this blog post to see a couple videos where he describes his setup.

He also happens to be my coworker’s (Jon Galloway) brother.

humor comments edit

World Cup Widow Leading up to the world cup there was a lot of attention to how the World Cup would affect the wives of soccer football crazy men. Some were on the humorous side such as this list of rules for women, while others attempt to help women avoid becoming a world cup widow.

Fortunately for me, my wife enjoys watching the world cup. What’s not to like with such pretty boys as Beckham, Kaka and Cristiano Ronaldo on the pitch? The real widow appears to be my blog. As I pass by the office on the way to the television, I can hear her whimper, “Another day of neglect. I understand. Go on. Don’t worry about me.”

I figured I would spare the world of yet another person’s biased and uninformed opinion on the events of the World Cup. But today I thought I would take a few moments to say something.

I had the good fortune to go in for jury duty this past Thursday. It was good fortune because I was not called into a trial so I was able to sit back and watch a couple World Cup with other dedicated soccer fans. When the noon lunch break came, everybody left except for a few of us who had pizza delivered to the courthouse.

Today I enjoyed the Brazil win and will watch Korea play France next. Heavily favored Brazil is not looking so heavily favored so far. Unless they pick up their play, I think Argentina is the team to beat. Ghana looked wonderful against the Czech Republic. The U.S. is going to have their work cut out for them. They will be very lucky if they advance. At least they showed some grit against Italy.

code, asp.net comments edit

1040 EZ Form Today I ran across some code in a 3rd party open source library that used the following function in order to retrieve the form id.

public static string GetPageFormID(Control page)
{
    string id = null;

    foreach (Control con in page.Controls)
    {
        if (con is HtmlForm)
        {
            id = con.ClientID;
            break;
        }
    }
    return id;
}

Which gets called like so:

Control page = HttpContext.Current.Page;
string formID = GetPageFormID(page);

Unfortunately this didn’t work for me because I don’t have the form declared as a direct child of the page. Instead the page contains a user control which contains the form. This is a common scenario when using a MasterPage (in my case an ASP.NET 1.1 backported master page control). When looking for the form, the function should search recursively like so:

public static string GetPageFormID(Control page)
{
    string id = null;

    foreach (Control con in page.Controls)
    {
        if (con is HtmlForm)
        {
            return con.ClientID;
        }
        id = GetPageFormID(con);
        if(id != null)
            return id;
    }
    return id;
}

This will search the entire control hierarchy until it finds the HtmlForm. In the most common case, it will find it without having to recurse. But for crazy folks like me who always look for ways to be different, this will do the trick. Luckily this was an open source library I was using so I was able to fix the code and send the authors a patch.

personal comments edit

Soccer BallWorld Cup Fever!.

Hence the lack of blogging. Last weekend, I went on a camping trip with my coworkers Micah and Jon in the Los Padres national forest. I was fighting a bad cold at the time and am only now getting over it.

But a new fever has taken its place. In part, I was feeling pukey after the horrible showing by the U.S. against the Czech Republic. The only bright side to that game was the play of Oguchi Onyewu and Claudio Reyna.

Today during lunch I will watch Brazil play the beautiful game. Since I don’t have cable I will have to watch it on Univision (a former employer) and brush up on my Spanish. At least I know what GOOOOOOOOOOOOOOOL means!

subtext comments edit

If you are hosting multiple blogs on a single installation of Subtext, the recent Subtext 1.5 release unfortunately introduces a security bug that will allow an admin of one blog to login to another blog. The fix has already been posted to Sourceforge as part of the Subtext 1.5.1 release.

If you already upgraded to Subtext 1.5, you only need to update the Subtext.Framework.dll file in the bin directory. The fix was a one line code change. I apologize for the inconvenience and for the mistake. Please spread the word.

Tags: Subtext

comments edit

IMPORTANT UPDATE: There was a security bug in Subtext 1.5 for multiblog setups that will allow the admin of one blog to login to another blog. If you are only using a single blog setup, you have nothing to worry about. For multiblog setups, please upgrade to Subtext 1.5.1. The change is a single line change in Subtext.Framework.dll so if you have already upgraded to Subtext 1.5, you can simply copy over the old Subtext.Framework.dll file with the new one instead of copying every file from the installation package. Sorry for the the inconvenience. Download Subtext 1.5.1.

Subtext Logo After much hard work, the Subtext team is proud to announce the release of Subtext 1.5, dubbed the Nautilus R&R Edition. This is primarily a bugfix release with a load of bug fixes, but there are a couple of significant new features as well as a bit of developer candy thrown in.

Release Notes

Rather than list all the bug fixes and new features here, I will point you to the release notes published online.

I will point out a few key changes.

HtmlEditorProvider

Subtext now supports an HtmlEditorProvider. You can swap out the html editor used in the admin section by implementing a provider for your editor of choice. Providers that ship with Subtext include the FreeTextBoxProvider (default), FCKeditorProvider, and PlainTextProvider for you angle bracket jockeys.

For more information on how to change the text editor, read this.

Javascript BlogInfo object

By default now, Subtext emits a javascript object useful for client scripts to access information about the blog. This object is intentionally simple, but may be expanded as needed. The variable name for this object is subtextBlogInfo. The following is an example of its usage.

var imagesPath = subtextBlogInfo.getImagesVirtualRoot();
alert(imagesPath);

Improved Skin configuration {.clear}

If you write custom skins, the Skins.config file has a few new features. Here is a sample skin configuration that highlights several new features.

<SkinTemplate SkinID="Haackify" Skin="Haackify">
    <Scripts>
        <Script Src="~/Admin/Resources/Scripts/niceForms.js" />
        <Script Src="~/scripts/lightbox.js" />
        <Script Src="~/scripts/XFNHighlighter.js" />
        <Script Src="~/scripts/ExternalLinks.js" />
        <Script Src="~/scripts/LiveCommentPreview.js" />
        <Script Src="~/scripts/AmazonTooltips.js" />
    </Scripts>
    <Styles>
        <Style href="~/scripts/XFNHighlighter.css" />
        <Style href="~/scripts/lightbox.css" />
        <Style href="niceforms-default.css" />
        <Style href="print.css" media="print" />
    </Styles>
</SkinTemplate>

Notice that you can reference files using the tilde (~) syntax. Subtext now comes with several script files in the base Scripts directory that can be shared across skins. For example, if you want to add the LightBox JS effect to your skin, just reference the ~/scripts/lightbox.js in the Script section and the ~/scripts/lightbox.css section in the Style section as in the above example.

The root Scripts directory is a central repository for Subtext client script files that are accessible to all skins. Typically skins will include their own scripts. But occasionally the Subtext team will make popular scripts available to all skins. Some of these scripts have been modified to use the subtextBlogInfo object mentioned before.

Also notice that Style elements may now specifa a media. Thus you can add a stylesheet to a skin specifically for printing.

New Skins and Skin Controls

Checkout the new Submarine skin designed by our friends at TurboMilk. We have also added several new skin controls such as the Previous Next control which renders a link to the previous and next blog post. Note that not every blog uses every skin control, but it is quite easy to add such a control to the skin of your choice.

CSS Based Printing

Nearly every skin in Subtext now implements CSS based printing.

Upgrading from Subtext 1.0

Subtext 1.5 has an automatic web-based upgrade wizard that will upgrade your schema and stored procedures if you are currently running Subtext 1.0. We have made changes to web.config so if you have made any customizations, you will need to merge these changes yourself which should not be too hard. Please read the upgrade instructions carefully before upgrading.

If you are uncomfortable upgrading your database schema automatically, you can try the manual schema upgrade process outlined here. The steps in that process are the same ones that the automatic wizard takes on your behalf.

Installation

If you are installing Subtext for the first time, the web-based installation works as before. Just follow the instructions here.

Next Stop, Daedelus!

The next version of Subtext is code named Daedelus. It will simply be a straight upgrade to ASP.NET 2.0. We hope for a quick turnaround as we don’t plan to add a lot of features in this iteration. We just want to get up and running in ASP.NET 2.0. Afterwards we will start heavy work on Subtext 2.0 Poseidon. This will be a more ambitious release.

Please note that we may introduce a lot of breaking changes for skins in Subtext 2.0. We will try and keep you informed so that you will have advanced warning on how to upgrade your custom skins.

comments edit

In the Subtext 1.5 release announcement, I mentioned we had a few new skins. I thought I would post a couple of screenshots of the Submarine skin to give you a sense of what it looks like.

And here is a detail shot.

One problem with this skin is that it is fairly narrow for a fixed width skin. Hopefully we can fix that in the next version and offer a few more intriguing skins.

Be sure to check out these skins as well KeyWest, Semagogy, Piyo, WPSkin. I think some of them are new, but I cannot remember.

Also, if you have some sweet design skills, we are always looking for new skins to add to the default installation.

comments edit

Found a useful nugget in Richter’s recent CLR via C# book I want to share with you. But first some background.

Sometimes when I write a catch block, I don’t really have any plans for the caught exception. The following is a contrived example that is somewhat realistic.

try
{
    DoSomething();
}
catch(System.SomeException)
{
    DoSomethingElse();
    throw;
}

In the above code, I only want DoSomethingElse() to execute if DoSomething() throws an exception of type SomeException. I can’t put DoSomethingElse() in a finally block because then it would always get called and not just when the exception is thrown. I don’t need to do anything with SomeException because I am propagating it up the callstack via the throw keyword to let some other method handle it.

But now, as I am stepping through the code in the debugger, I may actually want to examine SomeException when the debugger reaches the line DoSomethingElse(). Typically I would have to rewrite the code like so:

try
{
    DoSomething();
}
catch(System.FormatException e)
{
    DoSomethingElse();
    throw;
}

Just so I can examine the exception now stored in the variable e. This is plain dumb and Richter points out why in a little tip in his book. You can use the debugger variable $exception provided by the Visual Studio.NET Debugger to examine the exception in a catch block. I wish I had known about this a while ago.

humor comments edit

As you may well know, today is June 6, 2006 or in shorthand notation 6/6/06, the mark of the beast. As the church lady would say, “mmmmm, isn’t that special?”

Dana Carvey is the Church
Lady

This day reminds me of a song I heard a loooong time ago called “Sweat Loaf” by the Butthole Surfers (pleasant name). It starts off with a nice conversation between an all American boy and his all American dad.

  1. Son

    Daddy?

  2. Dad

    Yes, son?

  3. Son

    What does regret mean?

  4. Dad

    Well a funny thing about regret is, its better to regret something you have done, then to regret something you haven’t done….Oh and if you see your mother this weekend remember to tell her- SATAN, SATAN, SATAN

So in commemoration of this numerologically significant day, be sure to show the evil horns gesture to somebody today and include a little head banging. Our President will lead us with a showing of the horns.

Bush Horns

I always knew he was in with the Devil

tdd comments edit

If you’ve read my blog for any length of time, you know that I am a fan of the MbUnit generative unit test framework.

What I haven’t been a fan of is linking to the MbUnit website. If you want to refer someone to NUnit, you simply link to http://nunit.com/. But if you want to refer someone to MbUnit, you have to type out this monstrosity: http://www.mertner.com/confluence/display/MbUnit/Home.

So in addition to complaining about it, I decided to do something about it. I purchased MbUnit.com and pointed it to their website. Now when I mention MbUnit in a post, I can spare my fingers a few keystrokes.

One other issue I hope this helps solve is that the MbUnit website comes up second in Google search results. First is its old hosting location. Hopefully everyone will start updating their links to point to MbUnit.com instead and help solve that issue.

open source, blogging comments edit

Recently I highlighted a site named DotNetKicks which is like Digg.com, but targetted to .NET technology. In particular I thought it was a smart move for them to share in their advertising revenue with those who submit stories.

Well to make it easier to kick stories from the convenience of your favorite RSS Aggregator, I wrote an IBlogExtension plugin so that you can submit/kick/unkick stories from RSS Bandit or any RSS Aggregator that supports the IBlogExtension plugin model (.NET 1.1 must also be on the machine).

Just download and unzip the extension to your plugins directory. The default location for RSS Bandit would be c:\Program Files\RssBandit\plugins.

Once you have it installed, restart RSS Bandit and right click on any feed item and select the DotNetKick This - Configure… menu option.

Context Menu

This will bring up the plugin configuration dialog. You should leave the URLs as they are. I made left them to be configurable in case the URL ever changes. Just enter your DotNetKicks username and password and click OK.

This will save your username and password in an xml settings file with the password heavily encrypted.

Now you can right click on a story to submit it to DotNetKicks. If the story hasn’t been submitted, you will get the following dialog.

Submit a Story Dialog

This form is pretty self-explanatory.

If a story has already been submitted, you will see the following dialog which allows you to kick it or unkick it (essentially adding your vote to the story or removing it (editor’s note: At the time of this writing, the unkick function was not working).

Kick/Unkick a story dialog

The API for DotNetKicks was published today on Gavin Joyce’s wiki. This was quite a turnaround as I emailed him on friday asking if there was a web-based API. We went back and forth formulating the API and he said he would work on it over the weekend. This morning, he sent me the URL to his wiki page describing the API! Much of the API was inspired by the del.icio.us API.

If you want to learn to write an IBlogExtension from scratch, check out my tutorial here. In this case, I was able to get a jumpstart by using Dare’s excellent del.icio.us plugin as a starting point.

Another plugin I wrote a while ago is the improved w.bloggar plugin for RSS Bandit that should hopefully be included in the next release of RSS Bandit.

Once again, in case you missed the first link to this DotNetKicks plugin, [DOWNLOAD] it.

blogging comments edit

I recently learned about DotNetKicks due to a referral in my referrer logs. It is essentially a Digg knockoff, but tightly focused on the .NET community, which makes it a nice complement to Digg.

Recently, security expert Bruce Shneier wrote a piece on how security can be improved by aligning interest with capability. One example he gives is how some retail stores promise refunds if you don’t get a receipt. This keeps the employee working the register honest by aligning the store owner’s security interest with the interests of the customers. The owner is effectively hiring the customers to keep employees honest.

In a similar manner, DotNetKicks has done the same thing to promote its own growth. If you have an AdSense account, you can submit your AdSense ID and earn 50% of the advertisement revenue on the site for all stories that you submit. This aligns story submitters’ interests with DotNetKicks’ interest and should go along way to ensure that more stories are submitted.

Of course, the signal to noise ratio of submitted stories may go down as a result, but that is hardly a concern because only the stories that the community deem interesting, via the voting system, percolate to the front page and have any chance of really generating advertising revenue. The question I have is what is the incentive to users to kick stories apart from the community benefits?