Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
Rails 4 Upgrade

Co Author: Andy Pliszka

Recently we updated Project Monitor, an open source project maintained by Pivotal Labs, to run on Rails 4. It was a fairly painless process. Most of the changes were minor, relating to gem versions, environment configuration, and small Rails API changes (e.g. scopes).

The best strategy is to divide and conquer: split the migration into small manageable parts and take advantage of your test suite. Get your Rails server running and then focus on getting to green.

Below you’ll find a technical description of our Rails 4 migration with Project Monitor.

Updating Gems

Migration of a Rails 3 app to Rails 4 requires gem updates. Although most of the gems worked well after updating the Rails gem, we had to change the following gems:

Delayed Job

We were able to get Delayed Job working with Rails 4 after updating to beta releases of dealyed_job and delyed_job_active_record gems.

gem "delayed_job", "~> 4.0.0.beta2"
gem "delayed_job_active_record", "~> 4.0.0.beta3"

Sass

If you are using Sass and Compass for your CSS then those gems also require some special attention. We had to update sass gem to version 4 and use a GitHub reference for compass-gem gem.

gem 'sass-rails', '~> 4.0.0'
gem "compass-rails", github: "milgner/compass-rails", ref: "1749c06f15dc4b058427e7969810457213647fb8"

Capybara-webkit

During our migration to Rails 4 we heavily relied on our test suite. We experienced some unexpected behavior when running integration specs. There were multiple issues such as brittle tests and hanging tests. The main reason for all of these issues was the capybara-webkit gem. For the integration tests to work properly with Rails 4 we had to update capybara-webkit gem.

gem "capybara-webkit", github: "thoughtbot/capybara-webkit", branch: "1.0"

Jasmine

Project Monitor has an extensive Jasmine test suite. There was a problem with serving of Backbone JavaScript files when running the Jasmine test from command line as well as from the browser. The solution was to update jasmine gem.

gem "jasmine", github: "pivotal/jasmine-gem", ref: "e8105401b6ed9d4b462bbbaf508ab7ac8a77a245"

Replacing Gems

Since Project Monitor is an old project, we had some older gems. One such gem we were using was coffee_filter which is used for embedding CoffeeScript code inside of our Haml templates. We were able to replace it with the coffee-rails gem.

gem 'coffee-rails', '~> 4.0.0'

Haml

We also replaced haml gem with haml-rails

gem "haml-rails"

New Heroku gem

Since at Pivotal Labs we deploy Project Monitor on Heroku, we added rails_12factor gem to our Gemfile but only to production group. rails_12factor is a gem written by the Heroku team to enable features such as static asset serving and logging.

gem 'rails_12factor', group: [:production]

Removing assets group from Gemfile

Rails 4 updates assets pipeline and the assets Gemfile group is no longer necessary. Therefore, we moved gems related to assets outside of the assets group.

Configuration updates

It was very easy to update our config files for Rails 4. To get your app started all we had to change was the Bundler API call. This allowed us to start our app with some deprecation warnings, that clearly explained what we needed to do to remove them.

#./config/application.rb

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

require 'rails/all'

if defined?(Bundler)
  Bundler.require(:default, :assets, Rails.env)
end
Bundler.require(:default, Rails.env)

