Upgrading to Rails 2.3.2 from Rails 2.1
We recently upgraded our Rails app at TST Media from Rails 2.1 to Rails 2.3.2. It was a pain in the ass! A little background on our application first. We have 36,600 lines of code, 322 models, 108 controllers, and a relatively weak test suite. Additionally we upgraded the majority of our gems and plugins, of which we have 32 gems and 16 plugins in our vendor directory. Thats 48 third-party open source ruby libraries our application depends on! Crazy. The application originated back in August 2006 when it was first running on Rails 1.1.2.
git rm -r vendor/rails
git ci -a -m "removing rails to prepare for upgrade"
rake rails:freeze:edge RELEASE=2.3.2
Here are the issues we ran into during the upgrade, many of which were not documented in the release notes:
1. In Rails 2.1 and earlier, when using check_box_tag the convention is to put a hidden_field_tag after it with the same name and a value of 0 if you need there to always be a name/value pair sent for the check box. The check_box form helper did just this. The browser would then send the check_box_tag value if it is checked, otherwise it would send the hidden_field_tag value. Well, for some reason, in Rails 2.3 the order is swapped. The hidden_field_tag must come before the check_box_tag. The Rails 2.3.2 check_box form helper swapped the order as well.
2. The form helper methods check_box_tag, check_box, select_tag, select, text_field_tag, text_field,(and others?) changed how they set the id of the form field when the name of the form field includes square brackets. Ex. select_tag('user[gender]') creates a select form field with a name of 'user[gender]' and an id of 'user_gender'. In Rails 2.1 and earlier the id of the select tag would be 'user[gender]'. I have no idea why this change was made in Rails, but it was a huge pain in the ass and I did not see this change documented anywhere! It broke all sorts of custom javascript which depended on the id of the form fields. Dozens of observe_field statements no longer worked as well since they were observing form fields that did not exist. And worse yet, for some reason text_area and text_area_tag did not change, so text_area_tag('post[description]') results in a text area with an idea of 'post[description]'. This inconsistentcy is unfortunate.
3. The behavior of observe_field when observing checkboxes has changed. In Rails 2.1 the event would be fired when checking or unchecking a checkox, whereas with Rails 2.3.2 the event is only fired when checking the checkbox. I assume this change can be attributed to the upgrade of the prototype javascript library. Usually when we are observing a checkbox we are simply calling a javascript function when the event is fired, as opposed to doing an ajax request, and so the fix is to simply not use observe_field and replace it with the standard onclick= within the HTML. Unobtrusive javascript be damned! I've seen so many issues with observing events on checkboxes, radios, and compatibility across browsers. It just isn't worth the bother. Going with the standard onclick="myFunction()" always works.
4. We ran into a weird issue in development mode when using render :file => 'mytheme', which we were doing to serve up dynamically generated css files. All of our theme css files are served through a single action, and the name of the file to render depends on the theme. The problem is that when changing 'mytheme' in development mode and refreshing the browser the changes do not come through until restarting the server. Not a big deal for me, but it was driving our designer crazy! So somewhere Rails is caching anything that goes through render :file. On initial inspection we realized that ETags were to blame. The ETag header is automatically set by Rails 2.3.2 when using render :file and a 304 not modified response is sent after the first time the file is requested. Modifying the file does not cause the ETag to go stale. Setting request.headers['ETag'] = nil causes the ETag header to not be sent, which we thought would fix the problem. It did not. Rails is still caching the contents of the file somewhere else! Instead of digging into this further, we switched away from using render :file and used render :action instead. In production mode we actually have Nginx serve up a cached version of the theme css files which is why we had no need of caching support built into the Rails app for this request.
Also while inspecting this issue I ran across the new configuration parameter, cache_template_loading which if set to true caches view files so they are not read from disk more than once. By default this is turned off in development mode, and further this has no affect on render :file anyway.
5. Previously we did not have our gems specified in environment.rb using config.gem. Due to an annoying gem spec warning we had to specify all our gems in environment.rb, which we should've done earlier with Rails 2.1 anyway. This is the gem spec warning:
config.gem: Unpacked gem ym4r-0.6.1 in vendor/gems has no specification file. Run 'rake gems:refresh_specs' to fix this.
Running rake gems:refresh_specs does not fix this unless the gem is specified in the environment.rb file using a config.gem line such as this:
config.gem "ym4r"
5.5. A bug was introduced somewhere between Rails 2.1 and 2.3 dealing with the serialization of integers, booleans, and floats. In Rails 2.1 and before, a serialized integer resulted in an integer when the attribute is unmarshaled. In Rails 2.3 serializing an integer results in a string, which is clearly not the desired behavior! See lighthouse ticket https://rails.lighthouseapp.com/projects/8994/tickets/1379-serialized-fixnum-returns-a-string-after-save
6. In test_helper.rb we renamed the class to ActiveSupport::TestCase
7. Installed SystemTimer gem and upgraded memcache-client to 1.7.2. Brought memcache_util.rb into lib directory as it was removed from memcached_client 1.7.2.
8. Had to add this cache direcotry since it was ignored by .gitignore file
git add -f vendor/rails/activesupport/lib/active_support/cache
9. Changed a few units tests to extend from ActiveSupport::TestCase instead of Test::Unit::TestCase
10. Changed the session_key for cookies to be set in the environment file.
11. Changed the image_path rails patch use of relative_url_root to: ActionController::Base.relative_url_root
12. Changed assert_redirected_to to be less specific with a partial match
13. Change deprecated session.session_id to request.session_options[:id]
14. Moved views/store/store_mailer/_result_details.html.erb to _result_details.rhtml..... Rails 2.3.2 can't handle partials for mailers with .html.erb unless you specify the partial extension directly.
15. Removed binding argument from concat call since it was deprecated.
16. Remove my own rails patch for caching of HABTM columns since it has been incorporated into rails 2.3.2