Friday, January 13, 2012

Rake Fundamentals: tasks with prerequisites

Rake tasks can have other tasks as prerequisites.  That means that a tasks prerequisites are executed before the task itself when the task is called from the command line.  Just like Nant, if you have a lot of small tasks you often run together, it’s convenient to roll them up into one task with prerequisites.  Let’s use rake to rock out:

image

require 'rake'

desc "open guitar case and get guitar"
task :get_axe do
puts "opening guitar case"
puts "grabbing telecaster"
end

desc "plug guitar into amp"
task :crank_up do
puts "plugging into 1959 fender tremolux"
puts "turning up volume"
end

desc "play A chord"
task :play_a do
puts "playing A chord"
end

desc "play D chord"
task :play_d do
puts "playing D chord"
end

desc "play E chord"
task :play_e do
puts "playing E chord"
end

desc "bask in the glory of adulation"
task :take_bow do
puts "taking a bow"
puts "winking at the girl in the front row"
end

desc "give the people what they want"
task :rock_the_house => [:get_axe, :crank_up, :play_a, :play_d,
:play_a, :play_e, :take_bow]

This rakefile defines these tasks:

image

And you could call each of these tasks individually if you wanted to.  But we want to rock the house, right?  So all we need to do is call “rake rock_the_house”.  According to the prerequisite list in the :rock_the_house task, rocking the house should consist of getting out our axe, cranking up, jamming some chords in this order: ADAEA, and bowing to a standing ovation.  Let’s give it a try and see what happens:

image

Our rock_the_house task ran all the prerequisites, except it didn’t play all the chords we told it to play – we only played ADE, not ADAEA.  LAME!  That’s because rake keeps track of tasks it has executed, and by default will only execute them once.  That might be cool for some tasks, but we want to play the blues, so we have to re-enable our “play_a” task every time we execute it so that we can execute it again.  Let’s fix our rakefile by creating a “play_the_blues” task that knows how to play our chords correctly:

require 'rake'

desc "open guitar case and get guitar"
task :get_axe do
puts "opening guitar case"
puts "grabbing telecaster"
end

desc "plug guitar into amp"
task :crank_up do
puts "plugging into 1959 fender tremolux"
puts "turning up volume"
end

desc "play A chord"
task :play_a do
puts "playing A chord"
end

desc "play D chord"
task :play_d do
puts "playing D chord"
end

desc "play E chord"
task :play_e do
puts "playing E chord"
end

desc "bask in the glory of adulation"
task :take_bow do
puts "taking a bow"
puts "winking at the girl in the front row"
end

task :play_the_blues do
Rake::Task[:play_a].invoke
Rake::Task[:play_a].reenable

Rake::Task[:play_d].invoke

Rake::Task[:play_a].invoke
Rake::Task[:play_a].reenable

Rake::Task[:play_e].invoke

Rake::Task[:play_a].invoke
end

desc "give the people what they want"
task :rock_the_house => [:get_axe, :crank_up, :play_the_blues, :take_bow]

Sometimes instead of calling a task as a prerequisite, you want to invoke it directly from within another task.  Calling the invoke() method on Rake::Task[] is how you do that.  Once you’ve called a task, you can reenable it to be called again by using Rake::Task[].reenable().  We’ve modified our “rock_the_house” task by calling our new “play_the_blues” task as a prerequisite.  Notice that since we didn’t give our “play_the_blues” task a description, it doesn’t show up on the task list when you type “rake –T”.  But it’s still available for use.  Also, notice that we didn’t provide a do…end block to our “rock_the_house” task.  Since we defined everything we need for rocking the house in our other tasks, we only needed to provide the “rock_the_house” task with prerequisites.  Let’s see what happens now:

image

Perfect!  All the chords get played in the right order.  We’ve got a single task to call from the command line that can do all the things we want it to do. 

Next up: directory and file tasks

Rake Fundamentals: rakefiles and tasks

Ok, what is Rake again? 

