Upgrading Your Mind to Rails 4

rails4

If you’ve spent any time doing Rails, you’ve undoubtably had a moment where you were doing project work in a specific version and upgrading to the latest and greatest was out of the question. This happened to me while working on a multitude of (at the time) greenfield projects that started life in the days of Rails 3. When Rails 4 shipped, the team made a decision NOT to upgrade due to the lack of value provided by the upgrade.

I’ve recently had exposure to a Rails 4.1 project and would like to share my experiences on upgrading my mind to think in Rails 4 conventions.

Setup

Starting up with Rails 4 is as simple ever. Let’s see what happens when a new project is generated.

$ rails new hello_rails4

Gemfile

The generated Gemfile is a lot cleaner than what you were used to with older versions of Rails. Gone are the assets group and other stuff that made it feel cluttered.

Spring

One interesting addition is this line:

# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/jonleighton/spring
gem 'spring', group: :development

The spring gem really does speed up development. It loads the Rails environment only once when running rake tasks, for example, that used to take forever. I’ve been using the zeus gem for this, but the spring gem is built-in and feels much more lightweight.

To set it up, all you have to do is run this command

$ bundle exec spring binstub --all

and run your commands from the ./bin directory. If you’re like me and tire of specifying “./bin” for these commands, you can install direnv (via homebrew if you’re on a Mac) and add this line to a .envrc file in the project.

export PATH=$PWD/bin:$PATH

Now, you can try running rake. The first time you have to wait as usual, but subsequent calls are instantaneous.

If you use rspec and cucumber for your tests, you’ll also want to add spring-commands-rspec and spring-commands-cucumber to your Gemfile.

gem "spring-commands-rspec"
gem "spring-commands-cucumber"

You’ll need to rebuild your binstubs for it to take effect.

$ bundle exec spring binstub --all

When you do, you’ll experience the same speed-up running your tests as you do with running other Rails tasks.

Turbolinks

If you like a good controversy, Turbolinks is one one of the most debated features to hit Rails since the asset pipeline. Just like the asset pipeline, turning it off is no big deal, so the controversy is (again) unfounded.

If you decide to use turbolinks, you may run into the some gotchas. I know I did and it took me a while to figure them out.

jQuery DOM loaded

When using turbolinks, you don’t always get the jQuery “DOM loaded” event when the page changes. Remember, the page is loaded only once and subsequent requests only refresh the page content.

Most apps use this event to initialize their javascript handlers. You tend to see stuff like, for example, initializing a fancybox.

coffeescript
jQuery -> $('.fancybox').fancybox()

To fix this, you need to bind to two events. The original DOM loaded event and a new page:load event. If you initialize this way, most javascript should work fine.

coffeescript
$(document).on 'ready page:load', -> $('.fancybox').fancybox()

Issues

When you run into issues with Rails 4.1, it’s usually with 3rd party libraries and gems that haven’t been updated and presume a clean slate everytime a page is shown.

I’ve had a problem with the recaptcha gem where the captcha would load fine when the page was refreshed, but did not display at all when a turbolink to the page was clicked. Often, in cases like this, you’ll find an issue with the authors of the library discussing options for using it with turbolinks. In this particular case, the form helper has to have ajax enabled:

ruby
recaptcha_tags ajax: true

Turning It Off

When you encounter a problem for which there is no easy fix, there is always the option to turn turbolinks off for a single page. Simple add a data-no-turbolink attribute to the link. The page will now load via a standard request.

To disable turbolinks completely you have to:

  • remove gem 'turbolinks' from you Gemfile
  • remove turbolinks_track from you javascript and stylesheet tags in your layout
  • remove //= require turbolinks from application.js

Rails 4 Configuration and Heroku

In Rails 3, there are a lot of ways to perform application configuration. From the simple-but-broken

if Rails.env.production
  Braintree::Configuration.environment = :production
  Braintree::Configuration.merchant_id = "merchant_id"
  Braintree::Configuration.public_key = "public_key"
  Braintree::Configuration.private_key = "private_key"
