Upgrading Rails apps from 2015, 2016, and 2017

UPGRADING TO RAILS 3, 3.1, AND 3.2 (ANTIPATTERN: STAYING ON VERY OLD VERSIONS OF RAILS)

Today’s antipattern — staying on a versions Rails for too long — kicks off a week-long series of upgrades. The differences between the Rails versions are significant, and this series gives some tips and tricks of what to expect when upgrading.

Bu first, why should you upgrade?

Many people think when you build a piece of software you just build it once and it is complete. The truth is, a complicated layer of different pieces of the puzzle make up your application. Your application code itself will only represent a fraction of the code you write to make it work. All of these other parts — in Ruby, we have gems, although this point could be made for many languages — move in an ecosystem of development at different speeds. This means sometimes advancements in parts of the gems you are using allow new things. Often, on the other hand, the new code contains security fixes— that is, in the older version of the code has an exploit you didn’t know about. (An exploit is when ‘an attacker,’ or ‘nefarious user,’ is able to use a flaw in the code to access that they shouldn’t have, typically for financial gain.)

You would think that after all these years of web development things were “safe.” But in fact the opposite is true: because exploits happen from new code we have the potential for exploits all over the place. In fact, it is “best practice” is to patch (or fix) your exploits immediately when you discover them.

An system called the Common Vulnerabilities and Exposure System, abbreviated CVE (the system that tracks them these exploits) trackks these exploits for Rails and all of its gems. The MITRE corporation (with funding from the Cybersecurity and Infrastructure Security Agency, part of the U.S. Department of Homeland Security) oversees CVE. When someone finds and confirms a security problem in open source software, this system assigns a “CVE” number. Typcially these are “CVE-” and then the current year, and then another dash, and then a five-digit identification number.

Well intentioned developers wrote code that but didn’t realize their code could be exploited in this way.

When an exploit happens, you want to make sure you apply the latest Rails patchlevel to fix the security vulnerability. A patchlevel is the “third” version point (for example in Rails 3.1.1, the patchlevel is the “.1” part).

Apply a patchlevel upgrade should not be a difficult process whatsoever— change the Rails version in your Gemfile, bundle install, run your specs, and that’s is. (It should be this easy, but very rarely a patchlevel runs into some kind of issue with your codebase.)

When we talk about a “minor” version we are referring to the “second” version number you see after the first dot.

And finally, the “major” version is the first number you see here, like Rails 4, Rail 5, and Rail 6. A minor version is also called a “point release” because it refers to the release a new “point,” as in, “We upgraded from Rails 6 point 0 to Rails 6 point 1.” To avoid confusion, Rails developers typically communicate the point release, or minor version, of their Rails stack when asked “What version of Rails are you on?”

That’s because, if, for example, you were on Rails 5.2.1, telling someone who asks you “What version of Rails are you on?” the patchlevel specifically, is probably too irrelevant to matter to them. Whereas if you say “Rails 5,” you could be on Rails 5.0, 5.1, or 5.2, and the differences between those minor versions is more relevant.

Rails Version number explained

The official Rails support policy is to support the last three minor versions: 1) the current minor version, 2) the last point release (“ultimate”), and 3) and the last-last point release (or “penultimate”).

So as of 2020, the release versions are, counting backwards:

  • Rails 6.0 — the current version
  • Rails 5.2 — The last version
  • Rails 5.1 — The one before the last

That means that if you are on 5.0.x or anything below, you are on a version of Rails that may not receive security updates in the event that critical vulnerabilities are found. In short, if you have functioning Rails app, you’ll probably want to be on either #2 or #3, that is, the last or last-last minor version of Rails. Staying on a very old version is today’s antipattern.

I won’t go back father than Rails 3.0 in this series, so to kick things off, here’s a broad overview of upgrading to Rails 3 (that means upgrading from 2.x to any version of Rails 3)

Rails 3.0

Application Object —

Your application now operates at an object where its own name begins the name of the object.

The Rails & Merb Story

Around 2007, a team led by named Yahuda Katz —a developer who had a high profile in the Ruby community — criticized Rails 2 for lacking modularity. Components could not be plugged in or taken out, was their main criticism of Rails. This is called component modularity. As well, Rails lacked a well defined API for how to define a plugin or an engine inside of your app. Finally, Rails in the early years had got some bad press because new entrant into the field didn’t understand how to write optimized queries. You will note, if you watched the lecture on N+1 queries, the notice Rails developer will easily create N+1 queries, and therefor bloat, if they don’t know better. These were the major criticisms of Rails that led this team to actually fork Ruby on Rails into something new called MERB.

MERB was created by Yahuda Katz and Ezra Zygmuntowicz (please forgive me if I have misprounced your name).

But then what happened next is fascinating: The Rails core team said “we’re a big tent.” We can take this feedback and incorporate it. So Rails 3 basically incorporates Merb, in other words, it becomes Merb.

Rails and Merb are officially re-joined and this stack, with Merb’s improvements, is called Rails 3.

