Demystifying the Magic of Rails

Jeffrey Warren
Ruby on Rails
4 min readJun 22, 2015

--

For me, like many others, Rails has a very “magical” feel. You don’t really even need to know Ruby to get started with Rails and in a few hours you’ll have a fully functional application, complete with users, sessions, and dynamic content. I probably made about 5 non-trivial Rails applications before I really knew what a block was (other than something I just passed to each). Never mind the inheritance structure, modules, mixins, and metaprogramming in Ruby. For me, it wasn’t until I started thinking about how various calls (attr_accessor, before_filter, etc.) work and what they’re doing that I really felt I had a good grasp on things.

Simple commands, before filters and validations for example, add a ton of functionality. A lot of Rails is clever uses of metaprogramming, but spend some time looking under the hood and you’ll probably stop feeling like things are just “magic”. That’s what I hope to help you accomplish in this post.

Ruby::attr_accessor

I want to start by looking at Ruby’s attr_accessor method. It’s pretty simple and has been used by almost everyone who’s ever written a line of Ruby; however, most people probably haven’t stop to understand how it works. It’s not Rails-specific but provides some insight into how we might think about certain functions.

Event class showing use of attr_accessor.

Here we see that by calling attr_accessor on `start_at` and `end_at` we have given ourselves a lot of functionality.

  • Public methods: [:start_at, :start_at=, :end_at, and :end_at=]
  • Instance variables: [:@start_at, :@end_at]

Try this out for yourself. Copy the above gist into an irb and and check event.public_methods and event.instance_variables.

So when we want to implement attr_accessor we need to do two things, set instance variables and define public methods. Luckily Ruby comes with instance_variable_set, instance_variable_get, and define_method — providing just what we need.

Custom attr_accessor method.

Now we see how easy it is to define functions such as attr_accessor, or the similar attr_reader & attr_writer. The important piece to understand here is that attr_accessor isn’t a special or magic function — it’s just one that happens to take advantage of Ruby’s dynamic nature.

Rails::before_action

Rails provides filters to be run before, after, or around controller actions. A common use case is to require that users are logged in. Putting a `before_action` (formerly and aliased, `before_filter`) in the application controller, as seen below, means that any class inheriting from ApplicationController will receive the filter (note there are ways to skip filters), and it will be run before any action.

Rails application controller with simple require_login.

Action filters are slightly more complicated than attr_accessor since we need to deal with inheritance and run arbitrary methods before or after a controller action. But let’s get started. We’ll make some simplifying assumptions to begin, that each action will be in a separate call, and there will be no additional parameters. I’ll leave it up to you to play with ways of relaxing these simplifications.

We first note that each filter must somehow be saved for each class, and that the inheritance structure must be obeyed. With this in mind we can write a simple controller class.

Controller class with basic before/after filter functionality.

Here we have a simple controller class with basic functionality for supporting before and after filters. Most of the code here is pretty simple and not too exciting. Storing & calling the methods (callbacks) is relatively straight forward. The only piece here that might be a little unfamiliar is the use of `self.inherited`. This method is invoked whenever a subclass is created and gives us the subclass itself. We can use this, and give the subclass our (already defined) before/after filters, conveniently passed through inheritance to our parent class.

Application controller requiring authentication.

Now when we inherit from the Controller class, we have a new class (ApplicationController) with it’s own filters.

Events controller defining a before and after filter.

Finally the EventsController class defines a few more actions. We can instantiate this and invoke the execute method to see our work in action.

$ EventsController.new.execute(:create)
Checking authentication…
Loading event…
Cleaning up…

We see the simple chain of actions run as expected. The parents classes will run first, which is exactly what happens in Rails.

Rails provides a lot of methods that seemingly augment the Ruby language with new functionality — in reality a lot of this is just Rails taking advantage of the extensibility of the Ruby programming language. I hope that seeing naive implementations of some of these methods will help you understand the dynamic nature of Ruby and embrace the “magic” within Rails. Some interesting things to think about are how ActiveRecord validations work.

--

--

Jeffrey Warren
Ruby on Rails

MIT ’14 Course 6, MIT M. Eng. Course 6 (on leave), Software Engineer @Wellframe