Upgrade Rails 3 to 3.1

This is a comprehensive guide to upgrading a Ruby on Rails 3 application to Rails 3.1. It also includes instructions for updating the compass gem. Lets get started!

Gemfile

# gemfile
gem 'rails', '3.1.0'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails', "  ~> 3.1.0"
  gem 'coffee-rails', "~> 3.1.0"
  gem 'uglifier'
end

gem 'jquery-rails'

# If you use compass:
  # gem 'compass', git: 'https://github.com/chriseppstein/compass.git', branch: 'rails31'

# If you use mysql, make sure it's above version 3:
  # gem 'mysql2', '> 0.3'

# terminal
$ bundle update
$ bundle install

Auto vs Manual

If you like you can do an auto upgrade, where you type in the console “bundle exec rake rails:update” and then it will go through each new file with you (press ‘d’ to see the differences between yours and the new file).

However if you are worried about over-writing your code you can follow along below. I would read on anyway in case the auto method misses something.

Add missing file wrap parameters

# terminal
$ touch config/initializers/wrap_parameters.rb

# insert following code into config/initializers/wrap_parameters.rb:

  # Be sure to restart your server when you modify this file.
  #
  # This file contains settings for ActionController::ParamsWrapper which
  # is enabled by default.

  # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
  ActiveSupport.on_load(:action_controller) do
    wrap_parameters format: [:json]
  end

  # Disable root element in JSON by default.
  ActiveSupport.on_load(:active_record) do
    self.include_root_in_json = false
  end

Update environment files with assets stuff

# config/application.rb
# 1 - replace this:

  # If you have a Gemfile, require the gems listed there, including any gems
  # you've limited to :test, :development, or :production.
  Bundler.require(:default, Rails.env) if defined?(Bundler)

# 1 - with this:

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

# 2 - then add these in (underneath class Application < Rails::Application)
  # 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'

# config/environments/development.rb
  # Do not compress assets
  config.assets.compress = false

  # Expands the lines which load the assets
  config.assets.debug = true

# also while in development.rb, REMOVE this line if it's there:
  config.action_view.debug_rjs = true

# config/environments/production.rb
  # Compress JavaScripts and CSS
  config.assets.compress = true

  # Don't fallback to assets pipeline if a precompiled asset is missed
  config.assets.compile = false

  # Generate digests for assets URLs
  config.assets.digest = true

  # Defaults to Rails.root.join("public/assets")
  # config.assets.manifest = YOUR_PATH

  config.assets.js_compressor  = :uglifier
  config.assets.css_compressor = :scss

# config/environments/test.rb
  # Configure static asset server for tests with Cache-Control for performance
  config.serve_static_assets = true
  config.static_cache_control = "public, max-age=3600"

Public Directory

Here there should be no folders at all. We instead put them into assets. We also should create the other assets locations in lib and vendor:

# terminal
$ mkdir app/assets
$ git mv public/images app/assets/images
$ git mv public/javascripts app/assets/javascripts
$ git mv public/stylesheets app/assets/stylesheets
$ mkdir vendor/assets
$ mkdir lib/assets

Note: If you have .sass or .scss files you will have to rename them to .css.sass or .css.scss

Application.js