Rake is a collection of Ruby scripts that live deep within the bowels of your Ruby installation.  Since you made sure you checked the box that adds the Ruby stuff to your PATH variable as you stepped through the Ruby installer wizard, you now have easy access to your Ruby installation and all of the special scripts it contains right from the command line.  To make use of those Ruby scripts, you do a couple of things:

1) Create a rakefile, which is itself a ruby script containing named tasks you can run from the command line.

2) Run one or more of those tasks from the command line like so:

C:\sandbox\rakefun\> rake mytask

And what’s a rakefile? 

That command you just issued told rake to look for a file called c:\sandbox\rakefun\rakefile.rb and execute a task called ‘mytask’. You can call the rakefile something else if you want to, but if you name it “rakefile.rb”, you don’t have to include the “-f my_special_rakefile_name_because_im_a_unique_delicate_flower.rb” switch on the rake command line call – just less to type if you stick to the convention. It’s easy to create a rakefile: open up notepad, type in your tasks, save it as “rakefile.rb”, and you’re done. No compiling. No real need for fancy IDE’s, just any old text editor will do. I like Notepad2, and Komodo Edit is another nice free one with Ruby syntax highlighting. So, what exactly is in a rakefile?

tasks

The fundamental building block of a rakefile is the “task”.   Here’s a hello world rakefile that has one task in it called “:say_hi”.

require 'rake'

desc "My first rake task"
task :say_hi =>[] do
puts "Hello, dude!"
end

Ok, I fudged a little bit: when you open your editor to create your rakefile, you should actually require ‘rake’ at the beginning of the script.  This tells Ruby to find the rake.rb ruby script and pull it into your rakefile when Ruby parses it.  Ruby parses your rakefile any time you make a call to it with the rake keyword on the command line.

Rake provides a keyword called “task”.  The task keyword is actually just a call to a method named “task()” that lives somewhere in the rake.rb ruby script.  But through the magic of Ruby, the task() method call in your rakefile is formatted as you see above.  Takes a little getting used to.  Every time you call a task in your rakefile, Ruby parses the entire file, and all the task() method calls get executed.  But don’t worry – as each task() method call is executed, Ruby is just looking through the rakefile to gather up the name of each task, and the code that Ruby should run when that task name is called from the command line.  So if we had “:task1” and “:task2” in our rakefile, and we called “rake task1” from the command line, Ruby would know that we have both :task1 and :task2 available, but it would only run the code associated with task1.

There are two arguments being passed to task(), and if you’re not familiar with Ruby, they’re a little hard to distinguish:

1) The first argument is “:say_hi =>[]” – This is a Ruby hash object, which is just a collection of key/value pairs.  In this case the hash contains only one key/value pair.  The key is the symbol “:say_hi”, which by convention becomes the name of the task.  The value is “[]”, which is Ruby parlance for an empty array.  In more complex tasks, this array would contain the names of other tasks which should be prerequisites to this task.  Ruby is pretty forgiving, and rake takes advantage of that, so if you have no prerequisites you don’t actually have to include the “=>[]” part; and when you have one or more prerequisites, you can exclude the brackets and just supply a comma-separated list of task names.

2) The second argument is this highlighted block of code starting with “do” and ending with “end”:
task :say_hi =>[] do
  puts "Hello, dude!"
end

A code block is a special Ruby construct that you can think of like an anonymous method or a lambda.  When you call a rake task, rake first executes all the prerequisite tasks you listed in the array in the first argument, and then executes the code in the do…end code block.  If you only want to run prerequisite tasks without executing any other code, just don’t include a do…end code block.

The :say_hi task is called from the command line like this:

image

Get a list of tasks with rake -T

Tasks decorated with a “desc” are shown when you call “rake –T”, “rake –tasks”, or “rake –D” on the command line: 

image

Tasks with no “desc” are not shown in the task list, but can still be called from the command line, or used as prerequisites for other tasks.

Next time: Tasks with prerequisites.

Rake. Huh?

Lately I’ve been using rake at my office to build .NET solutions with Derick Bailey’s wonderful albacore gem.  But I’ve had lots of questions from others on my team about rake:
1) What the heck is rake?
The official line on rake: it is a build system written in Ruby inspired by the make build system from the world of c.  If that doesn’t really mean anything to you, just think of rake this way: it’s just a task runner.  A rake script is a convenient place to script out small tasks that you have to do every day – tasks that don’t take long individually, but that you’re performing multiple times per hour while you are writing code.  Automating those little rote tasks will save you time, and will cut down on the number of silly errors you get performing those tasks by hand.
2) A build system?  Why do I need some fancy build system when I can just click the “Build Solution” menu option in Visual Studio?
Well first of all, having a build system outside of Visual Studio doesn’t keep you from using the Visual Studio build tools.  A good build system should work alongside the IDE, not against it.  But more importantly, when you have a complex solution with one or more deployable apps, some redistributable assemblies, and multiple test projects, you’ve got a lot of stuff to manage; a good build system enables you to manage all that stuff with a measure of convenience that Visual Studio just isn’t designed to provide.
3)  Why rake instead of NAnt?  Are you just trying to be cool?
NAnt is a perfectly useful build system, and I’ve used it for years.  And with UppercuT, NAnt is certainly easier to use than it used to be.  But it’s still just XML.  Same thing goes for MSBuild.  Ultimately you’re talking about filling out an XML configuration file. If I want a custom target type that does something NAnt (or MSBuild) doesn’t do out of the box, I have to use a real programming language to implement the guts of the target; so now I’ve got yet another project to manage.  I’m sure there are people who love it and use it very successfully, but it’s just not my cup of tea.  Not only is it not my cup of tea: it makes me hate tea.  I’m a programmer, not a form-filler-outer.  I want a programming language, not an XML configuration file. 
4) We’re building .NET apps on Windows machines.  Why automate with rake instead of PowerShell?
I’ve seen some very compelling demos of psake, another make-inspired build system, written in PowerShell.  It’s certainly more to my liking than an XML-based build system.  But PowerShell is vendorscript.  I would rather spend time using a language that is a more transferrable skill, like Ruby. 
Those are the “why” questions I get about rake, and my lame attempts to legitimize my desire to mess around with Ruby at work.  I’m off and running on rake now, and the rest of the questions I get usually start with “how”.  I’m hoping to write a few posts answering those questions, and I’ll update this post with links to other posts in the series as I go.

Installing Ruby and Rake

Before we talk about rake, rakefiles, and tasks, we’ll need a couple of things:
1) Ruby – rake is really just a set of Ruby scripts. And what do you need to run Ruby scripts? Ruby! The easiest way to get up and running with Ruby on a Windows box download the latest Ruby One-click Installer (version 1.9.2 as of this blog post), and just run it. Important: Make sure you check the box that adds the Ruby stuff to your PATH variable as you step through the installer wizard. 
image
Once you’ve gotten through the wizard, open up a command prompt and type “ruby –v” to see the version number.  You should see something like this:
image
If you see a message saying something along the lines of “ruby is not a recognized command”, just reboot.  Windows just needs to deal with the PATH variable.
2) Rake – now that you’ve got Ruby installed, open up a command prompt and type
gem install rake
and hit enter.  The “gem” keyword hooks you into Ruby’s package management system – you’re telling Ruby to grab the “rake” package from the RubyGems repository, and install it in your local Ruby installation.  A bunch of stuff that won’t make a lot of sense will scroll by in the command window.  But at the end of it, once you get back to a command prompt, you’ll be ready to run rake scripts. 
3) A text editor – ok, three things.  Notepad works just fine.  If you want something fancier, there’s Komodo Edit, Notepad2, Notepad++ , etc.  And if you wanna pay for a nice Ruby editor, JetBrains (the people behind the wonderful ReSharper) have a good one called RubyMine.  I like RubyMine a lot, and I got lucky and picked up a copy for $29.  Every once in a while they put it on sale, you just gotta check the website for it.

Next up: rakefiles and tasks

Thursday, February 5, 2009

Announcing: the Dale Smith Valued Professional (DSVP) Program

