Jens Krämer

Rails 5 Upgrade Field Notes

 |  ruby, rails, upgrade

Here’s what I had to do to bring Bold up to speed with Rails 5. It’s a relatively new code base that was Rails 4.2 from the ground up, so this was fairly straightforward.

Besides the official upgrade guide, I found this list of changes quite informative.

In preparation of the upgrade make sure you run a recent Ruby (Rails 5 requires Ruby 2.2.2 or greater - this is a good moment to upgrade to 2.3 right away), and that you have a decent test suite which obviously should pass before you do the upgrade.

Next change your Gemfile to point to Rails 5 (at the time of this writing: gem 'rails', '5.0.0.beta2'), and wrangle with Bundler until all dependencies are resolved. You will probably have to update a few other dependencies as well to their Rails 5 compatible versions.

Once Bundler is satisfied, run rake rails:update. This will update or add some files in config/, and almost certainly will prompt you about conflicts if such files were modified by you. A good strategy is to let the rake task overwrite all conflicting files and then go through the output of git diff, taking your customizations back in as necessary. I didn’t notice any breaking changes here so I’ll skip over that.

Next, fire up your tests using the shiny new bin/rails t command which can take a test directory or file, the latter even including an optional line number for running just a specific test method. One of the best additions to Rails 5 imho. Here is some more info about the new Rails 5 test runner.

In my case that didn’t go very far however, turns out I had some more libraries to upgrade which weren’t Rails 5 ready but didn’t say so in their Gemspec.

Libraries to Be Upgraded

Obviously, depending on the nature of your app, you may have similar or completely different problems. This is just what occured to me during this upgrade.

SimpleForm

Upgraded to 3.2.1, I was using 3.1 before and basically the only relevant change was Rails 5 compatibility.

Draper

There’s a branch with some Rails 5 fixes. It’s also necessary to add the activemodel-serializers-gem:

gem 'draper', github: 'janraasch/draper', branch: 'rails5-compatibility'
gem 'activemodel-serializers-xml', github: 'rails/activemodel-serializers-xml'

Devise, Devise Invitable Extension

The invitable extension threw an undefined method 'hide_action' at load time, so in order to make any progress I upgraded Devise to 4.0.0.rc1 and the invitable extension to it’s current master.

Kaminari

The latest released version of Kaminari (0.16.3) suffers from the ActionController::Parameters refactoring (see below), throwing this error. Point your Gemfile to the master branch at github and you’re good.

Code Changes

With tests running now, lets fix the plethore of deprecation warnings and the few (in my case at least, ymmv…) errors that occured. Actually the only change that broke things in Bold was the changed ActionController::Parameters API. Any other changes described below were just to get rid of deprecation warnings.

ActionController::Parameters no longer inherits from hash

Any test failures I encountered had their cause here. There also was a warning in the logs when doing something like this to allow a sub-hash of params to have any keys:

def some_action
  # @model.config is a postgresql hstore column (think serialized hash)
  @model.config.update model_params[:config]
end

private
def model_params
  if params[:model]
    parms[:model].permit config: params[:model][:config].try(:keys)
  else
    { foo: {} }
  end
end

Anywhere you’re using params as if it were a Hash right now you’ll have to do as the message says and check the API for the new behavior of params. If you only use the standard permit calls with a hard coded set of parameter names you should not see any problems at all, and I also found a nice solution for my special case:

def some_action
  # @model.config is a postgresql hstore column (think serialized hash)
  @model.config.update model_config
  # more code left out for brevity
end

private
def model_config
  if params[:model]
    parms[:model].permit(:config).to_unsafe_hash
  else
    { }
  end
end

I really like the way this makes my code shorter and at the same time tells more clearly what’s going on.

Controller Filters Are Now Actions

Most of your before_filter methods don’t filter anything at all anyway, so this renaming spree might be annoying but it serves a greater good which is the readability of your code.

assigns and assert_template No More

Those fell out of fashion some time ago.

Personally I don’t totally buy into that yet, and certainly won’t change all my controller tests at once. For the time being adding the rails-controller-testing gem to the :test group of Gemfile will bring those methods back and all is good.

alias_method_chain

Simply don’t use it any more.

Instead use Module#prepend and just call super when you override a method and want to call it’s original implementation. You can also implement self.prepended(base) where needed to replace any included blocks in your concerns. Look here for a nice introduction if you don’t know what I’m talking about. For the sake of consistency I’m also going to replace any other uses of ActiveSupport::Concern with this pattern, since I feel that the only benefit of ASConcern, dependency resolution among concerns, doesn’t outweigh the fact that to my knowledge ASConcern still only gives you classic include and no prepend.

keyword arguments in controller tests

Vim macros saved my day here, because every single call of the get, post, put etc. methods that passes any parameters has to be changed to use the params: keyword to pass these. There’s also xhr and session keywords, the former replacing the always awkward-feeling xhr :get style calls, the latter receives an optional session hash.

get :show, { id: obj.to_param }, { session_var: 'baz' }
xhr :patch, :update, id: obj.to_param, something: { foo: 'bar' }
get :show, params: { id: obj.to_param }, session: { session_var: 'baz' }
patch :update, xhr: true, params: { id: obj.to_param, something: { foo: 'bar' } }

While slightly more verbose, it’s still much nicer this way. It shows especially when there are no params but you want to set some session state, for which previously you had to give an empty hash as params before the hash holding the session values. Now you can just leave out the params argument completely.

Rails5 branch build status

After all of this, all tests are green and Bold is down to 4 or so deprecation warnings where the source is a bit harder to determine (the message says it’s Ruby’s tsort.rb but I doubt that).