Inheritance on-the-fly with Ruby on Rails

Today, I’d like to try a new format of blogpost.

Indeed, the goal here is to break down a well known piece of Ruby code that we encounter everyday.

Today we’re going to talk about the way that Ruby on Rails ensure the compatibility of your migrations when you upgrade Ruby on Rails.

All the magic is done within the ActiveRecord::Migration class.

So, let’s get the party started!

Case Study

We use Ruby on Rails 5.2.x.

Let’s generate a Product model from the terminal

?> bin/rails generate model Product name:string description:text

Among all the generated files, this command will generate a migration class

Here a CreateProducts class is generated.

At first glance, this class seems to inherit from anything but a class.

But, in Ruby, a class can only inherits from a class.

So, what happens here?

Class.new(super)

As we’ve seen in the short guide to learning how Classes work in Ruby article, a class is an instance of the class Class.

So, our CreateProducts class could be defined as following — without using the class..end syntactic sugar

Normally, the Class.new method expects a class as parameter that will be set as superclass of the returned instance of Class.

So, as expressions passed as arguments to methods are always evaluated before the method call itself, then ActiveRecord::Migration[5.2] is evaluated and its return value — which is a class — is passed as argument of the Class.new method call.

So what’s theActiveRecord::Migration[5.2] return value?

ActiveRecord::Migration.[](version)

This statement is a shortcut to the ActiveRecord::Migration.[](version) class method call.

This method is in charge of returning the version of the ActiveRecord::Migration class that corresponds to the version of Ruby on Rails passed as argument — Ruby on Rails 5.2.x in our case.

Let’s see what’s the returned class in this case

Here we see that ActiveRecord::Migration[5.2] returns a class named as ActiveRecord::Migration::Current and that this class inherits from ActiveRecord::Migration.

This class contains a set of changes related to the current version of Rails— 5.2 in our case.

Also, this class derivates from ActiveRecord::Migration. So it shares all its properties.

Note that a call to ActiveRecord::Migration[5.1] returns the ActiveRecord::Migration::Compatibility::V5_1 class which derivates from ActiveRecord::Migration::Current.

From 5.2 to 6.0

So, let’s suppose that we want to upgrade our app to Ruby on Rails 6.0.

To ensure the compatibility of our migrations, all our migration are version to ActiveRecord::Migration[5.2].

If so, all our migration should generate the same database schema without any effort.

Let’s see what the Ruby on Rails Team does about migration when they release a new version of Ruby on Rails:

1- The ActiveRecord::Migration::Current is now returned by a call to ActiveRecord::Migration[6.0] — and not [5.2] anymore.

2- An ActiveRecord::Migration::Compatibility ::V5_2 class is created and all the specialisations of the old ActiveRecord::Migration::Current are moved within this class.

3- new changes that correspond to the Ruby on Rails 6.0 release are implemented within the ActiveRecord::Migration::Current class.

4-ActiveRecord::Migration[5.2] returns— in Ruby on Rails 6.0 — to ActiveRecord::Migration::Compatibility::V5_2.

So this mechanism ensures that the same database schema is generated when a Ruby on Rails application is upgraded to a newer version of Ruby on Rails.

Conclusion

The ActiveRecord::Migration — which is the superclass of any migration class — is versioned to avoid regression and side effects during database migration.

In effect, as the ActiveRecord::Migration library +/- differs between each major or minor versions of Ruby on Rails, then the generated schema can also slightly differ between each of these versions.

Concretely, if an app in Ruby on Rails 5.2 is upgraded to Ruby on Rails 6.0, then, when migrations are played, the generated schema can slightly differ between these versions and some undesired side effects can occur accordingly.

So, Ruby on Rails decided to version the ActiveRecord::Migration to address this issue.

Voilà!

ONE MORE THING ⬇

Feel free to subscribe here: www.rubycademy.com


Thank you for taking the time to read this post :-)

Feel free to 👏 and share this article if it has been useful for you. 🚀

Also, as I post an article every 3 days, feel free to follow me to be notified of my new article releases.

Here is a link to my last article:

Using the Find module to count the LOC of a Rails application