UPDATE: Scott Bellware has just made the grade as well.  Welcome to the program, Scott!

UPDATE: The DSVP program welcomes Louis Salin.  Way to go Louis!

Karl Seguin (among others) is a bit miffed about Microsoft's Most Valuable Professional (MVP) program.  Or at least mystified.  Seems that the rules of induction to the program have become a bit obscure.  Why does Microsoft have the MVP program?  According to Microsoft:

We present the MVP Award to thank individuals for their exceptional contributions to technical communities worldwide. When a community participant sees an MVP in a technical community, whether in a newsgroup, as a user group host, a conference speaker, or a respondent in forums, that community participant can be confident that the information shared by the MVP will be of the highest caliber and will help every user make the most of the technology.

So what kind of community contributions does Microsoft consider "exceptional"?  What kind of information shared by MVPs in various forums does Microsoft consider to be "of the highest caliber"?  I don't know - they don't really spell it out.  But Microsoft is a publicly traded company, so I'm sure their primary goal is providing value for their shareholders.  If I were in Microsoft's shoes I would consider information that benefits me in achieving that goal to be "exceptional" and "of the highest caliber".

I'm not in Microsoft's shoes.  In fact, I'm not wearing shoes at all (I'm from Mississippi).  And I have my own goals, chief among which is providing value for my employer and my customers so that I can earn a paycheck (hey, a guy's gotta eat).  As it turns out, Karl Seguin has helped me achieve my goals over and over again by his activity in the community, as have many others.  Those folks deserve an award!

So I hereby institute: <insert fanfare here> the DALE SMITH VALUED PROFESSIONAL (DSVP) PROGRAM.  Why do I have the DSVP program?

Dale Smith presents the DSVP Award to thank individuals for their exceptional contributions to technical communities in which Dale Smith participates or trolls. When a community participant sees a DSVP in a technical community, whether in a newsgroup, as a user group host, a conference speaker, or a respondent in forums, that community participant can be confident that the information shared by the DSVP will be that which Dale Smith considers to be of the highest caliber and will help Dale Smith make the most of the technology.

Here are some questions you're probably asking yourself:

How do I become a Certified DSVP?

Well that's really kind of up to me, Dale Smith (see above).  If you would like to be considered for the program, feel free to apply via the comment box below.  We will consider your application carefully, and my legal team will contact you when a decision has been reached.  I guess the main thing to remember here is this: some are born great, some achieve greatness, and others have greatness thrust upon them.

What does being a Certified DSVP get me? 

First of all: Once a DSVP, always a DSVP.  None of this you-have-to-renew-once-a-year stuff.  That's just the kind of guy I am.  (To be honest with you, this one is more about me avoiding paperwork than anything else.)

Second: I'll give you a warm smile and a hearty handshake if I ever run into you at a conference or something.

Third: You will have the right to use the letters D, S, V, and P after your name, like so -

Dale Smith, DSVP

See?  Pretty spiffy, huh?  If you use this baby after your name, you better believe that folks will know that when they see you in a technical community, whether in a newsgroup, as a user group host, a conference speaker, or a respondent in forums, that Dale Smith thinks you are one righteous dude.

Last, but not least:  You're welcome to use this nifty badge on your blog, business cards, and any other self-promotional marketing materials you desire:

So why is the badge a cassette tape?

Microsoft is a lot cooler than I am.  No, seriously, they really are.  Granted, they sometimes exhibit some bewildering behavior in the way that they engage the development community.  But the fact is, their achievements both in business and technology far outshine mine.  So receiving an award from them, compared to an award from me, would be like getting the entire recorded output of the Replacements on pristine vinyl, still in the original unopened shrink wrap, plus all Paul Westerberg's and Chris Mars's solo material, records from Tommy Stinson's post-Mats bands Bash-n-Pop, Perfect, and even the new one from Guns 'n' Roses.  While we're at it, let's throw in a high-quality bootleg of whatever you can find from Bob Stinson's post-Mats band Static Taxi.

In contrast, getting an award from me is like getting a self-made cassette from a local unsigned band.  Nothing earth-shattering, mind you, but certainly earnest, sincere, charming.  Definitely has its moments.  And twenty years from now you'll probably run across it again in a box of long-forgotten tapes, and you'll throw it in the deck and think, "Oh yeah!  I remember this!  This was actually pretty cool!"

 

And now I'm going to induct Karl Seguin as the very first Certified DSVP.  So here goes:

Yea and verily, be it known henceforth and forevermore that Karl Seguin is hereby Certified as a Dale Smith Valued Professional (DSVP) in the region of Ottawa, Ontario.  Oh, what the heck!  Let's make Karl's jurisdiction all of Canada!  Congrats, Karl!

Share this post :

Monday, October 13, 2008

Refactoring Legacy ASP.NET Code-Behind with ReSharper

I've been on a refactoring kick lately.  And with the state of the legacy code at my office, I'll be refactoring for a very long time to come.  This is a write-up of a refactor I did at work, altered enough so that I don't violate my NDA.  But I think there's enough of the real story left to illustrate what I was talking about last time: you can indeed beautify even the ugliest code.  A couple of items to note: 1) we're still on .NET 2.0 (no we haven't migrated to 3.5 yet - don't ask!), and 2) I'm not going to touch on testing much in this post.  I really want to focus on the fact that you can positively affect some seriously messed up code with some pretty simple steps.

ParticipantDetails Refactor

We have a legacy ASP.NET web app that has some very tangled code-behind pages which need to be addressed.  There are lots of items on the legacy app development wish list.  Near the top, we have a strong desire to pull .NET 1.1 custom components out of our legacy app and replace them with our .NET 2.0 custom components.  It would be wonderful if we only had to switch references to affect this change, but there are too many barriers in our code to a solution that simple.  Another wish list item is testability, which means our code has to be restructured in such a way as to make it easily testable.  If we tried to do all of this at one time we would most assuredly fail.  Instead, we have to approach the problem with a series of smaller steps with an eye toward refactoring large pages over multiple iterations.

The ParticipantDetails.aspx.cs file is a good example of a code-behind file that has grown large and cumbersome: it has 3384 lines of code in it.  It has many long methods in it, the longest of which is GetParticipant() at nearly 900 lines.  This method essentially does three things:

  1) fetches participant info from the database,

  2) formats that data for display, and

  3) assigns formatted data to the appropriate controls on the page. 

To deal with this page, and particularly long methods like GetParticipant(), I approached it with three goals for this iteration:

  1) make the methods much smaller, each focused on a single task, and therefore more easily readable and maintainable,

  3) move the persistence coupling entirely out of this page, and instead couple the page to a set of Data Transfer Objects, and

  2) apply the MVP pattern to this page to move as much code as possible into more testable classes.

I used ReSharper extensively for this refactor, particularly the Extract Method feature.

Small Methods

At 900 lines, GetParticipant() is extremely difficult to read, and even more difficult to modify correctly.  It's easy to believe that the primary purpose of writing code is to allow the developer to give operational commands to a computer.  We have to change our thinking here: that's the secondary purpose.  The code we write is a living document that contains business information.  The primary purpose I have when writing code is to create a document that allows me and you and everyone of our developers to communicate about how our company does business.  A 900 line method conceals that information by adding noise.  We reduce that noise and strengthen the real message of our code by separating concerns into multiple methods and classes.  (By the way, this has the nice side effect of breaking dependencies and reducing coupling, which will make our code much easier to change in the future.)

Granted, we will end up with "class explosion".  But the alternative is what we have now: "method bloat".  A code base with many small classes containing small methods is much easier to maintain than a few huge classes with very long methods.  How long is too long?  I like to be able to see every line of a method on one screen.  I have a rule of thumb called the "head rule": if I can't fit the whole method in my head, it's too long.  How much can I fit in my head?  Well, if I'm looking at the method on my screen and I hold my head next to it, if the method is longer than my head then it's too long. J

Data Transfer Object (DTO)

