Monday, October 24, 2011

The Danger of Extension Methods

Today I came across something with extension methods that can cause code to break.  If an extension method’s signature is equivalent to an actual method on the class or interface the extension method will not be called.  For example let say I have the following classes:
    public class Foo
    {
        public object FindItem(int id)
        {
            //Some implimentation
        }
    }

    public static class FooExtensions
    {
        public object FindItem(this Foo foo, int id)
        {
            //Some slightly different implementation.
        }
    }

The FindItem in FooExtensions will never be called unless you call it as FooExtensions.FindItem(foo, id);.  In general this is not a horrible problem as you should avoid naming extension methods the same as an existing method.  The problem comes in if someone extends or modifies a class so that the extension method was hidden.  I discovered this the hard way.  I had a class setup that looked like:
    public class Foo
    {
        public object GetItem(int id)
        {
            //Some implimentation
        }
    }

    public static class FooExtensions
    {
        public object FindItem(this Foo foo, int id)
        {
            //Some slightly different implementation.
        }
    }


GetItem and FindItem had subtly different behavior in how they returned a value.  In 90% of the cases the two methods behaved the same.  However, there was one case where they didn’t.  During a refactor of some code I was trying to unify the language in the Foo interface and renamed GetItem to be FindItem.  I didn’t realize that there was an extension method elsewhere in the code that this change would hide. 
Inevitably, this change broke in the one case where the two methods did not behave in the same manner.  However, it was unclear as to why the code broke.  I spent a good hour looking at the broken code and could not determine why it ever worked.  Since the extension method is hidden by the class’s method of the same name it does not appear when trying to find references.  After searching for a solution to no avail, I made a change to the code call to what used to be the extension method version of FindItem.  Interestingly, this made the code more efficient.  It was only a month later i.e. today when I came across the extension method and found no references to it that I realized what had happened. 

This experience has led me to some thoughts about good coding practices to avoid this issue.
  • Make sure all your extension methods have unit tests associated with them.  This issue would have been spotted in a unit test for the extension method if a test for it had existed.  My project doesn't use a TDD approach and we don’t have full coverage so this error was not caught at this level.  A good unit test would have caught the case that caused the error.  This is the most straightforward and safest way to prevent the error.
  • Collect the extension methods in a place that makes them easy to find.  They probably should be within their own namespace.  Part of the reason this error wasn't caught was that the offending extension method was in a general namespace that was referenced for other reasons.  If a namespace suddenly was no longer needed, this would have been a clue about the cause of the problem.
  • Be judicious with the use of extension methods.  The FindItem method should have been part of Foo.  It was a mistake to not put it there in the first place.  Extension methods are really tempting for people who come from a procedural programming background.  However, they should only be used as a mechanism to extend functionality when the code cannot be extended otherwise.  They should not be used as way of not adding a method to an object or just providing a method that should be a static method to the class.

Wednesday, October 19, 2011

My Experience Getting Started With Ruby on Rails


I have been meaning to learn to Ruby on Rails for a while and finally made the decision to start a project.  The following was my experience trying to setup Rails and getting started on building a first application.  Included are my thoughts as I went along.  Overall I am impressed with Ruby on Rails.  In reality it is simple to get something set-up quickly.  However, some of my experience has made me question how easy some of the more complicated aspects will be.  Those will be subjects of other blogs.  This should not be taken as a how-to on setting up Ruby on Rails.  There are many excellent tutorials about that out there and this isn't one of them.  I am in no way trying to write a tutorial.  Instead, I want to share my experience with setting it up.  This has a twofold purpose.  I found the processes to be enjoyable and wanted to share my joy in doing this.  The other was that I had some struggles as I went along.  My hope is that if someone encounters the same or similar errors, my experience might be helpful to them.  I've linked to most of the pages I've used in hope of directing people to the resources I found useful.

Friday 10:30 PM: I decided to get started.  First step go to the Ruby page.  I don’t know Ruby at all so I take a quick look at the documentation page.  I select a link for Ruby from Other Languages and select the From Java page, since it is the language in the list I am most familiar with.  I take a quick skim of the document.  It doesn’t seem too intense.  Some things I am aware of already like the concept of “everything is an object.”  Others I am not.  It is a good quick overview of Ruby in the abstract.  At this point I also go to the download page.  I read through the page and find the RubyInstaller for Windows page and download the latest version (1.9.2-p290).  After it completes the download I run the installer.  While it installs I go to the Ruby on Rails site.    I then get to the Ruby Downloads page which has a clear set of instructions for getting started with Rails.  I have already performed the first step of installing Ruby itself.  Next up is RubyGems.

Friday 10:40 PM: I follow the link to get to the RubyGems download page.  I download the latest version (1.8.10).  I then unzip it to a folder next to my Ruby install and using the command line run ruby setup.rb.  Installation proceeds quickly and now I can use gems.  The gem concept seems really nice as it allows you to easily install new components from the command line.  It all is working well so far.