In the javascripts folder you should have an application.js file.
In this file put the following text up the top (note that you don’t have to uncomment the require stuff – “//=” will work fine):

// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery_ujs
//= require_tree .

“require_tree” means include every single js file within a folder – and “.” refers to the current folder. So it’s just saying include everything in assets/javascripts.

If you don’t want to include every js file in every page, you could create a new folder, say “assets/javascripts/layout”, put in any js files that you want in every single page, then in your application.js call “//= require_tree layout/”.

Also note that each controller has its own js file (eg. for the controller “posts” I will get a js file “posts.js.coffee”, and in that file I could go “//= require_directory posts/” and then put all my js I need for the Posts controller in assets/javascripts/posts/.

Finally you could ignore directories and trees and just call certain files with “//= require posts” => loads posts.js

Now we do the same thing but for the stylesheets/application.css

/*
 * This is a manifest file that'll automatically include all the stylesheets available in this directory
 * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
 * the top of the compiled file, but it's generally better to create a new file per style scope.
 *= require_self
 *= require_tree .
*/

Layout

This is what should be in your layout file. Note that this will include every single javascript and stylesheet file in your assets folders. Rails sticks them all together into one big file so there are less requests to the server and hence faster loading times.

ERB
  <%= stylesheet_link_tag    "application" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>

HAML:
  = csrf_meta_tag
  = stylesheet_link_tag "application"
  = javascript_include_tag "application"

Other little tweaks

Add these to your .gitignore file:

.sass-cache/
public/assets/

Stylesheets: Images and Manifest files

Rails provides these helpers, which will find your images:

# .erb
asset_path 'asset_name'

# .sass OR .scss (without .erb on the end)
image-path("rails.png") becomes "/assets/rails.png"
asset-url("rails.png", image) becomes url(/assets/rails.png)

# eg for .sass or .scss file:
.button.checkable{
  background-image: image-path("tick.png");
}

Manifest files are the ones that tell Sprockets which files to concatenate into one big file. I had an issue with referencing folders in the vendor/assets directory. For example, I didn’t want to load every single css file (“require_tree .”), but instead wanted to load every css file in a folder situated in my vendor directory; “vendor/assets/stylesheets/layout/”. If in my application.css file I went “require_tree ./vendor” it couldn’t find it, and I didn’t really want to have to write “require_tree ../../../….blah blah”.

Anyway it seems the best solution I found was to create a manifest file in vendor (say “vendor.css”, and in that file I could go “require_tree ./vendor”. Then in application.css I would reference the vendor file “require vendor”. Comment if you have a better solution.

Optional: Getting Compass to work

I had the issue that my stylesheets were in app/stylesheets and needed to be app/assets/stylesheets. Here are the steps I took:

- went to initializers/compass.rb and commented out any path references

#css_dir = ‘public/stylesheets’
#sass_dir = ‘app/stylesheets’

- moved everything in app/stylesheets to app/assets/stylesheets
- deleted what was had been compiled by sass in public (screen.css, print.css, ie.css)
- renamed all my .scss files to .css.scss
- moved all my plugin and random stylesheets I never edit from app/assets/stylesheets to vendor/assets/stylesheets
- created/renamed application.css.scss in app/assets/stylesheets
- copied everything in screen.css.scss to application.css.scss
- in application.css.scss.erb I did a find for “url” and replaced the url’s with asset_path ‘asset_name’ (eg url(‘../images/top_x.jpg’) => url( asset_path ‘top_x’)
- also in application.css.scss.erb I deleted the “require_tree .” because this was loading my partials again (they are already included in my application.css.scss.erb)

Note: if you want an all-in-one resource to become an expert in ruby on rails 3, don’t bother with textbooks – just get this video tutorial series (highly recommended): Ruby on Rails Tutorial

Tagged: , ,

About the Author

Plattsi | Other Articles

A twenty something web developer and entrepreneur from Sydney, Australia. Loves building web applications that are both easy and fun to use (and don't require manuals).

  • Ben

    Great article, although that moving share button is evil.

  • http://webtempest.com Web Tempest

    Yeah going to have to change it. So hard to find a good WP share plugin

  • Ali Kapadia

    there seems to be something wrong with the line you’ve entered:

    wrap_parameters format: [:json]

  • Ali Kapadia

    i get the error whenever i start the server: 

    unexpected ‘:’, expecting kEND (SyntaxError)  wrap_parameters format: [:json]

    pointing to the ‘:’ at the end of format:

  • Danijel

    I see the same error, but I generated new rails 3.1 application. Is it rails 3.1 bug?
    Error occurs when using ruby 1.8.7p253, but not with ruby 1.9.2p290.Perhaps this line should be:wrap_parameters :format => [:json]According to the line above that states:# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.Seems that this fix works…

  • http://webtempest.com Web Tempest

    As Danijel points out, I’m using ruby 1.9 syntax for hashes. I wouldn’t create a rails 3 app in ruby 1.8 – the ruby creators are not supporting it anymore and want everyone to use 1.9