Rails Magic Autoload (Zeitwerk)

Rails Magic Autoload

Beginner

Rails Magic Autoload

Rails Autoloading is what happens in the background that most developers don’t even think about. The Rails Autoload is something that is important for you to know about. Rails will automatically load certain files of your Rails app application when it boots up.

When you first learn Rails you learn about its basic structure:

app/
   models/
   views/
   controllers/

We call this “MVC” for “model-view-controller.”

Then you get a little more experience and you learn about domain architecture and services. Perhaps you make a new Rails app and you notice an empty folder

Controllers/concerns/

And you look at this folder and you think, “What do I put there?”

Sometimes you must use the ‘require’ keyword to load files—- as you see in specs often— but sometimes you don’t. When do you use ‘require’ and when don’t you?

Rails has an important piece of magic that’s significant for every new Rails dev to know: the autoload with same-name, or correctly named, files. That is, Rails will autoload files for you if you meet the three conditions below.

Before letting you in on the secret, lets review the concepts of underscores, title case, and namespaces.

Underscore

An underscore is a character on your keyboard (probably to the right of the 0 on your number line). On most keyboards, you press SHIFT-(-). Press SHIFT and the dash key to produce an underscore, which looks like this: _ . The underscore appears where the underline would just below where a letter would be but denotes that there is no letter or number. Do not confuse the underscore with the dash, which appears in the middle of the line. On your keyboard, you don’t hold SHIFT to create a dash character, and you do hold shift to create an underscore.

What does the underscore mean? The underscore means the space of “no character” when in fact it does represent a character in the computer’s implementation. Because loading from operating systems is finicky with spaces, we don’t use spaces in our file names. Instead, we use underscores.

Here are some examples of Ruby files with underscores in them. You always use underscores when naming the actual files on your disk. (Even though your operating system might actually allow you to put a space in a file name, don’t do this when naming Ruby files.)

payment_adjustor.rb

line_item.rb

subscriptions_controller.rb

Titlecase:

Titlecase refers to a special syntax used by Ruby and some other programming languages to represent objects. In short, if you have several words, you make each of the first letters of each word uppercase and then you string the words together. So, if you have a phrase like “something wicked this way comes” to make it title case would look like:

SomethingWickedThisWayComes

Note how we capitalize (uppercase) the first letter of each word but the other letters are not capitalized. Also notice how there are no spaces or underscore separating the words. We call this titlecase and it is how we write all objects in Ruby. (Do not confuse titlecase with camelcase, often found in other programming languages, which is very similar to titlecase but the very first letter of the whole object name is lower case. In our example, the camelcase of what we have above would be somethingWicketThisWayComes with lowercase s in something)

Namespace or Namespacing

A namespace, or namespacing (the act of applying a namespace), is really just when we want to group things together and give them a common element in their name, typically prepended (or put before). In Rails, we have a special way to namespace (explained below) using subfolders. But, if, for example, we wanted all parts of our application involving subscription management to be under the name ‘subscriptions,’ we might (actually, as I will explain below, we can and should) move our Ruby objects into a subfolder called ‘subscriptions’ and also namespace our objects by renaming them Subscriptions:: plus what we first called them.

A namespace is really just a way for programmers to group ideas together (and truly, it is probably a relatively poor way to think about object abstraction. The examples provided in this post provide examples of what I would call good use of namespacing. If you find a large system that is highly namespaced or is overly convoluted, you may have some antipatterns. This is because developers overused namespacing, or relied on it when they should have created more extensible encapsulations. This advanced subject is beyond the scope fo this post. Just keep in mind that namespacing, while a quick way to clean up small messes, should not be overused when what you really need are better abstractions.)

OK, now the 3 magic rules of the Rails autoload:

Your Rails files will be loaded by Rails if (and only if):

1) they are in the app/ folder or any subfolder

2) The name of the file matches, using the correct underscore-to-titlecase conversion when writing your Ruby class names. Put another way, your file names must be in underscores (and lowercase), and your Ruby classes that each file defines must match exactly (in titlecase) the name of its file.

3) When a file appears in a subfolder, which is encouraged, you must namespace it –– the folder’s name (or namespace) is titlecased and prepended to the class name with ::

Those 3 rules are key. If you get them, you will master the Rails autoload.

Rails has all kinds of reasons why you would want to create sub folders INSIDE of other folders. For example, let’s say you have a polymorphic set of Products. That is, you have a Product object with a type column that Rails will use to instantiate the objects.

to verify you run

 bin/rails zeitwerk:check

Let’s say you have a folder in jobs called abc and a file called xyz_job.rb, but it doesn’t correctly define the object Abc::XyzJob. (Maybe it defines no object or mis-spelled version of the object.). You would get:

expected file app/jobs/abc/xyz_job.rb to define constant Abc::XyzJob

in dev the eager load doesn’t happen, but in prod and asset compile it does, which it why unreferenced files don’t hiccup in dev but they do in prod. (so you didn’t see the load error in dev)

A Basic Polymorphic Example

class Product < ApplicationRecord

end

class Shirt < Product

end

class Purse < Product

end

In the type column, you will see the name of the subclass directly. I will start by making a new Purse called “Little Clutch”

2.6.4 :004 > Purse.create(name: “Little Clutch”)

   (0.2ms)  begin transaction

  Purse Create (2.0ms)  INSERT INTO “products” (“name”, “type”, “created_at”, “updated_at”) VALUES (?, ?, ?, ?)  [[“name”, “Little Clutch”], [“type”, “Purse”], [“created_at”, “2020-05-13 16:55:49.132712”], [“updated_at”, “2020-05-13 16:55:49.132712”]]

   (2.0ms)  commit transaction

 => #<Purse id: 1, name: “Little Clutch”, type: “Purse”, created_at: “2020-05-13 16:55:49”, updated_at: “2020-05-13 16:55:49”> 

Then, I will create a Shirt called “Button down”

2.6.4 :005 > Shirt.create(name: “Button-down”)

   (0.1ms)  begin transaction

  Shirt Create (0.6ms)  INSERT INTO “products” (“name”, “type”, “created_at”, “updated_at”) VALUES (?, ?, ?, ?)  [[“name”, “Button-down”], [“type”, “Shirt”], [“created_at”, “2020-05-13 16:56:05.042379”], [“updated_at”, “2020-05-13 16:56:05.042379”]]

   (1.0ms)  commit transaction

 => #<Shirt id: 2, name: “Button-down”, type: “Shirt”, created_at: “2020-05-13 16:56:05”, updated_at: “2020-05-13 16:56:05”> 

In the example above, the three Ruby files (Product, Shirt, Purse) all appear in the root of the models/ folder

Database table showing two records

Models generated in Rails

In the example above, the three Ruby files (Product, Shirt, Purse) all appear in the root of the models/ folder

Polymorphic Autoload Example

Polymorphic Autoload example

Now let’s say we have many products and we want to move them into a sub folder called products/

We’ll want to do this with a little change to our class objects. Here’s what our new files & folders will look like:


Notice we did some things here: One, the superclass Product (was product.rb) has been renamed to products/base.rb

The class names now have namespaces— that’s because they are in the products/ folder. You must always match the folder name to the namespace, but you will titleize the namespace and keep the folder names lowercase.

So our new Purse becomes

class Producs::Purse < Products::Base

end

And now, our Shirt becomes

class Products::Shirt < Products::Base

end

One last thing, in our Products::Base we’ll need to set the table name:

class Products::Base < ApplicationRecord
self.table_name = 'products'
end

Now, Rails knows how to magically load your files. Never do anything else– like put non-namespaced files into subfolders. That is a Very Bad Thing and Very Bad Things will happen to you if you do.

Remember, Rails will magically load all of your objects in models/ controllers/ but only when the name of the object matches exactly— including the namespace to in titleized form— with the name of your Ruby class. THE RAILS COACH

EXAMPLE APP FOR THIS POST CAN BE FOUND AT

https://github.com/the-rails-coach/rails-magic-autoloading