One of the first things I needed to have done at my new job was to convert the application from the Ruby on Rails 1.2.6 to the latest 2.x version. This wasn't the first time I've upgraded a 1.x app to Rails 2, but it's the first time with a significantly complex application. Some issues we ran into:
The dreaded "Expected X to define Y" error message. This message can come from a few places in the rails code, and it is a usually useless message that obscures the real problem. In this case I added a rescue clause around an internal require statement in active_support/dependencies.rb and reported the true culprit: a model definition was bombing out when trying to require acts_as_list. Many of the acts_as components of ActiveSupport were pulled out to plugins in Rails 2. So the solution was to script/install acts_as_list.
The rake deprecated task is handy, but it just does a context-free grep of the source code and warns about code that isn't problematic, like complaining about @session_key with "@session is deprecated, use session instead". Still it's a helpful place to start. It didn't catch everything of course. It was fine spotting the deprecated render_partial calls, but it didn't complain about render_text for example.
We had a number of problems in tests that had been dependent on the fact that in Rails 1 fixtures loaded into models didn't set the timestamps. In other words, unless you specified a created_at or updated_at in the YAML file you got NULL for those columns in the database. In Rails 2 apparently that's no longer true. It sets the timestamps on all fixtures. A quick workaround for this that worked for us was to clear the fixtures before tests that were dependent on only certain records having timestamps. The tests and fixtures will need to be fixed eventually.
Behavior change in the radio_button_tag. Rails 2 added this:
def radio_button_tag(name, value, checked = false, options = {})
[...]
pretty_name = name.to_s.gsub(/\[/, "_").gsub(/\]/, "")
[...]
end
Which changed the ids on several of our input tags to something arbitrarily prettier but unexpected by the application.
Associations on models were screwy. From script/console:
>> g.game_category
NoMethodError: undefined method `game_category' for #
from /Library/Ruby/Gems/1.8/gems/activerecord-2.0.1/lib/active_record/attribute_methods.rb:205:in `method_missing' from (irb):2
>> g.game_category_id
=> 1
>> class << g
>> belongs_to :game_category
>> end
=> nil
>> g.game_category
=> #
This was annoying. I dug back into the active_support/dependencies.rb and started puts-ing around with the code that does the loading. I found that the Game constant was being defined when the Migrator class was being loaded. It turns out that a migration was defining Game in order to cheaply access some ActiveRecord methods, but not bothering to declare the associations. I could have added the association in the migration, and I thought for a moment of changing the AutomaticMigration code to clear any constants it loaded when it was finished, but decided the safer and quicker appproach is to just rename the class in the migration and use set_table_name to point it at the games table. This goes along with my general notion that using ActiveRecords in migrations is a bad idea.