else
  Braintree::Configuration.environment = :sandbox
  Braintree::Configuration.merchant_id = "sandbox_merchant_id"
  Braintree::Configuration.public_key = "sandbox_public_key"
  Braintree::Configuration.private_key = "sandbox_private_key"
end

to loading application configuration files yourself, like Ryan Bates descibes in his railscast #85 YAML Configuration.

None of these ever seemed appropriate, especially when Heroku is your deployment platform. Heroku’s use of environment variables is a good design decision, but we’re Rails developers and we’re lazy. The “good” kind of lazy, so we’ve settled on using the figaro gem.

I’m not going to go deep into how the gem works (the docs for it are great). Suffice it to say that the figaro gem provides best of both worlds:

  • Simplicity to have the configuration in files you control and not have them checked inside source control. We’re lazy so no more fiddling with loading environment variables is a good thing.
  • Power to deploy and have real environment variables kick in on Heroku.

I could have continued using figaro with Rails 4, but since I came across this article by the author of the figaro gem, I’ve wanted to use the built-in functionality in Rails 4.1 – secrets.yml.

Not having to install yet another gem to handle something the framework should have included from the start is always a win in my book. To be fair, the built-in functionality is not the most feature rich. If you’re doing manual configuration of app servers, copying secrets.yml files may be an option, but if you want to deploy to Heroku, you have a problem since Heroku uses environment variables.

Hopefully the environment variable issue will get addressed sometime in the near future. In the meantime, I’m going to be adding this lightweight extension for the secrets.yml feature in Rails 4.1:

gem 'heroku_secrets', github: 'alexpeattie/heroku_secrets'

To use it, create a config/secrets.yml file. Notice you can use anchor and reference syntax to DRY it up a little.

default: &default
  recaptcha_public_key: ""
  recaptcha_private_key: ""

development:
  <<: *default
  secret_key_base: ''

test:
  <<: *default
  secret_key_base: ''

production:
  <<: *default
  secret_key_base: ''
  google_analytics_code: ''

  smtp_username: 'admin@example.com'
  smtp_password: 'xxx'
  smtp_domain: 'example.com'
  smtp_address: 'smtp.example.com'

You can have the gem copy your production configuration to Heroku via a rake task and automatically pick up the variables:

$ rake heroku:secrets[heroku_app_name] RAILS_ENV=production

And that’s it! You now have your app configured and can use Rails to do your configuration for you

Braintree::Configuration.environment = Rails.application.secrets.braintree_environment
Braintree::Configuration.merchant_id = Rails.application.secrets.braintree_merchant_id
Braintree::Configuration.public_key = Rails.application.secrets.braintree_public_key
Braintree::Configuration.private_key = Rails.application.secrets.private_key

Don’t forget to add this file to your .gitignore file, as Rails isn’t doing that by default yet.

Conclusion

Rails 4 is a much easier upgrade that its predecessor. Most of the changes are in the form of new features that (if used properly) enhance your life as a developer. Opting out is easier than ever if something isn’t working for your particular situation.

