Cory Foy

Tuesday, February 26, 2008

Talking about a Revolution...

This week I realized that I just broke the 100,000 mile mark in my frequent flyer miles. Not bad for a year and a half of working for billg. It's actually been a lot of fun, but the travel is brutal when you have two little girls and a loving wife just wondering when you are going to be home.

So, as of March 17th, I will no longer be a blue badge but will be a "Revolutionary" - joining the architecture team of a very interesting company called Revolution Money. Some great things:

  1. I'll be home every night

Ok, so there are some other cool perks, but man will it be nice to be home every night. One of my resolutions this year was not to get frequent flyer status, and I was in danger of breaking that since this year alone I've been in NYC, Atlanta, Des Moines, Bloomington, Chattanooga, and Augusta. It's a lot of fun, and I've gotten to meet some very cool people, but that's a bit much. I'm also chomping at the bit to get back hands on to code (especially since I believe architects must code) and to work with Ruby and agile methodologies again.

And that's one of the downsides to Microsoft. I would have loved to have worked on a product team, and in fact some very interesting positions came up. But they all required relocation to Raleigh or Redmond - something we weren't willing to do. Heck, when we bought our current house and moved in, it was our 7th move in 7 years, and we just want to be settled and near our family.

So thanks everyone, and I'm looking forward to being able to jump back into the local dev community now that I'll actually be home.

Friday, February 15, 2008

Multiselect control in Rails with a Many to Many relationship

This evening I had the chance to dive back into some Rails stuff. I came across a problem of which there was surprisingly no straightforward documentation from start to finish, so I wanted to put it here.

Let's say you have 3 tables - restaurants (id, name), categories (id, name), and restaurants_categories (restaurant_id, category_id). You want to let people who are entering or editing a restaurant select multiple categories the restaurant will belong to. Straightforward, right?

Actually, somewhat. There are a couple of good articles which get us close. The key is setting up the relationships, and combining select_tag with options_for_select.

First, after you've generated your models and controllers for restaurant and category, open up your model for Restaurant and add has_and_belongs_to_many :categories. This is the magic to let Rails know about the relationship.

Next, go into your restaurants_controller and in your create and update methods, just after you new up the object, add @restaurant.categories = Category.find(@params[:category_ids]) if @params[:category_ids]. category_ids is what our multiselect options box will put the selected items into.

Up till now, all of this is in the jrhicks.net blog. However, to implement the categories, he spits out HTML. There's a better way - using select_tag with options_for_select. Open up your restaurants\_form.rhtml file, and add the following:

<p><label for="restaurant_categories">Categories</label><br/>
<%= select_tag("category_ids[]", options_for_select(Category.find(:all).collect { |cat| [cat.name, cat.id] }, @restaurant.categories.collect { |cat| cat.id}), {:multiple=>true, :size=>6})%></p>

Whew! Let's break this down:

  • label - This is just the title of the select list.
  • select_tag - this follows the format (object, options, html_options)
    • "category_ids[]" - the variable that we'll populate when the form is submitted. It has to end with "[]" to let Rails know we are populating into an array
    • options_for_select - This is a helper which spits out the options select_tag is looking for. It has two parameters - an array of objects to use for the options list, and an array of item ids that should be selected
    • {:multiple=>true,:size=>6} - This sets our select list to allow multiple selections, and show 6 entries at a time

Going back to the options_for_select - Notice we do two things:

Category.find(:all).collect { |cat| [cat.name, cat.id] }

This loops through all of the categories and builds an array with their names and ids. This will be turned into <option value="#{cat.id}">#{cat.name}</option> basically.

Second, we do:

@restaurant.categories.collect { |cat| cat.id }

which loops through all of the categories we have set in the restaurant, passing an array of their ids as the second parameter.

So effectively, this displays the multiselect list, and automatically selects the appropriate options if any have been selected.

Friday, February 08, 2008

FitNesse and...F#?