module Project Monitor
  class Application &ltRails::Application
    config.from_file 'settings.yml'

    config.autoload_paths += %W(#{config.root}/lib #{config.root}/lib/payload)

    config.encoding = "utf-8"
    config.i18n.fallbacks = true

    config.serve_static_assets = true

    config.assets.compile = true
    config.assets.enabled = true
    config.assets.version = '1.1'
    config.assets.initialize_on_precompile = false
    config.assets.paths <<
    Rails.root.join('app','assets','skins')

    config.action_mailer.default_url_options = Rails.configuration.emailer_host.to_hash.symbolize_keys

    config.secret_key_base = 'project_monitor'

    config.cache_store = :memory_store
    config.static_cache_control = "public, max-age=3600"

    config.filter_parameters += [:password]
  end
end

 

Rails 4 adds eager loading of code during app boot to improve performance. Therefore, we added config.eager_load to out ./config/environments/production.rb file. The eager loading of code should be disabled in test environment.

#./config/environments/production.rb

Project Monitor::Application.configure do
  config.cache_classes = true

  config.eager_load = true

  config.consider_all_requests_local = false
  config.action_controller.perform_caching = true

  config.active_support.deprecation = :notify

  config.assets.compress = true
  config.assets.digest = true

  config.cache_store = :mem_cache_store

  config.log_formatter = ::Logger::Formatter.new
  config.log_level = :info
end

Routes

Rails 4 does not support the #match method and we had to specify either get or post for each route.

match 'builds' => "home#builds” #old
[get|post] 'builds' => "home#builds” #new

Scopes

Rails 4 depreciates passing blocks to scope methods if favor of lambdas. This solves a lot of issues that arise when performing queries using Time. For example:

scope :recent, { where(“time > ?”, 5.days.ago) }
scope :recent, -> { where(“time > ?”, 5.days.ago) }

The default_scope method now takes a block instead of a symbol.

deafult_scope :reverse_order #old
default_scope { :reverse_order } #new 

Rails 4 is moving away from the with_scope method and now using #scoping which is chained to a query. This can also be seen when performing calculations (e.g. average). The main motivation for this change is to reuse where syntax.

with_scope(find: where(aggregate_project_id: aggregate_project_id), &block) #old
where(aggregate_project_id: aggregate_project_id).scoping(&block) #new 

Strong Parameters

Adding strong_parameters to controllers can be a pain, but it was straightforward and simple. Although you can add the attributes_protected gem, we found conflicts with this gem and other gems that have been modified for Rails 4 and strong_parameters. It was simpler to switch to strong_parameters then to try to get the attributes_protected gem to work.

JSON

We had to make sure that our #as_json returns root object. This can be either by add explicit root: true to #as_json method call or global setting in Rails config. So make sure you test your JSON responses if the are still valid.

# To enable root element in JSON for ActiveRecord objects.
ActiveSupport.on_load(:active_record) do
  self.include_root_in_json = true
end

Act as taggable Heroku hack

For some reason when Heroku runs rake assets:precompile it does not give your app access to the database which raises an error in the acts-as-taggable-on gem which looks for the database. This seems like it is more a bug with Heroku then with acts-as-taggable-on. Other gems may be affected by this and we could imagine using this same hack to get around the exceptions.

act_as_taggable raise nil

Conclusion

In summary, the migration to Rails 4 was not very painful. The most challenging was determining which gems needed to be updated and which are no longer compatible. Having a comprehensive test suite was very helpful during the migration. It is worth noting that you should upgrade Capybara before running your suite, we spent some time trying to fix brittle tests that disappeared once we updated this gem.

Updated

Jasmine is currently being updated, and you now need to reference e8105401b6ed9d4b462bbbaf508ab7ac8a77a245 when updating to Rails 4.

Comments
  1. Alex Kwiatkowski says:

    Heroku does not give you access to the application config vars during deployment by default. This is why you cannot connect to the database for act_as_taggable. There is a Heroku labs feature you can turn on to enable this https://devcenter.heroku.com/articles/labs-user-env-compile

  2. Draco Li says:

    thx this is really useful

  3. Bill Horsman says:

    Thanks, that was useful. For the acts_as_taggable workaround, did you mean:

    act_as_taggable rescue nil

    “rescue” not “raise”?

  4. Ratnakar says:

    I am getting error when config.eager_load = true in produciton.rb , not sure why it fails to start server in production mode , Only I could able to run server in production mode successfully when I comment both config.eager_load = true and config.cache_classes = true , Can please let me know , the following is the error when I try to run the server in production mode without commenting the above lines

    home/.rvm/gems/ruby-2.1.5@glimpse/gems/actionview-4.2.0/lib/action_view/helpers/debug_helper.rb:25:in `debug': wrong number of arguments (0 for 1) (ArgumentError)

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *