Migrating big applications from Rails 2 to Rails 3

Today will have a rather long and technical post, focused on all the steps needed for migrating big applications from Rails 2 to Rails 3.

First question: why bother?
Rails 2 is great, but Rails 3 is awesome.
Apart from all new goodies given by Rails 3 (assets, increased security, bundler…), more and more Rails libraries ship only for Rails 3.
Not upgrading is no longer an option for an application that is bound to be maintained in the long run.

I got a bunch of Rails 2 applications, and when reading on-line resources, I got strong confidence: upgrading from Rails 2 to 3 is easy! Right?

Well, considering your Rails 2 application kept the simplicity of the default Rails’ blog example, yes: it will be easy.
For real world applications, situation is a bit more complex.

So this post tries to enumerate all the steps I had to take, and all the problems I got when migrating big Rails applications from 2.3.x to 3.2.9.

If not already the case, make sure your application is currently running using the latest version of Rails 2.3.x. If not, migrate first to this version.
Then there are some steps you can perform while your application is still using Rails2: details here. This will greatly simplify your migration afterwards.

  1. First steps: Rails 3 and upgrade setup
  2. Upgrade basics
    1. Check points to upgrade
    2. Generate Rails3 files
    3. Merge files with Rails2 backups
    4. Session secret setting
    5. Cookie verifier secret
    6. Routing
    7. Bundler used to handle dependencies
    8. Test helpers path
    9. Application configuration
    10. filter_parameter_logging deprecated
    11. Block helpers in ERB
    12. Deprecated RAILS_ENV, RAILS_ROOT, RAILS_DEFAULT_LOGGER
    13. Remove useless migration plugin
    14. Remove useless .rails2 files
  3. Other tasks, not as straightforward
    1. Use raw to avoid escaping HTML characters in views
    2. Use assets to serve your public images, stylesheets, javascript files
    3. No more stylesheets controllers to generate CSS files
    4. Writing helpers using concat does not work anymore
    5. Changing table names for models has changed
    6. Recognizing routes has changed
    7. Rails3 declare more MIME types
    8. Accessing current controller from the environment has changed
    9. Migrate to Devise gem version >= 2.0
    10. Use Prototype library
    11. link_to_function API changed and is deprecated
    12. Models’ attributes can’t be mass assigned anymore by default
    13. Form error messages don’t work anymore
    14. The Mailer interface has changed

First steps: Rails 3 and upgrade setup

First thing to do is to install Rails 3.

> gem install rails

Rails developers have provided a little helper script that will diagnose your Rails 2 application and tell you what files need attention.
This script is installed this way:

> ruby script/plugin install git://github.com/rails/rails_upgrade.git
Initialized empty Git repository in /path/to/railsapp/vendor/plugins/rails_upgrade/.git/
remote: Counting objects: 27, done.
remote: Compressing objects: 100% (22/22), done.
remote: Total 27 (delta 3), reused 20 (delta 3)
Unpacking objects: 100% (27/27), done.
From git://github.com/rails/rails_upgrade
 * branch            HEAD       -> FETCH_HEAD
Thanks for installing the Rails upgrade plugin.  This is a set of generators and analysis tools to help you upgrade your application to Rails 3.  It consists of three tasks...

To get a feel for what you'll need to change to get your app running, run the application analysis:

    rake rails:upgrade:check

This should give you an idea of the manual changes that need to be done, but you'll probably want to upgrade some of those automatically.  The fastest way to do this is to run 'rails .', which will simply generate a new app on top of your existing code.  But this generation also has the effect of replacing some existing files, some of which you might not want to replace.  To back those up, first run:

    rake rails:upgrade:backup

That will backup files you've probably edited that will be replaced in the upgrade; if you finish the upgrade and find that you don't need the old copies, just delete them.  Otherwise, copy their contents back into the new files or run one of the following upgraders...

Routes upgrader
===============

To generate a new routes file from your existing routes file, simply run the following Rake task:

    rake rails:upgrade:routes

This will output a new routes file that you can copy and paste or pipe into a new, Rails 3 compatible config/routes.rb.

Gemfile generator
=================

Creating a new Gemfile is as simple as running:

    rake rails:upgrade:gems

This task will extract your config.gem calls and generate code you can put into a bundler compatible Gemfile.

Configuration generator
=======================