This week I happen to be at one of our offices, and we had a ton of MSDN Magazines there. Flipping through one, they had an intro to F#, a functional language that runs on the CLR. I figured it would be fun to see how hard it would be to get it running with the FitNesse DotNet runner.

First, set up a FitNesse instance with the DotNet runner using C# to make sure everything is running well. Gojko's site is a great place to start (since all of my tutorials weren't updated for 2.0). Got it? Good. Let's get F# going.

First things first. While FitNesse has been known to work with functional languages, I'm going to take advantage of the fact that we can define a class definition using F# which compiles to look like a .NET class. I'm using the latest drop of F# from the above site, which includes integration into Visual Studio.

After you've installed F#, open up Visual Studio and create a new F# project:

image

Name it whatever you'd like. I called my FSharpSpike. Next, right click on the project, and Add->New Item of an F# Source File. Name it fsfixtures.fs:

image

Delete any text that it puts in the file. What we are going to do is recreate the canonical Division FitNesse example. Once you've deleted the text, type the following:

type Division() = class
end

Now compile that. You should see in your project directory a new exe called FSharpSpike.exe. If you explore it with Reflector, you'll see that it looks much like a regular class definition, with one caveat:

 

image

Division is an inner class of Fsfixtures. This is not a problem, but I simply don't know enough about F# to know how to get around it. Our source is being compiled into a class named after the file name, which then contains our Division class. If you know how to get around this, please post in the comments.

Ok, so that out of the way, let's set up our FitNesse test. Mine is called DemoDivision and looks like:

!define COMMAND_PATTERN {%m %p}
!define TEST_RUNNER {dotnet2\FitServer.exe}
!path C:\Projects\FSharpSpike\FSharpSpike\fsharpspike.exe

!|FsFixtures.Division|
|numerator|denominator|quotient?|
|10       |2          |5        |
|12.6     |3          |4.2      |
|100      |4          |25       |
|20       |2          |10       |

Notice that we are pointing our path to the generated exe, and that our fixture is using FsFixtures.Division. Save this, make sure the properties are set to Test, and then run it. You should see that we didn't extend Fixture. Remember that to write fixture code we have to inherit from fit.Fixture. Luckily, F# gives us the ability. First, modify your F# code to:

type Division() = class
    inherit fit.ColumnFixture() as base
end

compiling this will give you an error that fit does not exist. Notice that we don't have a way to add references from the UI. We do have a way to add references from the project properties. So right-click on the FSharpSpike project, and choose properties. Notice the line "DLL References"? Modify that to point to fit.dll. Mine looks like:

image

Now recompile. You should succeed. Now rerun your FitNesse test, and you should see it go a little nutty:

image

But, this is a good thing! It means that FitNesse has found our fixture, and just can't map anything. We are on the home stretch!

Now modify your fixture code to add in statements for the "properties" we need to retrieve:

type Division() = class
    inherit fit.ColumnFixture() as base
        let num = 0.0
        let den = 0.0
        let quo = num/den
        member obj.denominator = den
        member obj.numerator = num
        member obj.quotient = quo
end

Now rerun, and you should see a similar look, except that it says that the setter property couldn't be found. If we refresh Reflector, we can see what we've done:

image

We have getters for everything - including quotient. So let's add some setters:

type Division() = class
    inherit fit.ColumnFixture() as base
        let num = 0.0
        let den = 0.0
        let quo = num/den
        member obj.denominator = den
        member obj.denominator(d:double) = den = d
        member obj.numerator = num
        member obj.numerator(n:double) = num = n
        member obj.quotient = quo
end

Now compile and run, and....

image

WooHoo! We are extremely close. At this point, I've been brute forcing things, but I'm sure this is the wrong way. And indeed, I was close, but not quite there. The F# Wiki showed the way. So modify your fixture code to look like:

