I have created a few rails 3 sites, but i have never before ported an existing rails2 application to rails3. I will describe the problems i encountered. First off there were some very good resources (step-by-step descriptions) to guide me through it:
- the rails_upgrade plugin
- The asciicast (readable) upgrading to rails3 part 1
- and part 2 The conversion ran pretty smoothly, but I will describe the things i bumped into that were less obvious.
ActiveSupport::Callbacks
Before we wrote: [ruby] module Workflow class Base define_callbacks :before_something, :after_something def some_method run_callbacks :before_something puts "some method" run_callbacks :after_something end end class SpecificWorkflow < Base before_something :do_something def do_something puts "something" end end [/ruby] Now, this has changed. Into the following: [ruby] module Workflow class Base define_callbacks :something def some_method run_callbacks :something do puts "some method" end end end class SpecificWorkflow < Base set_callback :something, :before, :do_something def do_something puts "something" end end [/ruby] Which is pretty nice. It has the disadvantage we can only use :before and :after
callbacks anymore, but that was easily solved.
ActionController::ParamsParser
We hooked into the Rack middleware before the ActionController::ParamsParser
to make sure we return Bad Request when the parsing fails. This middleware has been renamed to ActionDispatch::ParamsParser
. Easy fix :)
safe_helper
removed?
We prepared our Rails 2 project a long time ago, and used the rails_xss plugin. We used the safe_helper
all over our helper-methods, but apparently this is not supported in Rails 3. Bummer. So, before [ruby] def some_helper "<strong>something</strong>" end safe_helper :some_helper [/ruby] After: [ruby] def some_helper "<strong>something</strong>".html_safe end [/ruby] A bit annoying to do, a bit unfortunate, because we thought we were preparing ourselves for a smoother upgrade. Not entirely so.
The routes!
Only after those steps did my rails get far enough to discover my routes. I did not change anything about the routes, just placed inside the correct block. I deleted my index.html
and put back my original application.html.haml
and the first view was working.
The missing to_key
The to_key
was missing inside the user_session
model. I described that solution already in this blogpost. So i just needed to fix that.
Replacing ActionController::RecordIdentifier.singular_class_name
We used ActionController::RecordIdentifier.singular_class_name
, in rails3 this is replaced by ActionController::RecordIdentifier.dom_class
.
Missing translations
A feature we used a lot was I18n.t('.filter')
and this would look for operators.filter.filter
because the line was inside a partial _shared/_filter.html.haml
and called from the operators/index.html.haml
. Now this does not work anymore, and now instead I18n looks for _shared.filter.filter
. I did not find an easy way to solve this, just moved some translations. Luckily we did not override the translations of a partial based on the context where it was rendered yet. Does anybody has any tips on that?
Upgrading javascript helpers
In rails3 unobtrusive is the way to go. So, a remote_form_for
like [ruby] - form_remote_tag :url => update_url do [/ruby] becomes [ruby] - form_tag :url => update_url, :remote => true do [/ruby] Which seems easy enough. It does get more complicated when the :update
tag is used. For example: [ruby] - remote_form_for post, :update => {:success => "result_#{id}"} do |form| [/ruby] becomes [ruby] - remote_form_for post, :remote => true, :html => {:class => 'remote-post-form', :'data-update' => "#result_#{id}"} do |form| [/ruby] The :update
is no longer supported as before, and we have to perform some glueing ourselves. So i replaced the :update
by data-update
. And then inside javascript (e.g. application.js
) we can do the following: [javascript] jQuery(function($) { $(".remote-post-form") .bind("ajax:success", function(data, status, xhr) { var update_selector = $(this).attr('data-update'); $(update_selector).html(status); }); }); [/javascript] If you need more elaborate example, check this question on stackoverflow.
master_helper_module
This has been replaced by _helpers
.
active_layout
The active_layout
method, of a controller, no longer exists inside rails3. To replace it you need to use two methods:
action_has_layout?
returns true if a layout exists.- To get the layout-name, i have no better solution then calling the private method
_layout
. This can be done, as known, by doingcontroller.send(:_layout)
.
Upgrade rspec
I had to upgrade Rspec 1 to v2. First off in the Gemfile i added the correct version. I removed the rspec.rake
file. I ran [ruby] rails g rspec:install [/ruby] But still all rake spec
commands were missing. The fix was simple. In the Gemfile
make sure the rspec-rails
is also usable from development mode: [ruby] group :development, :test do gem 'rspec-rails' end [/ruby] Now on to fixing the tests!
Fixing the tests
In the upgrade from rspec1 to rspec2 a lot of things have changed. What i encountered:
- replace all
Spec::
byRSpec::
controller_name 'users'
no longer exists, write a surroundingdescribe UsersController do
instead (that is also clearer imho)- ...
Extending Rspec::Rails::ExampleGroup::ControllerExampleGroup
Before, in rspec1, ControllerExampleGroup
was a class and you could add code to it. So for instance, we had a file like this in our support folder: [ruby] module RSpec module Rails module Example class ControllerExampleGroup let(:the_account ) { Factory(:account) } let(:the_user ) { Factory(:user)} end end end end [/ruby] A bit larger, but you get the picture: this allowed us to define a set of shared let
definitions. In rspec-2 this is no longer possible this way, because ControllerExampleGroup
is now a module. I handled that like this: [ruby] module Lets def self.included(base) base.let(:the_account ) { Factory(:account) } base.let(:the_user ) { Factory(:user)} end end [/ruby] and inside my spec_helper
this file is automatically required (since it is stored in the support folder) and i just add [ruby] config.include(Lets) [/ruby]
Rspec: replace config.extend
I also noticed that using config.extend(ModuleName)
dit not work anymore. Instead i had to write (on the same place, inside the configure block) : [ruby] Rspec.configure do |config| .. include ModuleName .. end [/ruby] and that worked for me.
More?
Do you have more hints to share? What bumps did you find and how did you overcome them?