Much of the configuration information that lived in environment.rb now belongs in a new file named config/application.rb; use the following task to generate code you can put into config/application.rb from your existing config/environment.rb:

    rake rails:upgrade:configuration

Now we are ready to use all those goodies to help us upgrading our application.

Upgrade basics

Check points to upgrade

As the upgrade script told us, let’s start with checking what needs immediate attention.

> rake rails:upgrade:check
Deprecated session secret setting
Previously, session secret was set directly on ActionController::Base; it's now config.secret_token.
More information: http://lindsaar.net/2010/4/7/rails_3_session_secret_and_session_store

The culprits: 
        - config/initializers/session_store.rb

Old router API
The router API has totally changed.
More information: http://yehudakatz.com/2009/12/26/the-rails-3-router-rack-it-up/

The culprits: 
        - config/routes.rb

Deprecated test_help path
You now must require 'rails/test_help' not just 'test_help'.
More information: http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices

The culprits: 
        - test/test_helper.rb

Deprecated filter_parameter_logging calls
The list of filtered parameters are now stored in /config/application.rb. For example: config.filter_parameters += [:password]
More information: http://de.asciicasts.com/episodes/224-controller-in-rails-3

The culprits: 
        - app/controllers/application_controller.rb

New file needed: config/application.rb
You need to add a config/application.rb.
More information: http://omgbloglol.com/post/353978923/the-path-to-rails-3-approaching-the-upgrade

The culprits: 
        - config/application.rb

Deprecated ERb helper calls
Block helpers that use concat (e.g., form_for) should use <%= instead of <%.  The current form will continue to work for now, but you will get deprecation warnings since this form will go away in the future.
More information: http://weblog.rubyonrails.org/

The culprits: 
        - app/views/activities/_form.html.erb
        - app/views/user_carts/_checkout_forms.html.erb

Deprecated constant(s)
Constants like RAILS_ENV, RAILS_ROOT, and RAILS_DEFAULT_LOGGER are now deprecated.
More information: http://litanyagainstfear.com/blog/2010/02/03/the-rails-module/

The culprits: 
        - app/controllers/application_controller.rb
        - app/helpers/application_helper.rb

Wow! This is quite a big list of culprits.
Let’s backup some files before updating them. Here again, a helper script comes in handy.

> rake rails:upgrade:backup

* backing up .gitignore to .gitignore.rails2
* backing up app/controllers/application_controller.rb to app/controllers/application_controller.rb.rails2
* backing up app/helpers/application_helper.rb to app/helpers/application_helper.rb.rails2
* backing up config/routes.rb to config/routes.rb.rails2
* backing up config/environment.rb to config/environment.rb.rails2
* backing up config/environments/development.rb to config/environments/development.rb.rails2
* backing up config/environments/production.rb to config/environments/production.rb.rails2
* backing up config/database.yml to config/database.yml.rails2
* backing up doc/README_FOR_APP to doc/README_FOR_APP.rails2
* backing up test/test_helper.rb to test/test_helper.rb.rails2