Friday 10:50 PM: Now on to Rails.  I download version 2.2.3 from the download page.  This would be my first misstep of the night.  I really was not paying close attention at this point and just figured that what was on the page was the most recent.   After all, the link on the Rails home page had said try Rails 3.  That link had sent me to the download page with the instructions which had sent me to this page.  All this I didn’t realize at the time.  I also didn’t understand how gems worked at this point.  I didn’t even need to download the package.  All I had to do was in the command line type gems install rails.  I realize my mistake and I run the command prompt.  Rails installs. I’ve decided to call my test application “Dolphin”, just as a simple code name. So now I create my source directory by typing rails new Dolphin in a folder where I keep my source code.

Friday 10:55 PM: At this point I decide I need more directed help and find a good walk through setup guide called Getting Started with Rails.  This is good because it walks through things in more detail and I want to understand why things work, not just that they do.  This also points out the database.yml file. 

Friday 11:10 PM: Reading the “getting started guide” has made me want to use MySQL instead of the built-in SQLite instance.  I change the configuration then go to install MySQL.  To do this I figure the easiest way to do this is install a XAMPP stack.  This way I get PHPMyAdmin to manage the database.  I download the latest version (1.7.7) and install.  After the installation is complete Apache won’t start because I have IIS running on port 80 for some other development.  I change Apache to run on port 8000 because that’s much quicker and easier than turning off IIS.  I get this up and running and decide to call it quits for the night.

Saturday 9:30 AM: For some reason I am having trouble with getting the rails instance setup.  Folders aren’t being created in the way I would expect.  I go into the Dolphin folder and type rake db:create. I get an error when I do this telling me that there is no such file to load.  I go into my explorer view to look at the folders and realize the problem.  Nothing is going into my Dolphin folder and a folder called new is being created instead.  I think maybe I need to be in the Dolphin folder, but the new folder then appears in there.  I also realize at this point that all the files I’m expecting to be in my Dolphin folder are in my new folder so I omit the new and it seems to create what I want.   I then do the database create rake again and get a new error.  This time around I get a different error telling me:  NameError (undefined method `path' for class `ActionController::UploadedStringIO').  I use Google to find me what my problem is.  After some looking this post clues me in.  I have Rails 2.2.3 installed but I am using Ruby 1.9.2. 

Saturday 9:40 AM: No reason not to start with Rails 3 so I go to figure out how to update to Rails 3.  After some searching around and following the same path multiple times I find the Rails 3 beta page which tells me I can install it by typing gems install rails –pre.  It works like a charm and installs.  I now go back and type db:create and it fails again.  However, it is a different failure this time.  This time the error message said: rake aborted! uninitialized constant Rake::DSL.  Back to Google for this one.  This proved to be an easy fix.  I just needed to change the Rakefile to include require 'rake/dsl_definition'.

Saturday 9:55 AM: At this point I was sure that when I typed db:create it would all go smoothly.  Unfortunately, the major headache of the day was just beginning.  When I ran the command again I got another error that looked similar to the previous one.  This time the error was uninitialized constant mysql2.    A Google search resulted in that I needed to install the mysql2 gem.  This seemed easy enough.  Just type gem install mysql2 and all should be good.  But, no, another error.  Fortunately, this time the error told me what to do.  The error even provided me a helpful link to the ruby installer download page.  The problem was I needed to install the Development Kit.  Finally, a logical error that had a logical resolution.  I followed the instructions for installing.  These were some of the best instructions I’ve found for installing software.

Saturday 10:10 AM: At this point I think I am home free.  I go try to install mysql2 again.  This time I get another. I got the following Error:  Failed to build gem native extension. Again back to Google to find some help for this one.  One of the suggestions pointed me to use gem install mysql -- --with-mysql-lib=/usr/lib/mysql/lib.  This made sense.  My MySQL instance was installed inside XAMPP so of course I’d have to specify its location to the gem installer.  This was correct, however I did not understand the command line syntax correctly lead me down another false path.  I thought the first two -- were unimportant.  In my mind I was interpreting them as a filler for other switches.  So I typed gem install --with-mysql-lib=C:\Programming\Tools\xampp\myphp.  Every time I tried to run this because it kept saying that “C:\” is not a valid path.  At this point I was very confused.  I tried using only a relative path.  When I used the relative path the command ran successfully.  I figured I was home free.  However, when I tried the db:create command again, I got the same failure.  Back to the drawing board, well back to Google to search for more help. I found lots of results and lots of different suggestions.  None of which worked.  It is at this point I realized there are probably a lot of Apple users who use Rails.  There was a surprising number of results related to fixing the problem in OS X.  I had to resort to searching for a Windows specific fix.

Saturday 11:00 AM: After an over half an hour of not making any headway I decide to take a break and get ready to watch the Penn State game.  I figure I’ll return to this later with a fresh mind as I was just beating my head into the wall.

Saturday 6:00 PM: After watching a dull Penn State victory and some other chores it was time to return to trying to get this instance up and running.  I returned to some of my earlier searches and finally determined that my previous attempt had failed not because the instructions were wrong but that I was implementing them wrong.  I looked in the mysql folder and saw that I had created a with\mysql\(and lots more nested folders) directory.  I realized my mistake from earlier.  By omitting the -- in the previous command I change how that command functioned and it took the –with-mysql- as a folder to create instead of an option to the command.  I ran the command with the -- and it seemed to succeed.  I ran the rake command again and I got the failure once again.  It said “rake aborted! uninitialized constant mysql2.”  Same problem.  All the error handling I found said I needed to add the mysql2 constant to the gem file.  I mistook this as something I had to do in the ruby install and spent a fruitless half hour trying to find the gem file in the Ruby and the Ruby on Rails folders.  It is only much later that I realized the gem file was contained within my Dolphin folder.

Saturday 6:50 PM: After unsuccessfully trying to find the gem file in a location that did not exist I went looking for more help and I found this page.  This was a bug report and at the bottom of it I found what I thought was helpful advice.  It said “It seems like a MYSQL Version Problem. Try uninstalling MySQL 5.5 if you have this version and install MYSQL 5.1.”  This seemed like sage advice, but was annoying because it meant determining which version of XAMPP had MySQL 5.1 included.  I then had to uninstall my current XAMPP version and reinstall that.  I made some stupid mistakes in doing this (I reinstalled my same version and installed the lite version.  I eventually got version 1.7.3 installed.  This took a while because I installed and uninstalled a couple times and go distracted looking at parts to build myself a new computer. 

Saturday 8:00 PM: I finally was setup to go again.  After installing the new XAMPP instance I reinstalled the Ruby gem for mysql2 and thought I was home free.  However, it failed again.  I was really frustrated.  It was at this point I decided to figure out the gem file problem from before.  After some more searching online and looking in my folders I saw what I was missing.  There in my Dolphin folder I found a file called Gemfile.  It could not have been more obvious.  All that pain was because I was overlooking something simple.  Since I had done nothing useful in the Dolphin folder I deleted it and re-ran the rails new Dolphin command in hopes that it would update the Gemfile.

Saturday 8:50 PM: I tried running the rake again and I had the DSL problem from before.  I looked up how to solve that problem again.  I fixed it the same way as before.  Then I typed rake db:create.  Success!  Well, sort of.  At this point I had forgotten that by deleting the whole Dolphin directory I had deleted my yml file.  I thought I was using the MySQL instance but I was actually using the SQLite.  A fact I only realized when I looked at the database in PHPMyAdmin and nothing had been added.  At this point I was just excited to play with Ruby and Rails.  I didn’t feel like going through any more hassle.  Using MySQL would have to wait for another day. 

Saturday 9:10 PM: At this point I figured I’d be writing some Ruby code soon so I wanted an IDE to do that in.  I figured since I was a novice at the syntax this would be a good idea.  I went searching for what IDE to use.  I first thought I’d use NetBeans because I had seen Ruby support in it before.  However, this thought was dashed because I found this announcement.  Apparently Ruby was not being supported by NetBeans anymore.  I searched online for some recommendations.  I eventually decided on Aptana Studio (which is built on top of Eclipse) based upon some recommendations and my familiarity with Eclipse.  I got that downloaded and extracted.  This was wasted time really because I didn't wind up using it at all

Saturday 9:20 PM:  I now returned to where I was the night before going through the walk through.  My next step was to just start the server.  I typed rails server to get it going.  This was awesome; it seemed to start with no issues.  Way simpler than getting a Java project to deploy.  I went to http://localhost:3000  (the port it starts on by default) and the default start-up page was there.  It was good to have something work right off the bat. 

Saturday 9:23 PM: Back in the console I shut down the server using Ctrl + C.  The next step was to generate some content.  So I used the rails generate controller home index command as instructed to in the walk through.  This succeeded with no issues. The tutorial told me then to go to the app/views/home/index.html.erb file and change the contents.  This I did. I also deleted the default index page by removing the public/index.html file and changed the routing by uncommenting the root :to => "welcome#index" line in the config/routes.rb

Saturday 9:38 PM:  I once again restarted the server and went to the default page, hopeful  to see the HTML I had entered a minute ago.  Instead I got an error on the page that said “uninitialized constant WelcomeController.”  Another road block.  I search for an answer for a minute before I notice something.  In the tutorial I am reading it says to uncomment root :to => “home#index” line but my routes.rb actually has root :to=> “welcome#index” for the line.  I change welcome to home and reload the page.  Viola, it works, showing the HTML I had put into the template. 

Saturday 9:55: I follow along with the tutorial without any more incident.  I gets me up and running with a simple module really quickly.  It  creates a creation page, a listing page, and an edit page by just running the rails generate scaffold Post … command.   

I am going to end the discussion of my experience at this point as the rest of what I did was rather vanilla.  Overall I had a positive first experience with Ruby and Rails.  If I hadn’t detoured to try to use MySQL it would probably have taken no more than an hour to go from nothing to an application that can enter, retrieve, and edit data.  There is a lot more for me to explore and I am excited about exploring it.  I will post more blog entries as I explore more.