Bundler and Gemfile

The config.gem method is gone and has been replaced by using bundler and a Gemfile.

This is the Rails version where the Rails core team introduces Bundler and it becomes default. If you don’t know bundler, learn it now to upgrade.

Many Things Decoupled

Many parts of Rails have been de-coupled. After a recompense occurs with the MERB team as described above, Rails now has decoupled its engine architecture and allows for a cleaner API for “mounting engines.”

Railsties

Railsties is a way that applications— that is, engines — can interoperate with one another. Each application now has its own namespace, your application is now begun with YourAppName.boot for example, makes interacting with other applications a lot easier. Let me stop here and make an important point: In 2007, when you mounted two disparate pieces of code into one operating codebase, you called it “two applications.” For example, you might have YourApp and you might have Devise gem. Devise operates at the global namespace Devise::, whereas your app operates at YourApp::

Both are considered “applications” for the purpose of this discussion, but in 2020, when people say “talk to another application” they typically mean a separate piece of software running on a separate server (at its own endpoint). This is called an external application, meaning an application we talk to outside of this one. (There are other ways applications can talk to one another on a system or in a local network, but that’s beyond the scope of this introductory tutorial.)

For the purpose of what you’re leaning about today, consider these mounted “applications” (really, Rails engines) to be “internal applications,” or parts of your code that you’ve incorporated from other people’s code.

Protect From Forgery By Default

application_controller.rb now has protect_from_forgery on by default. That means that CSRF tokens are automatically appended to your forms and also sent back with your forms. CSRF is a little bit beyond the scope of this introduction but just know for now this mechanism ensures that users make a form request before a form submission, and it eliminates lots of bot traffic that will hit your forms and website attempting to find vulnerabilities.

Cookies & Secrets

• The cookie_verifier_secret has been deprecated and now instead it is assigned through Rails.application.config.cookie_secret and moved into its own file: config/initializers/cookie_verification_secret.rb.

Respond With

• Introduction of respond_with syntax

Quick Flash While Rendering

•  You can now pass :notice => 'this is a message' or :alert => 'oops, there was an error' to the format call inside a respond_to block. The flash[] hash still works as previously

• Rails 3 is where remote: true is introduced for “Unobtrusive Javascript”

form_for @post, :remote => true

• Prior to Rails 3, you may have seen helpers like form_for or div_for that insert content from a block using

<% form_for %>

 Notice that this old-style syntax does not have an equal sign (which means “print out to the file”). Without the equal sign, the code evaluates the block but does not output its result. (Prior to Rails 3, this output to the view.) Now, when writing a block in this style, you must change <% to <%= like so

<%= form_for %>

• A new Syntax for Validations now simplifies things:

validates :attribtue

(However, the old-style validates_* syntax still works via magic methods and the release notes do not suggest this new-style is mandated.) How, we write things like

validates :email, presence: true

The validates method has the following options:

  • :acceptance => Boolean.
  • :confirmation => Boolean.
  • :exclusion => { :in => Enumerable }.
  • :inclusion => { :in => Enumerable }.
  • :format => { :with => Regexp, :on => :create }.
  • :length => { :maximum => Fixnum }.
  • :numericality => Boolean.
  • :presence => Boolean.
  • :uniqueness => Boolean.

Sessions and Cookies

Previously, the session_store was configured in ActionController::Base.session. That is now moved to Rails.application.config.session_store. See the config file config/initializers/session_store.rb

ActiveRecord (Arel)

ActiveRecord now returns Arel objects and has a observer-effect that is beyond the scope of this post. See the posts here and here that cover Arel obejcts.

What to Get Rid of

  • Remove RAILS_ROOT and replace it with Rails.root,
  • Remove RAILS_ENV and replace it with Rails.env, and
  • RemoveRAILS_DEFAULT_LOGGER and replace it with Rails.logger.
  • named_scope in an Active Record class is deprecated and has been renamed to just scope.

Rails 3.1

Remember, 3.1 was Rails point release (also known as a “minor release”). That means it received only a few updates and is not considered as “major” of an upgrade as from Rails 2 to Rails 3.

• jQuery is the default Javascript library in Rails 3.1. Now you can get jQuery just by using Rails.

•  add this line to this file config/application.rb. This is necessary for the Asset Pipeline.

  • config.assets.enabled = trueconfig.assets.version = '1.0'

• Several more changes for the asset pipeline in Production & test. See the docs for details.

Rails 3.2

• If you have a custom table name set on your models with:

self.set_table_name "cc"

You’ll want to change it to this syntax:

self.table_name = “cc”

Rails::Plugin is deprecated and will be removed in Rails 4.0. Instead of adding plugins to vendor/plugins use gems or bundler with path or git dependencies.

• In a migration, when you specify the t.timestamps, the migration creates the created_at and updated_at columns, it makes them non-nullable by default.

I hope you have enjoyed this tour of the Rails 3 new features. If you are on a very old version of Rails, you should probably upgrade it as soon as possible.