This is a list of the files analyzed and backed up (if they existed);
you will probably not want the generator to replace them since
you probably modified them (but now they're safe if you accidentally do!).

- .gitignore
- app/controllers/application_controller.rb
- app/helpers/application_helper.rb
- config/routes.rb
- config/environment.rb
- config/environments/development.rb
- config/environments/production.rb
- config/environments/staging.rb
- config/database.yml
- config.ru
- doc/README_FOR_APP
- test/test_helper.rb

Generate Rails3 files

If you didn’t backup your files previously (using rake rails:upgrade:backup or other means), be careful when conflicts are found during the new files generation.

Generating Rails3 files is done this way in your application’s directory:

> rails new .
       exist
      create  README.rdoc
    conflict  Rakefile
Overwrite /path/to/railsapp/Rakefile? (enter "h" for help) [Ynaqdh] Y
       force  Rakefile
      create  config.ru
...
Using rails (3.2.9)
Using sass (3.2.3)
Using sass-rails (3.2.5)
Using sqlite3 (1.3.6)
Using uglifier (1.3.0)
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

Merge files with Rails2 backups

The backup step has created a bunch of .rails2 files everywhere in your application.
You have to review each one of them, and port their content into their corresponding Rails3 migrated file.

For example: port the content of app/controllers/application_controller.rails2.rb into app/controllers/application_controller.rb, respecting the structure of the newly generated file.

Part of the merging conflicts is covered in the following sections.

Session secret setting

Use Railsapp::Application.config.session_store instead of ActionController::Base.session to store the session store key, and eventually move the secret to an extra file. Details are given here.

# Be sure to restart your server when you modify this file.

# Rails 2 - Obsolete
# ActionController::Base.session = { :key => ‘mykeysession’, :secret => ‘somereallylongrandomkey’ }
# Rails 3
Railsapp::Application.config.session_store :active_record_store, :key => 'mykeysession'

The cookie verifier secret is usually set in file config/initializers/cookie_verification_secret.rb. The way to set it has changed:

# Rails2
# ActionController::Base.cookie_verifier_secret = 'A BIG STRING';
# Rails3
ActionController::Base.config.secret_token = 'A BIG STRING';

Routing

Routing format has completely changed, but here comes a little helper script in handy to migrate most of them:

> rake rails:upgrade:routes >config/routes.rails3.rb

Then, you can review your new Routes file, and replace the content of config/routes.rb with the one of config/routes.rails3.rb.
Details of these modifications can be found here.

Here is a little example of a migrated routes.rb file:

Railsapp::Application.routes.draw do
  
  # Resources
  resources :my_resources
  match 'resources/feed' => 'resources#feed', :as => :feed_resources, :format => 'atom'

  resources :items do
    member do
      get :display
    end
  end

  # Statistics
  match 'statsdisplay' => 'stats#display', :as => :statsdisplay

  # User carts
  resources :user_carts do
    resources :cart_contents
  end

  # Users
  devise_for :users, :controllers => { :registrations => 'registrations' }
  resources :users do
    resources :user_event_subscriptions
    resources :user_carts
    member do
      get :edit_external_password
      get :current_cart
    end
  end

  # Static pages
  match 'live' => 'home#live', :as => :live
  root :to => 'home#index'

  # Default pages
  match '/:controller(/:action(/:id))'
  match '*path' => 'home#handle404'

end

Bundler used to handle dependencies

If you don’t know yet about bundler, take a look at this. This is the tool Rails3 now uses to handle Gem dependencies.
If you haven’t done it already, migrate your application to bundler: a little helper script does everything:

> rake rails:upgrade:gems >Gemfile

You can review your Gemfile, and add eventually add missing dependencies that were formerly declared in your config/environment* files. Here is an example:

source 'https://rubygems.org'

gem 'rails', '3.2.6'

# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'

gem 'sqlite3'
gem 'json'

# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails',   '~> 3.2.3'
  gem 'coffee-rails', '~> 3.2.1'

  # See https://github.com/sstephenson/execjs#readme for more supported runtimes
  gem 'therubyracer'

  gem 'uglifier', '>= 1.0.3'
end

gem 'jquery-rails'
gem 'prototype-rails'

# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'

# To use Jbuilder templates for JSON
# gem 'jbuilder'

# Use unicorn as the app server
# gem 'unicorn'

# Deploy with Capistrano
# gem 'capistrano'

# To use debugger
#gem 'ruby-debug19'

gem 'mongrel', '1.2.0.pre2'
gem 'nokogiri'
gem 'mechanize'
gem 'warden'
gem 'devise'
gem 'devise-encryptable'
gem 'cancan'
gem 'recaptcha', :require => 'recaptcha/rails'
gem 'friendly_id'
gem 'dynamic_form'
gem 'rUtilAnts', '>= 1.0', :require => false
gem 'rails-ajax'

group :test do

  # RSpec
  gem 'rspec'
  gem 'rspec-rails'

  # Cucumber
  gem 'cucumber-rails', :require => false
  gem 'database_cleaner'
  gem 'launchy'

  # Capybara-webkit
  gem 'capybara-webkit'
  
  # Spork
  gem 'spork-rails'

  # Guard
  gem 'guard'
  gem 'guard-cucumber'
  gem 'guard-spork'

end

Once your Gemfile has been created, you have to run bundle install to make sure all dependencies are met.

Test helpers path

Require rails/test_help instead of test_help (usually found in test/test_helper.rb).

ENV["Rails.env"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'

class ActiveSupport::TestCase
  # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
  #
  # Note: You'll currently still have to declare fixtures explicitly in integration tests
  # -- they do not yet inherit this setting
  fixtures :all

  # Add more helper methods to be used by all tests here...
end

Application configuration

Most of the application configuration has been moved from config/environment.rb to a new file called config/application.rb. A little helper script helps us generate this file:

> rake rails:upgrade:configuration >config/application.rb

Review the configuration file config/application.rb. Here is an example:

require File.expand_path('../boot', __FILE__)

require 'rails/all'

if defined?(Bundler)
  # If you precompile assets before deploying to production, use this line
  Bundler.require(*Rails.groups(:assets => %w(development test)))
  # If you want your assets lazily compiled in production, use this line
  # Bundler.require(:default, :assets, Rails.env)
end

module Railsapp
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.

    # Custom directories with classes and modules you want to be autoloadable.
    # config.autoload_paths += %W(#{config.root}/extras)

    # Only load the plugins named here, in the order given (default is alphabetical).
    # :all can be used as a placeholder for all plugins not explicitly named.
    # config.plugins = [ :exception_notification, :ssl_requirement, :all ]

    # Activate observers that should always be running.
    # config.active_record.observers = :cacher, :garbage_collector, :forum_observer

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de

    # Configure the default encoding used in templates for Ruby 1.9.
    config.encoding = "utf-8"

    # Configure sensitive parameters which will be filtered from the log file.
    config.filter_parameters += [
      :password,
      :password_clear,
      :password_verify
    ]

    # Use SQL instead of Active Record's schema dumper when creating the database.
    # This is necessary if your schema can't be completely dumped by the schema dumper,
    # like if you have constraints or database-specific column types
    # config.active_record.schema_format = :sql

    # Enforce whitelist mode for mass assignment.
    # This will create an empty whitelist of attributes available for mass-assignment for all models
    # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
    # parameters by using an attr_accessible or attr_protected declaration.
    # config.active_record.whitelist_attributes = true

    # Enable the asset pipeline
    config.assets.enabled = true

    # Version of your assets, change this if you want to expire all your assets
    config.assets.version = '1.0'

  end
end

filter_parameter_logging deprecated

Parameters filtered from log files are now defined in the previously generated config/application.rb file.

Delete all filter_parameter_logging calls (usually found in app/controllers/application_controller.rb), and update your config/application.rb file accordingly.

Block helpers in ERB

This is one of the most annoying changes in big projects: in ERB views, block helpers now return strings instead of silently concatenating their result to the output.

This means that using form_for, form_tag, fields_for and link_to (even in its block form) are now called using <%= instead of <%.

<%= form_for(resource) do |f| %>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.submit button_name %>
  </p>
<% end %>

<%= link_to resource_path do %>
  <img src="myimg.png"/>
<% end %>

You have to replace all former calls in all your views.

Deprecated RAILS_ENV, RAILS_ROOT, RAILS_DEFAULT_LOGGER

This one can be seen as simply as a little text replacement in your source files (details here):

Replace with:
RAILS_ENV Rails.env
RAILS_ROOT Rails.root
RAILS_DEFAULT_LOGGER Rails.logger

Remove new_rails_default

The file config/initializers/new_rails_default was intended for Rails2 to Rails3 migration preparation. Every configuration it contains is now a default in Rails3.

You can simply delete this file.

Remove useless migration plugin

The migration plugin installed at the beginning of this tutorial is inside vendor/plugins/rails_upgrade. However this location is marked as deprecated for plugins, and this directory can be removed, now that we won’t use rake rails:upgrade:* tasks anymore.

Remove useless .rails2 files

At that point, you have completed all the common migration tasks. For simple websites, that will be all.

You can now remove all .rails2 files.

Next lie extra steps that bigger websites might need to finalize their migration correctly.

Other tasks, not as straightforward

And now comes the candy: all the tasks that were not listed as part of a Rails3 migration.
Some of them might not apply to your application, as it can deal with external gems, or specific usages of the Rails API.

Use raw to avoid escaping HTML characters in views

For big projects using heavy HTML generation in views, this can be a real pain.

In Rails2, you could use view helpers with HTML characters. For example, to display an image as a link:

<%= link_to '<img src="myimg.png"/>', resource_path %>

However in Rails3 for security reasons, this will escape all HTML characters, and instead of having a link on your image, you will have a link on the text <img src="myimg.png"/>.

If you want to force keeping your string as you intended, use raw method:

<%= link_to raw('<img src="myimg.png"/>'), resource_path %>

The same goes for any Ruby string that is returned directly into a view:

<%= raw '<p>I don\'t want the p tag to be escaped, therefore I use raw.</p>' %>

Therefore the same goes for any view helper you might have defined that was bound to return HTML snippets:

def add_bold(text)
  bolded_text = "<b>#{text}</b>"
  # Rails2
  # return bolded_text
  # Rails3
  return raw bolded_text
end

Use assets to serve your public images, stylesheets, javascript files

Assets are really awesome, and you should definitely use them.
More info on assets can be found here.

Although Rails3 can continue to deal with all the files stored in the public/ folder the same way Rails2 did, I strongly recommend migrating those files to the assets pipeline.

However when doing so in production mode, Rails3 alters the name of your files served as assets. When you access them from your code, you cannot hard-code them anymore ; instead you will use the asset_path helper.

# Rails2
mycss_url = "#{ENV['RAILS_RELATIVE_URL_ROOT']}/css/mystylesheet.css"
# Rails3
mycss_url = asset_path('mystylesheet.css')

Remember to check that your assets are enabled in file config/application.rb:

    # Enable the asset pipeline
    config.assets.enabled = true
    # Version of your assets, change this if you want to expire all your assets
    config.assets.version = '1.0'

No more stylesheets controllers to generate CSS files

When your application needs CSS generation, a common trick in Rails2 was to define a stylesheets controller, and make it render a CSS file like a view.

With Rails3 you don’t need that anymore: CSS files are served using assets, and this mechanism allows CSS files to be generated using lots of different engines: SASS, LESS, ERB

Writing helpers using concat does not work anymore

If you have written view helpers that concatenate data to the output (the same way form_for did in Rails2), you certainly have used the concat methods.
In Rails3 you will have to change this behavior: your helper methods have to return the HTML strings, and not bother anymore about concatenating them to the output.

# Encapsulate an inner block into a section div
def section(&iBlock)
  inner_html = capture(&iBlock)
  # Rails2
  # concat("<div class=\"section\">#{inner_html}</div>")
  # Rails3
  return raw "<div class=\"section\">#{inner_html}</div>"
end

And this change is reflected in your views that now get the string directly from the call, using <%= section do instead of <% section do:

<%= section do %>
  <p>My section content</p>
<% end %>

Changing table names for models has changed

If you wanted your model to use a specific table name in the database, you were using set_table_name. It has been replaced with self.table_name = .

class MyModel < ActiveRecord::Base
  # Don't use the default table name
  # Rails2
  # set_table_name 'my_table_for_model'
  # Rails3
  self.table_name = 'my_table_for_model'
end

Recognizing routes has changed

In Rails2, when you wanted to recognize a path (ie. translating a URL into the hash giving controller, action), you could use ActionController::Routing::Routes.recognize_path.
In Rails3, the same call is Rails.application.routes.recognize_path.

# Rails2
# url_params = ActionController::Routing::Routes.recognize_path('/path/to/resource/15/edit')
# Rails3
url_params = Rails.application.routes.recognize_path('/path/to/resource/15/edit')

Rails3 declare more MIME types

By default, Rails2 was missing some important MIME types to be declared. You had to do it by yourself.
In Rails3, common MIME types are already declared by default.

If you were declaring MIME types by yourself, you can comment out the following ones as they have been added into Rails3:

# Rails3: Already declared MIME types
#Mime::Type.register 'image/x-windows-bmp', :bmp
#Mime::Type.register 'image/gif', :gif
#Mime::Type.register 'image/jpeg', :jpeg
#Mime::Type.register 'video/mpeg', :mpeg
#Mime::Type.register 'application/pdf', :pdf
#Mime::Type.register 'image/png', :png
#Mime::Type.register 'image/x-tiff', :tiff
#Mime::Type.register 'multipart/x-zip', :zip

Accessing current controller from the environment has changed

Some plugins require that your initializers define callbacks that will need access to the current controller. This is ugly, but unfortunately sometimes there is no choice.

In Rails2, this could be done using env['action_controller.rescue.response'].template.controller.
In Rails3, you have to use env['action_controller.instance'].

Migrate to Devise gem version >= 2.0

If you were using the Devise gem, you will have to migrate it to a version >= 2.0.
This can be quite painful, all the more so as you customized it deeply.
The best way to do it is to generate from scratch new controllers and views from Devise’ generators after having updated it. More information can be found here.

Basically, here are the points to consider when migrating Devise:

  • :http_authenticatable and :activatable modules have been removed.
  • Views files have been reorganized, in a app/views/devise/ directory (devise_mailer/, confirmations/, passwords/, registrations/, sessions/, shared/ and unlocks/ directories have been moved there).
  • Locales have been slightly changed.
  • Signing out users is done using :delete HTTP method instead of :get.
  • Users updating their email does not change them in the database, but set a unconfirmed_email column instead.
  • Some changes have to be done on the database. Here is a migration file I used for this purpose. Don’t forget to adapt it to your own needs:
    class MigrateDeviseTo204 < ActiveRecord::Migration
      def self.up
        change_table(:users) do |t|
          t.datetime :reset_password_sent_at
          t.string   :unconfirmed_email # Only if using reconfirmable
        end
        remove_column :users, :remember_token
      end
    
      def self.down
        change_table(:users) do |t|
          t.string :remember_token
        end
        remove_column :users, :reset_password_sent_at
        remove_column :users, :unconfirmed_email
      end
    end
    
  • If you were using cancan with Devise, you may have to change your app/models/ability.rb file, and add devise/ to any Devise action. Here is an example:
    # User and sessions
    can [:new, :create, :edit, :update], 'devise/passwords'.to_sym
    can [:new, :create, :show], 'devise/confirmations'.to_sym
    can [:new, :create], [
      :registrations,
      'devise/sessions'.to_sym,
      User
    ]
    can :destroy, 'devise/sessions'.to_sym
    

Use Prototype library

By default, Rails2 was packaged with Prototype library.
Rails3 ships with jQuery by default. However it is very easy to add Prototype back if you need it in your application.

First, add the prototype dependency in your Gemfile:

gem 'jquery-rails'
gem 'prototype-rails'

Run bundle install to make sure it is installed.

Then add the prototype inclusions in your asset pipeline:

//= require jquery
//= require jquery_ujs
//= require prototype
//= require_tree .

Method link_to_function from JavaScriptHelper now takes 2 mandatory arguments instead of 1, and does not accept blocks anymore.
The solution is to not use it anymore, as it is going to be deprecated.
Here is a usage example:

# Rails 2
link_to_function('link text') do |page|
  page << 'alert(\'Hello\');'
end

# Rails 3
content_tag(:a, 'link text',
  :href => '#',
  : onclick => 'alert(\'Hello\'); return false;')

Models’ attributes can’t be mass assigned anymore by default

When your models are being saved using update in your controllers, all the attributes given in the hash will be updated.
In Rails3 this is not possible anymore for security reasons: you have to declare in your Models which attributes can be updated, using the attr_accessible method:

class User < ActiveRecord::Base

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :name

end

Form error messages don’t work anymore

In Rails2, you could call the method error_messages in your forms to display error messages. This method does not exist anymore.

<%= form_for(@message) do |f| %>
  <%= f.error_messages %>

  <% ... %>

<% end %>

Simplest way is to add it back, which can be done using the dynamic_form gem.

# ...

gem 'dynamic_form'

# ...

The Mailer interface has changed

Here are the differences:

  • Mailers are defined in the app/mailers directory instead of app/models.
  • Variables accessible in Mailer views are directly set using @my_variable instead of @body['my_variable'].
  • Sending mails is done using MyMailer.my_mail_method(my_args).deliver instead of MyMailer.deliver_my_mail_method(my_args).

That’s all for what I have found so far.
I will update this list as I find more migration steps.

About Muriel Salvan

I am a freelance project manager and polyglot developer, expert in Ruby and Rails. I created X-Aeon Solutions and rivierarb Ruby meetups. I also give trainings and conferences on technical topics. My core development principles: Plugins-oriented architectures, simple components, Open Source power, clever automation, constant technology watch, quality and optimized code. My experience includes big and small companies. I embrace agile methodologies and test driven development, without giving up on planning and risks containment methods as well. I love Open Source and became a big advocate.
Howto, Ruby on Rails, Web development , , , , , , , , , , , , ,

1 comment


  1. James

    Muriel, if you’ll need to localize a Rail app for international markets, you can choose a collaborative translation management platform, and I recommend to go for POEditor, because it has a simple and neat work interface. But before that, you can convert yaml to po files (not supported by POEditor), by using this free converter tool http://yml2po.com/. It worked for me, maybe it can help you too.

Leave a Reply

Your email address will not be published. Required fields are marked *