Wolfmans Howlings

A programmers Blog about Programming solutions and a few other issues

Upgrading a Rails 2.2.2 app to Rails 3

Posted by Jim Morris on Fri Dec 17 15:27:17 -0800 2010

After porting my blog engine from Merb to Rails3 code here I thought I'd upgrade my Snow Dogs R Us Site (aka dogz.us) to Rails 3.

It was written in Rails 2.2.2 so I figured it would not be too hard, well it was easier to port my Merb App than it was my Rails app!

Most of the issues were related to plugins currently not working with Rails 3, and having to find equivalent ones and port to them, so to be fair the actual change from Rails 2 to Rails 3 was not that hard, but fixing several of the plugins was a pain.

First I followed several blogs the most useful being this one.

That got most of the mundane stuff done. Especially using the upgrade plugin helped.

Then the plugins had to be upgraded, several do not seem to have been updated by the original owner so I had to point to several other git repos, it is easy if they are on Github, use the network tool and pick the latest one and see if they have a rails 3 branch. Several of the plugins I was using are now gems, so I just added them to my Gemfile and removed them from vendor/plugin,

I was using calendar-date-select but switched to using JQuery date select instead and the new Unobtrusive JavaScript pattern. I had to add the jquery-ui-timepicker-addon to get data and time selected in one input field.

I also was using will-paginate, but switched to just previous and next links. Although will-paginate has a Rails 3 version

gem 'will_paginate', '3.0.pre2'

I was using cssFormBuilder, which no longer appears to be supported so I switched to simple_form which meant going through all my forms and changing them, but ended up with a pleasant result.

acts-as-taggable(-on steroids) does not seem to have been updated to Rails 3 so I switched to acts-as-taggable-on which required minimal changes.

acts-as-commentable has been upgraded and I just needed to switch to the gem version.

attachment-fu was the biggest pain, it no longer works on Rails 3, and the repos I tried that supposedly had fixed them did not work for me, so I switched to paperclip, which works fine with Rails 3, and there are a few blogs showing how to upgrade from attachment-fu to paperclip. I wrote this little script to copy the files over from attachment-fu to paperclip...

and here is the migration...

I needed a few plugins...

rails plugin install git://github.com/rails/dynamic_form.git
rails plugin install git://github.com/rails/verification.git

Just so I didn't have to change too much these are mostly compatibility plugins. However after getting everything working I removed the dynamic_form plugin and switched to simple JQuery UJS techniques to handle the remote calls.

The other big pain that was a lot of work is the new auto escaping of text in Rails Views. I already had used h() on all user inputs, but now most of my view helpers were broken because they did not generate html_safe strings. The fix was either to use != in my HAML views (!= in HAML will print the result raw with no escaping) so it would override the escaping, or use String#html_safe in my helpers to mark my strings as safe.

A few of my plugins continued to work fine with very minor fixes like acts_as_rated used class_name to get the name of a class in ActiveRecord, but that has been removed so I used base_class in the two places it was used. I fixed it in my clone of the plugin and issued a Paul request, which I believe has been accepted.

The auto_complete plugin doesn't work out of the box either, probably because inline javascript is frowned on now and unobtrusive javascript is all the rage. There is a git repo which supposedly fixes it for Rails 3, and it does work except you need to html_safe the strings it returns. (or use != in haml).

rails plugin install git://github.com/fcioffi/auto_complete.git -r rails3 is what you need to do to use this.

However after getting it to work, I deleted it and switched all my uses of auto_complete to use the JQuery based autocomplete. Which worked pretty well with a little tweaking here and there. I had to look at the source code to determine that if I wanted to get multiple autocompletes using a comma I had to add 'data-delimiter' => ',' to the form input.

The most work was getting all my specs to pass, apart from minor changes to the application itself, RSpec 2 has changed quite a lot, and updating my specs to work with RSpec 2 requires a lot of tedious work:-

  • needed to rename all my views from .haml to .html.haml, (or .html.erb) although Rails 3 seemed OK with the old naming RSpec2 view specs failed to find the templates with the old name.

  • rewrite all my view specs...

    • change ...@controller.template. to view.

    • change have_tag to have_selector and change the parameters

      • place holders not supported, need to use #{}
      • add :content => for any text in second parameter
      • could also use have_xpath, which allows things like have_xpath('//a', :href => "/post/#{post.id}/edit", :content => 'Edit')
    • change assign[:blah]= :blod to assign(:blag, :blod)

    • change the describe "a descripion" do to have the path to the view rather than text description describe "events/show.html.haml"

    • change the render '/show' to just render or render :file => 'full template spec'

    • change have_text to match(/../)

    • change response.should to rendered.should

  • modify the controller specs

    • controller_name is no longer supported, so need to nest all my describes under a top level describe that has the controller name. describe 'UsersController' do or use subject {SomeController.new} (not tested yet)

    • when using post/delete/put :something, :id => 1 passing in an integer id used to work, now it needs to be a string (probably a rails3 thing) delete :destroy, :id => "1"

  • helper specs only needed minor changes

    • change have_tag to have_selector or have_xpath

After all this the specs passed and the app was updated to use JQuery and UJS, however I had a few DEPRECATION warnings some of which were hard to track down...

To get rid of these deprecation warnings...

DEPRECATION WARNING: "Rails.root/test/mocks/development"...
DEPRECATION WARNING: "Rails.root/test/mocks/test"...

Just delete the test/mocks directory.

So for a fairly small Rails app of around 5 KLOC it took about 10 days to fully upgrade to Rails 3, switching over to UJS everywhere and fix the specs.

To be honest I don't think it was worth the effort, but it was a great learning experience, and I had the free time! and now I know all about Rails 3.

Posted in Rails  |  Tags upgrade,rails3  |  3 comments


  1. Michael said on Fri Jun 24 00:24:08 -0700 2011
    Wow yeah so I *just* made a git branch to try to upgrade my rails 2.2.2 app to 3.0 and you confirmed my biggest fears.. it's not gonna be worth it.. I too use attachment_fu but even bigger of a pain in the ass is that I'm using engines which is probably gonna be another nightmare to untangle to work with rails 3... man... this sucks but at some point I'll have to bite the bullet and upgrade because of security patches.
  2. Eduardo said on Sat Sep 10 23:14:12 -0700 2011
    act-as-taggable-on-steroids did update to Rails 3. If you use act-as-taggable-on, you are going to have issues with the "context" column added in it, that was not present in the steroids one. If you already had a database populated with tags created based on the steroids one, you are going to have problems. At least I guess that the "not steroids" one has a caching that works, because in steroids one I never got it to work.
  3. wolfman said on Sun Sep 11 01:05:27 -0700 2011
    I had to use the following migration to covert

    class AddTaggableOnColumns < ActiveRecord::Migration
      def self.up
        add_column :taggings, :tagger_id, :integer
        add_column :taggings, :tagger_type, :string
        add_column :taggings, :context, :string
        add_column :taggings, :created_at, :datetime

        remove_index :taggings, [:taggable_id, :taggable_type]

        # we need to set the context for exisiting tags
        execute "UPDATE taggings SET context = 'tags'"

        add_index :taggings, [:taggable_id, :taggable_type, :context]

      def self.down
        remove_index :taggings, [:taggable_id, :taggable_type, :context]
        remove_column :taggings, :tagger_id
        remove_column :taggings, :tagger_type
        remove_column :taggings, :context
        remove_column :taggings, :created_at
        add_index :taggings, [:taggable_id, :taggable_type]

(leave email »)