Case Study: Upgrade to Rails 4.1
This article is not about what is in Rails 4.1. It is about what I had to do to get a (albeit simple) production app using 3.2.18 to 4.1. If you want a “What’s New in Rails 4/4.1” article, the internet is rife with them, including a great one by our own Michael Olah.
Today, I am going to enumerate my steps to get a Rails 3.2.18 app upgraded to Rails 4.1, using any new Rails conventions when possible. I hope my experience injects a little reality into your should-I-upgrade-right-now-or-will-I-end-up-in-hell decisions.
As I mentioned, the application I am upgrading is currently on Ruby 2.0 and Rails 3.2.18. It is a relatively vanilla app, using Devise for authentication and RSpec and Guard for testing. The database is PostgreSQL and it is deployed to Heroku.
Let’s get to it, shall we?
The first thing to do is make a decision about Ruby versions. I figure that, while I am going to Rails 4.1, I might as well go to Ruby 2.1 too. There have been some issues around Ruby 2.1, some of which have been addressed in 2.1.2. However, there are still some bad things in 2.1.2, so know what you’re getting into, K?
Also, if you’re using RVM and/or Pow like me, be sure to change any
.powrc files. Pow simply times out after you forget to change
.powrc after specifying a new version, which burned up about 30 of my Earth minutes.
I changed the lines
rails '3.2.18' ruby '2.0.0'
rails '4.1.0' ruby '2.1.2'
Now for the new gems. Much of the ado around 4.1 centers on Spring, a shiny application preloader that is aimed at removing Rails start-up time from our collective craw. As with anything associated with Rails, there is some community consternation about adding Spring to the default Rails conventions. For one, it doesn’t work on a couple of big-time implementations, namely JRuby or Rubinius. Nor does it work on any Windows platform. When I first read that, I agreed with the naysayers. Why add something that is non-functional for so many users?
Anyhoo, I figure I’ll try it out, see if it’s worth the hubbub. If not, I’ll write a post condemning it as pure evil. Or something.
gem 'spring' group: :development to the Gemfile.
Rails 4.0 added Strong Parameters, which is an approach to whitelisting attributes the application can pass through to your model. I have been using a similar approach by simple
slice-ing the parameters in the controller as needed. This means I don’t have to use the strong parameters approach to get up and running, but can work toward it over time.
However, since I am not fixing all my models/controllers for strong parameters right now, I have to use some duct tape in the form of the protected_attributes gem.
My Gemfile includes a couple of gems (activeuuid and uuidtools) used to add UUID support and utilities to my application. Rails 4.0 improved UUID support, so I don’t need these anymore. If you need a uuid value in your app somewhere, you can now use
SecureRandom.uuid. I also had to remove
include ActiveUUID::UUID from any model using uuids, now that activeuuid is gone.
The Gemfile is still using a
:assets group, which was removed by Rails 4.0, so back that
Finally, I have been a long time fan of the mailcatcher gem, but Rails 4.1 adds ActionMailer Preview to the core offering. As such, I am going to remove mailcatcher and role with the new stuff. I understand that mailcatcher does a bit more than ActionMailer Preview, but less gems is better (except for when it isn’t).
OK, all Gemfile changes done. Time to
bundle update and all should be well.
There are some necessary changes to the application environment files. First, if you get an exception about removing
config.active_record.whitelist_attributes when you try to start the server/console, then you need to go back to the Gemfile section and add protected_attributes. Also, shame on you for making me repeat myself.
After my successful
bundle, I saw some warnings about
config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly: * development - set it to false * test - set it to false (unless you use a tool that preloads your test environment) * production - set it to true
So, update those files accordingly. Note: If you have a “staging” environment defined like I do, be sure to make the appropriate changes to that file as well.
While reading the Rails upgrade guide, it mentions a new cookie serializer. Per the guide, I added
config.action_dispatch.cookies_serializer = :hybrid
to config/application.rb. This is another READ THE GUIDE moment. Blindly changing your cookie serializer can make your cookies crumble, so know why you’re doing it.
match keyword for Rails routing is now obsolete. If you have
match routes in your config/routes.rb file, you’ll see neat things like:
/Users/dood/.rvm/gems/ruby-2.1.2@app/gems/actionpack-4.1.0/lib/action_dispatch/routing/mapper.rb:196:in `normalize_conditions!': You should not use the `match` method in your router without specifying an HTTP method. If you want to expose your action to both GET and POST, add `via: [:get, :post]` option. If you want to expose your action to GET, use `get` in the router: Instead of: match "controller#action" Do: get "controller#action" (RuntimeError)
I have a (shockingly) large number of
match routes, which are all now
I think the new approach to application secrets deserves its own section. The Rails guide section on the subject is worth reading.
First, create a
config/secrets.yml file with the following content:
development: secret_key_base: <output of rake secret> test: secret_key_base: <output of rake secret> production: secret_key_base: <output of rake secret>
With my new secrets file in place, it’s time to delete
config/initializers/secret_token.rb. If I did this right, my cookies will be upgraded to the new, more secure cookies. Again, make sure you understand what is happening here, because there is no going back. You may need to be sure that all your users have the new cookies before upgrading, and the upgrade guide tells you how to support both as needed.
My application is currently using the settingslogic gem to hold my application settings, which I have liked well enough to use on several apps. Some people swear by figaro, and I can see why. In either case, settingslogic adds just enough for me to want to keep it. As such, my secrets file will only hold the
At this point, I seem to have a Rails 4.1 application that is ready to roll. To be honest, I thought this was going to be more of a deal.
I did remove the vendor/plugins directory. I’ve been getting a warning about plugins not being supported for what seems like several lifetimes. It’s gone now, along with another small bit of Rails annoyance.
Since I added spring to the Gemfile, I need to create the binstubs in order to make Spring happy.
spring binstub --all
which spits out:
* bin/rake: generated with spring * bin/rails: spring inserted
I am deploying to Heroku, so it should be a snap, right? However, when Rails 4.0 came out, I attempted to deploy a Rails 4 app to Heroku and the asset pipeline had other thoughts. The following image is how I felt trying to get it to work:
Thankfully, time has done what time will do and things are much improved. This article covers the changes I made. Basically, I added the rails_12factor gem to my bundle and then made sure this was in my config/environments/staging.rb file.
config.serve_static_assets = true
I think doing both is overkill, but it worked so I am backing away slowly. If your assets (including the application.css and application.js) are 404ing, what I mention here should fix it.
After the upgrade, Rails is still Rails. If you remember, I mentioned I had a bit of angst about the whole Spring thing, so let’s fire up a rails console and see if I notice any change in the start-up time.
(5 seconds-ish later)
Nope, seems the same. I’ll just close the console and open another one.
That can’t be right. I have never seen a Rails console start that fast.
(Does it again, same result.)
(Feels lighter, but slightly dirty, as all convictions about Spring not working for everyone leaves body and mind.)
Wow. Spring makes a HUGE difference. I can’t WAIT to see what it does for RSpec.
(Runs specs several times)
Hmph. It doesn’t seem to do anything for RSpec. I have restarted guard, but the specs have the same start-up time.
(Seeks counsel from Google, discovers answer)
OK, there is a spring_commands_rspec that adds the necessary binstub. I
bundle in that gem, then
spring binstub --rspec to add the binstub, followed by
spring stop to make sure Spring stops to smell the new binstub. Unfortunately, we’re still not done. As I mentioned at the beginning of the article, I use Guard. For some reason, the generated
rspec binstub isn’t picked up by Guard (or anything, it seems) so you have to modify your
Guardfile. I changed
guard :rspec do
guard :rspec, cmd: 'bin/rspec' do
guard. KABLAMMEY! My specs now start before key-up on every save.
I get Spring. Sorry JRuby (and other) users. If it makes you feel any better, a couple of my much larger Rails apps are JRuby, so I feel your pain.
Well, my app is upgraded to Rails 4.1 and will start its life in staging this week. That’s great. The upgrade was painless, for the most part, and the gains are tangible. Just like I like it.
Next steps consist of seeing what other new bits are of interest to my app’s code base. Things like:
- Enums: I don’t see much value here. Enums only make sense to me if you have a data type that makes sense as an integer, like a flag. Otherwise, when you’re looking at data in the database, you have to remember what number maps to what value. I probably just don’t get it.
- Message Verifiers: Our Devise mailers do some of this with forgot passwords and registration confirmations. I wonder if the Plataformatec kids are going to use this?
- Concerning: This is a “low ceremony way to separate bite-sized concerns.” It’s interesting, but not for this app. Maybe some of my behemoths.
- Variants: This app is not responsive, outside of what Foundation has given, which we did a poor job implementing. We need to do some work in this area, so I’ll bet we look here soon.
- New Test Methods: Some of the new test methods look neat and may allow us to dump more gems. Gem dumping, FTW!
Have you upgraded to Rails 4.1 yet? Impressions? Thoughts? Pain? Share your experience!