type Division() = class
    inherit fit.ColumnFixture() as base
        let mutable den = 0.0
        let mutable num = 0.0
        member obj.denominator with get() = den
            and set d = den <- d
        member obj.numerator with get() = num
            and set n = num <- n
        member obj.quotient() = num/den
end

Save and Build, run your FitNesse test and...

image

Bask in the glory! Now go out and have fun (and maybe I'll figure out this whole functional thing yet...)

Wednesday, February 06, 2008

There is no Agile

Esther Schindler recently published an article in CIO magazine entitled Getting Clueful: 7 Things CIOs Should Know About Agile Development. The article starts well enough, but quickly falls into the Agile trap with headers like:

Agile Creates Better Software

and

Agile's business value comes from organizations' ability to focus on which projects to do, what features to include in those projects, and which processes to use...

My main objection is solely based around the term "Agile" (with a capital "A"). It doesn't exist. We have agile methodologies (e.g. Scrum, XP, Crystal). We have agile practices (e.g. Test-Driven Development, Pair Programming, Onsite Customer, Daily Standups). We can even have agile data and agile code. But these don't magically roll up into a tidy package called Agile, and I think it only muddies the water when this does happen.

If we want real change to happen, we have to stop looking for a single band-aid to apply, and start discussing the real issues we are having. The real takeaway I hope CIOs get from that article is that, if they want to introduce real change, they should talk about what is in the way, and what the pain points are, and address practices and strategies to get them on track. That's what agility is all about.

Sunday, February 03, 2008

Getting photographed by Google

This afternoon I was on my way up to the local home improvement store when I saw a strange looking car pulling out of a parking lot. Well, the car wasn't strange, but it had a weird, tall antenna looking thing on top of it. As it turned onto the road, I saw the Google logo on the side and figured out immediately that it must be the Street Level view vehicle driving around snapping pictures. Alas, I was not able to get a picture of it, but I hope that if they did get a picture of me, I wasn't either a) bug-eyed staring at them or b) picking my nose. Because that would be a pretty bad way to be immortalized.

The interesting thing is that although I live near Tampa, FL, we're pretty far north and it is fascinating that they would be interested in getting street level views up here.

Saturday, February 02, 2008

Ron Jacobs session on TDD/MVP in Tampa

On Thursday, Microsoft hosted a day of Patterns and Practices at the Tampa office. We happened to be there for some unrelated meetings, so I ended up staying for the festivities. Besides finally getting to meet David Hayden in person, Ron Jacobs was down from Redmond to do a talk on TDD in the UI using MVP. This was a topic very near and dear to my heart, and it was really great to hear Ron discuss it in the very same way that I bring to teams.

The reason I was happy is that Ron primarily focuses in the architecture space, so while I normally can win over devs, bringing concepts to "architects" seems to be a bit challenging. But when you've got the guy who runs ArcCast talking about it, it really helps to win the ideas over.

I think Ron was off to the South Florida Code Camp that's this weekend. If you are in the area be sure to stop in and see his session!

Friday, February 01, 2008

Microsoft to buy Yahoo!?

Here's something to ponder over your morning coffee:

Microsoft bids $45 billion for Yahoo

Purty nutty...

Update:  Here's some more interesting details including that the proposal was made directly to the Yahoo! Board of Directors - write after their chairman resigned last night.

More Updates: Microsoft is going to hold a conference call at 0830 EST (0530 PST!). CFO.com thinks it is all one big churn. TechCrunch discusses the implications for Europe, but the EU Commission is not commenting. Finally, here's the full statement to Yahoo!'s board, including this great comment:

In February 2007, I received a letter from your Chairman indicating the view of the Yahoo! Board that “now is not the right time from the perspective of our shareholders to enter into discussions regarding an acquisition transaction.” According to that letter, the principal reason for this view was the Yahoo! Board’s confidence in the “potential upside” if management successfully executed on a reformulated strategy based on certain operational initiatives, such as Project Panama, and a significant organizational realignment. A year has gone by, and the competitive situation has not improved.