A Data Transfer Object (DTO), as the pattern is described by Martin Fowler, is "an object that carries data between processes in order to reduce the number of method calls".  A DTO contains nothing but simple properties, no business logic inside the getters and setters for each property, and no methods at all.  I used ReSharper to help me create a DTO called ParticipantDetailsDTO. 

I started by declaring an ParticipantDetailsDTO variable at the top of the method.  ReSharper immediately notices that I don’t have a class called ParticipantDetailsDTO in my solution and turns the new class name red.  When put my cursor on the class name and click Alt+Enter, a menu appears that offers to create a new ParticipantDetailsDTO class for me.

 

I lucked out a bit when it was time to fill out the DTO.  Most of the controls on the page are hydrated from private variables rather than directly from the Business 1.1 objects.  My next step was to delete the declarations of those private variables.  When I did that, ReSharper turned the variable names red wherever they appeared in the method.

.

.

.

Then, I prefixed every bad variable with “participantDetailsDTO”.  Resharper asked me if I wanted to create a new property in ParticipantDetailsDTO called “strAsstEmail”.  I took Resharper’s advice, and I worked my way through the whole method this way, filling out the ParticipantDetailsDTO class with new properties as I needed them.  I also changed the property names to make them a bit more readable: e.g., strAsstEmail became AssistantEmail.  Since the participant concept contains collections of other things (like Groups, Jobs, Specialties, etc.), I ended up needing several other DTOs.  ParticipantDetailsDTO now contains List<T> properties for those collections.  At the end of this process, all the controls on the page were dependent on DTOs rather than individual private variables.  I had created a seam - I'm sure I'm not using that term as strictly as Michael Feathers intended, but I think the idea is similar: a specific place in my class meant to be moved or otherwise modified without affecting the rest of the class.

From there, still in the page code-behind, I started pulling similar code together: anything that was concerned with hydrating participantDetailsDTO got moved into a separate method.  Same with all the other DTOs: each got its own method.  This was a bit tricky.  Sometimes I ran into code that decided how certain property values should be set.  I had to be careful to preserve this code.  But at the end of it, I had a number of methods that were all about hydrating the participantDetailsDTO and its collections.  So I put them all into a class called ParticipantDetailsDTORepository and moved that class into the Vega.Common assembly.  I also created an interface for it called IParticipantDetailsDTORepository – more about that in a minute.  I ended up with something like this:

 

This is just a quick sketch, but I think you can see the bones of the design.  This implementation of the IParticipantDetailsDTORepository works only with the .NET 1.1 components.  IParticipantDetailsDTORepository contains a method called GetParticipantDetailsDTO(int participantId).  When the time comes to switch this page over to the .NET 2.0 Business Components, we’ll write a new implementation of this repository interface that pulls participant info from the BC components.  That should be a goal in our next iteration.

Model View Presenter (MVP)

So we’ve moved our participant fetch logic out of the code-behind.  And obviously the code that assigns values to page controls has to stay in the code-behind (or does it?).  But I see no reason for the data formatting code to remain here.  This is where the model view presenter (MVP) pattern comes in.  I declared an empty IParticipantDetailsView and made the page inherit from it.  I also created a ParticipantDetailsPresenter class in Vega.Common – this is where I put all the formatting logic, and pretty much any other logic that wasn’t directly concerned with hydrating page controls.  I made heavy use of Resharper’s Extract Method feature here.  So now we also have something like this:

It is actually possible, and even desirable, to move control-hydration code into the presenter, but I didn’t go that far.  I would love to go that direction and show it all under unit tests, but I didn’t have time in this post. 

We're not done yet.  There is still much more we can do to simplify this page.  But the result so far is that the persistence-related code has been moved out into testable classes, the ParticipantDetails code-behind is now about 1200 lines instead of more than 3300, the code is much easier to read and understand, and we have an upgrade path to more easily switch out our persistence mechanism when we're ready.  Next time, I'll talk about testing to detect change to ensure that the DTO built from the new persistence mechanism is the same as from the original mechanism.

Share this post :