Development Blog

 Thursday, February 22, 2007

I'm pleased to announce that I've committed Castle.Tools.CodeGenerator into the CastleContrib repository. What's in trunk also includes some patches posted on the mailing list by Chris Ortman and some other suggestions. It also includes some bug fixes I made. We've deleted the code generator from our repository, so any changes we make will be to the castle repository. Eleutian.Tools.CodeGenerator is no more, long live Castle.Tools.CodeGenerator!

by Jacob on Thursday, February 22, 2007 12:03:57 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Tuesday, February 20, 2007

I spent yesterday and today refactoring the code for the Controller Action/View map generator tool. I made some improvements, wrote some tests and I'm pleased to announce the first release. I saw Brian had implemented his as an MsBuild task and I liked that idea, so that's how ours runs now. First, things aren't as "drop in and go" as I would like because it's still evolving in and around our, already large, code base. It's my hope that over time the tool evolves as peoples needs change, etc... we can add some more configuration, that sort of thing. I'm more than willing to hear suggestions and such. Anway, here we go...

So in order to get yourself up and running, you'll need to do a few things. First, you'll need to add the task to your MsBuild. Simply open your csproj and add the following:

<UsingTask TaskName="Eleutian.Tools.CodeGenerator.MsBuild.GenerateMonoRailSiteTreeTask" AssemblyFile="Eleutian.Tools.CodeGenerator.dll" />
  <ViewSources Include="../YourViewsDirectory/**/*.brail">
  <ControllerSources Include="Controllers/**/*.cs">
<Target Name="BeforeBuild" Inputs="@(Compile)" Outputs="$(ProjectDir)\SiteMap.generated.cs">
  <GenerateMonoRailSiteTreeTask File="SiteMap.generated.cs" Namespace="YourNamespace.SiteMap" ControllerSources="@(ControllerSources)" ViewSources="@(ViewSources)">

You'll notice a few things in here. ViewSources is just the collection of view files (be it .vm or .brail, etc..). It will use the paths of those files to add the View nodes into the map. ControllerSources is the C# source files for your controllers only, specifying this helps speed up parsing. I'll get to why in a bit. And for the Sources attribute on the task you should specify all the sources in the assembly, which @(Compile) works great for.

With the task added, you should be able to build and have a SiteMap.generated.cs file, now you'll need to tweak your "base" Controller class. If you're like us, you have a Controller class that you made yourself that all of your Controller's inherit from. In that class you'll need to add some code like the following:

private ICodeGeneratorServices _services;

public ICodeGeneratorServices CodeGeneratorServices
  get { return _services; }

public RootAreaNode Site
  get { return new RootAreaNode(this.ControllerServices); }

