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:
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
Now, this has changed. Into the following:
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
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
def some_helper
"<strong>something</strong>"
end
safe_helper :some_helper
After:
def some_helper
"<strong>something</strong>".html_safe
end
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
- form_remote_tag :url => update_url do
becomes
- form_tag :url => update_url, :remote => true do
Which seems easy enough. It does get more complicated when the :update tag is used.
For example:
- remote_form_for post, :update => {:success => "result_#{id}"} do |form|
becomes
- remote_form_for post, :remote => true, :html => {:class => 'remote-post-form', :'data-update' => "#result_#{id}"} do |form|
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:
jQuery(function($) {
$(".remote-post-form")
.bind("ajax:success", function(data, status, xhr) {
var update_selector = $(this).attr('data-update');
$(update_selector).html(status);
});
});
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
rails g rspec:install
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:
group :development, :test do gem 'rspec-rails' end
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 doinstead (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:
module RSpec
module Rails
module Example
class ControllerExampleGroup
let(:the_account ) { Factory(:account) }
let(:the_user ) { Factory(:user)}
end
end
end
end
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:
module Lets
def self.included(base)
base.let(:the_account ) { Factory(:account) }
base.let(:the_user ) { Factory(:user)}
end
end
and inside my spec_helper this file is automatically required (since it is stored in the support folder) and i just add
config.include(Lets)
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) :
Rspec.configure do |config| .. include ModuleName .. end
and that worked for me.
More?
Do you have more hints to share? What bumps did you find and how did you overcome them?