This article sums up what I went through while trying to upgrade my thinking to the latest and greatest. You can always dig deeper when you read the documentation, but, hopefully, this article gives you a good start.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://codefol.io/ Noah Gibbs

    Thanks for a great overview! Now I’ll go on to nitpick one little thing, because I’m like that ;-)

    Turbolinks is an unfounded controversy if, and only if, you can avoid using other people’s code.

    You can turn it off for yourself and it’s a non-issue. But you can’t turn it off in a legacy app that somebody wrote with it — the events are different, and “turning it off” isn’t just removing the gem, but removing the gem and reworking a lot of the JavaScript events.

    Sometimes that’s easy. Often it’s not. And you don’t know which in advance.

    I think Turbolinks is a good idea. I’m glad it’s out there, and I think it will improve the overall code quality of some JavaScript libraries that currently assume they load once on page-load and never reconfigure themselves (or get errors if they do).

    However, it’s also potentially putting a much larger maintenance load on Rails programmers — many JavaScript libraries are now broken by default for us when they aren’t broken without Turbolinks.

    For me, for now, that’s no big deal. I can think of several reasons that may change.

    • Michal Oláh

      I completely agree with you. It does hurt when you have to turn it off in an app that wasn’t built with change in mind. There are however ways of writing the event bindings more generically to shield yourself from change. IMHO thats just good software practices in general and I think software (and frameworks in particular) should be geared towards those. When you encounter code thats not ideal it should hurt (just like assets did when they came out) and thats a good thing making us rethink our software and (hopefully) make it better.

      • http://codefol.io/ Noah Gibbs

        So are you in favor of keeping a variable around for what event to bind to for init code? Like, to switch off between page-load and turbolinks-load events? That’s one of those things that’s annoying for any app that never switches back and forth, and may not work (you wouldn’t normally test it). That would shield you from change, but would still kind of suck.

        It would be nice if the framework did that for us… But currently, nope. You have to do it yourself.

        There’s a balance between “non-ideal code should hurt” and “you’ll be punished for using third-party code.” That’s one reason RSpec often hurts badly to use — it tries to make badly-designed code painful. Unfortunately, some of that badly-designed code is in third-party gems or the Ruby standard library. You *could* customize every gem you use to be more compatible with RSpec, but can you call that a win?

        And most library authors aren’t sympathetic to the argument that goes, “you need to change your API and break compatibility with all your existing users because your opinions aren’t enough like RSpec.” It’s a hard sell.

        Turbolinks is similar. You can file cleanup bugs — “no, none of your other users have needed this, but I don’t want to turn off Turbolinks so you need to fix these things that you’ve never seen break except with Turbolinks. Yes, I’m sure this isn’t a Turbolinks problem, even though nobody else has ever reported it.”

        But realistically, that just mostly means that you’ll get a lot less compatibility between Rails and most third-party JavaScript libraries from now on — unless you turn off Turbolinks. And if you do, you should turn it off early, because switching is (apparently intentionally) painful.

        • Michal Oláh

          Actually I would try and wrap the call somehow and make it a generic thing used throughout the app. If I encounter a need for this (so far I had to do only a handfull of Turbolinks bindings so I haven’t thus far) I may dig into this and release something that handles the problem. We were all happily binding (knowing that this could potentially lead to maintanance problems) to the jQuery DOM loaded event and the moment that changes we’re helpless. Turbolinks forces us to rethink this – positive design pressure.

          As for libraries, they usually are a mess under the hood and their authors should have gotten their act together long ago. The problems really aren’t Turbolinks specific, they are just manifestation of poor design. Again positive design pressure.

          I’m not sure if you remember the controversy when the Asset Pipeline first came out, but there was a fair bit of people who didn’t like it. Granted, there were problems initially, but I have worked on projects that used it and those that didn’t. Those that didn’t just had a junk drawer in /public and junk was just added to it. Those that used it had a nice folder structure under /app with generally much better code. Again positive design pressure at work.

          • http://codefol.io/ Noah Gibbs

            Sure. Many of those third-party libraries whose “authors should have gotten their act together long ago” aren’t maintained, or are maintained by people who don’t want to or can’t fix the problems. They’re still usable — as long as you don’t use Turbolinks.

            Positive design pressure, but it manifests as “Rails guys (only) can’t use a significant percentage of third-party software without random bugs.”

            I remember a lot of people not liking the asset pipeline. As far as I can tell, most of them still don’t use it. It’s still easier to convert away from it, mostly.

            Though if you use CoffeeScript, sure, converting sucks. My experience is that most of those same people don’t use CoffeeScript. You’d think you could convert it to tags in the templates or something, but no — Rails doesn’t support CoffeeScript that way, despite quite a lot of “positive design pressure”, which appears not to work on the Rails core team :-)

            They ignore many of the RSpec issues as well.

  • Guest

    I completely agree with you. It does hurt when you have to turn it off in an app that wasn’t built with change in mind. There are however ways of writing the event bindings more generically to shield yourself from change. IMHO thats just good software practices in general and I think software (and frameworks in particular) should be geared towards those. When you encounter code thats not ideal it should hurt (just like assets did when they came out) and thats a good thing making us rethink our software and (hopefully) make it better.