protected virtual void PerformGeneratedInitialize()
  _services = new DefaultCodeGeneratorServices(
    new DefaultControllerReferenceFactory(),
    new AspDotNetRedirectService()
  _services.Controller = this;

First is the member variable, _services. You can do this however you like, but it's consumed by the ControllerAction/ViewReference classes to do various likes. CodeGeneratorServices is a property the partial controller code uses to create the MyActions and MyViews node instances. Site is just an easy way to get the top, root level node. PerformGeneratorInitialize is overridden in the partial controller classes to add the MyViews and MyActions nodes to the PropertyBag so they are available in the views. So there you go. If I'm not missing anything, that should do the trick. Let me know if I did and I'll append to this post.

Oh, the reason for specifying the ControllerSources is to cut down on the number of classes/types we visit when generating the internal "tree" that the source code is generated from. At first glance it seems like you should just be able to parse the ControllerSources only. We coudln't really do that because we needed to gather information on the other types in the assembly so we'd be able to use them in the arguments of our actions, etc... 

I have to stress that the code is relatively new, having undergone a major refactor to isolate it from the rest of our project. I make no promises that I won't have to upload a new copy, with a fix. :)

Source and Binaries

by Jacob on Tuesday, February 20, 2007 6:36:46 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [4]  |  Trackback

The Billings Gazette just published our first front page article!

by Aaron on Tuesday, February 20, 2007 7:45:38 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, February 18, 2007

Hello everyone. I just wanted to let those who responded to the string literal posts know what was "going on". Basically, we're still trying to find a way that we can release the source to some of our tools. I apologize for dragging my feet. I manage to keep pretty busy. Heh.

I was catching up on the castle-project's mailing list and found this post by Lee Henson. He's tossed together an implementation of a PropertyBag wrapper generator and posted the source. It's very similar to our approach. One main difference is that we do our generation at runtime so we don't have to generate and reference another assembly. We have an IViewFactory interface and a PropertyBagViewFactory implementation that hands them out. In tests we have an IViewFactory that pulls the views out of the MockRepository.

Incidentally, our code that does this lies with some other code for generating Control.Invoke proxies for our System.Windows.Forms view interfaces. It seems we're not the only people who do that particular type of generation. RĂ¼diger Klaehn has an article here about it. I strongly suggest reading it if you do any kind of SWF work.

In the thread I found a link to Brian Romanko's post about his implementation of the controller action/view SiteMap, as well as the PropertyBag wrapper. So just in case anybody gets impatient they can find something to tinker with over there. While his implementation is different from ours, the end result is very similar. I like his idea of generating a property for actions to refer to them without parameters.

I mentioned that for the SiteMap code generation we used CodeDom. CodeDom is great and means we don't have to use StringBuilder to construct C#. It also abstracts the language away. For those who are interested in this kind of code inspection/generation, I strongly suggest taking a look at N Refactory, it's a great library for parsing C#/VB.NET source. It's what we use and I've been incredibly happy with it. The only real hurdle when doing that kind or work is type resolution. I'll try and make a post about that in the near future.

by Jacob on Sunday, February 18, 2007 7:04:16 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, February 15, 2007
workspace Hosted on Zooomr

1 3007WFP + 2 2007FP = 4960x1600

Need I say more?

by Aaron on Thursday, February 15, 2007 11:14:50 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Monday, February 12, 2007

So I was recently at a good friend's birthday gig. He's actually my old lead at Microsoft. Anyways, some of our friends that are still at Microsoft were at the party and I got a chance to talk to one of them for a while about various things... primarily open source, TDD, Inversion of Control and Mock objects. He was interested in Rhino Mocks and wanted me to email him about it. Instead of just doing that I threw together a good sized email. Here it is (slightly modified):

Good to see you again. As we talked about, there has been quite a shift in the way we write programs... that is a shift towards a much more testable, more maintainable type of programming. I know there are teams at Microsoft that have embraced recent changes... some have gone agile, most use some form of continuous integration, and some have written Inversion of Control containers (the Composite UI Application Block by the patterns & practices group has a container in it), but I'm sure most teams aren't employing all of these practices and could benefit from at least some of them.

In a number of teams, I think that there are a lot of improvements that can be made... even without shipping anything that's open source.

Firstly, you should take another serious look at Test Driven Development. I've attached an interesting study on TDD, and you may want to skim the MS Press book on the subject.

Also, the following blogs have some real good info on it:
Jeremy D. Miller's TDD Posts
Ayende's TDD Posts

A big part of TDD and Unit Testing in general is being able to remove not only your ephemeral dependencies, but also the rest of your dependencies. That way you know, when something fails, exactly what class caused the failure... it's the class being tested. In order to get rid of the dependencies, you need to replace them. In order to replace them, you need two things. The first is a replacement. The second is a method in which to inject the replacement. Mocks and stubs can handle the first task. I mentioned that we use Rhino Mocks:

Ayende's Rhino Mocks Posts

The library is great, it makes for very readable code and it's pretty darn flexible.

The second item can be a bit more complicated. The thing you usually see when class Foo depends on class Bar is this:

class Foo 
  private Bar _bar = new Bar(); 

  public string DoStuff()
    return _bar.DoSomeOtherStuff() ? "yes" : "no"

Now if you're going to test Foo, you'll also be testing Bar. You can't avoid that the way it's written. Now, it's good to test Foo talking to Bar eventually, but that's what integration tests are for... not Unit tests. So what do you do? This:

interface IBar { bool DoSomeOtherStuff(); } 

class Foo
  private IBar _bar;
  public Foo(IBar bar) { _bar = bar; } 

  public string DoStuff()
    return _bar.DoSomeOtherStuff() ? "yes" : "no"

Subtle difference, but this allows you to throw any implementation of IBar (including a mock) into Foo. That allows you to write a test like this:

void DoStuff_WhenBarReturnsTrue_ReturnsYes()
  MockRepository mocks = new MockRepository();
  IBar bar = mocks.CreateMock<IBar>();
  Foo foo = new Foo(bar);

  Assert.Equals("yes", foo.DoStuff());

Now even if Bar changes, or Bar doesn't even exist, this test will still pass.

Here's the definition from Martin Fowler of Dependency Injection/Inversion of Control

Unfortunately, real programs are a lot more complicated than just one class depending on another... dependency chains are generally pretty deep, and one class can depend on 5 classes, each of which depends on 2, each of which has a different lifetime... per web request, transient, singleton, etc. It would be quite a pain if you had to instantiate everything like this:
Foo foo = new Foo(new Bar(new Blah(), Yadda.Instance));

Fortunately, it's not terribly complicated to write a Container that can handle all of this for you... so all you do is something like:
Foo foo = container.Resolve<Foo>();

There are several:

And then I went on to mention how long this mail was getting and that I was going to post it to this blog.

by Aaron on Monday, February 12, 2007 1:33:16 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Sunday, February 11, 2007

It amazes me how often developers lack fundamental competencies. Things that every developer should at LEAST know about, somewhat understand and know the capabilities of (i.e. when they're presented with a problem that is easily solvable using something such as... oh, regular expressions, they can at least google the steps to a solution.) What I'm trying to say is:

Understand and make use of regular expressions whenever it is appropriate.

Unfortunately, if you're reading this blog, you probably read several other blogs, so you've already been exposed to regex's. If that's the case, the tip for you is:

Think of more ways you can use regular expressions, understand them even more, oh and tell a friend/coworker who doesn't subscribe to 30 dev blogs and doesn't understand/make use of regular expressions about them.

There are several resources for regular expressions.

Roy Osherove has come up with some pretty killer tools for regex:

What do you do with them? Well, there are tons of uses for them, but here's a quick list of some of the basic things they're used for:

  • Find things - Duh.
  • Replace things - tons of cool stuff you can do here with and without capture groups. Here's a naive example:
    Foo bar
    Blah yadda
    Etc etc
    // search: (\w*) (\w*) replace: \1 \2 = new \1();
    // becomes:
    Foo bar = new Foo();
    Blah yadda = new Blah();
    Etc etc = new Etc();
  • Parse things - obviously we love to parse things.
by Aaron on Sunday, February 11, 2007 10:33:10 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, February 09, 2007

I've seen several people say the following when talking about TestDriven.NET: "It's so easy, just right click, and click run tests!" Maybe I'm just too lazy, but what's easy about moving my hand to the mouse, right clicking, finding the appropriate menu item and clicking it?

Bind a key to Run Test(s) and Run Test(s) with Debugger (I use Ctrl+Shift+R and Ctrl+Shift+D)

Just look for TestDriven.NET.RunTests and TestDriven.NET.Debugger in your keyboard settings. I personally use Resharper's Unit Testing feature (except in our 32bit apps on our x64 machines... they're still fixing that bug), but it's all the same. Bind a key to it. Real easy to finish up a test, hit Ctrl+Shift+R, and away you go. No arm movement required!

by Aaron on Friday, February 09, 2007 4:46:23 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  |  Trackback

I've mentioned several tools that we use here, many of which cost money.  We're very lucky here that we can have most of the tools that we want, I know it pains the check writers--"What? Another $300s per dev?!?!" but thankfully they understand that it helps us immensely. I can't help but notice that occasionally people will mention on their blogs that they really like a tool, but that their company won't buy it for them. I think that usually, that shows a bit of shortsightedness in their management.

Developers are expensive and impatient. Good developers are often even more expensive and even more impatient. Tools help developers write more code faster, better, and with less distraction. We'll use ReSharper as an example to try and calculate true cost of the tool.

Let's say a developer's salary is $60,000 per year, that's about $240/day, or $30/hr. Say you build 10 times in an hour, half those times, you accidently typo'd something so you have to rebuild. We'll say fixing/rebuilding costs you 2 minutes per hour. That's 16 minutes per day or $8/day, $40/week, or about $2000/year. All for losing 2 minutes per hour. It's worse than that too, because 2 minutes is a gross exaageration, and that 2 minutes is during a developer's most productive time... the time they actually spend producing.

Now what if, for $300 you could have something that would warn you of impending build errors, so that you could fix them before you actually built? What if it had keybindings (I use Ctrl+Shift+N and Ctrl+Shift+P) to skip to the next error so you can quickly correct it? What if it saved you not only those 2 minutes per hour, but also gave you several other nifty features that saved you time with navigating, formatting, and several other things. Would you spend $300 to save $2000 per year? I would hope the answer is yes. Many other tools share this same productivity boost to cost ratio.

Does this mean developers should have carte blanche for tools? I don't think so. There still needs to be some filter in place, some time for evaluation, and someone who is in tune with both a developer's need for a tool and the company's need not to spend money senselessly, but please, managers, seriously consider requests for tools.

by Aaron on Friday, February 09, 2007 4:33:41 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  |  Trackback