A Student's Guide to Software Engineering Tools & Techniques »

Ruby on Rails

Authors: Chattoraj Ayush

Reviewers: Lu Yang Kenneth, Marvin Chin

Ruby on Rails Overview

Ruby on Rails is a web application framework written in the Ruby programming language. It was designed with the intention of making programming web applications easier and quicker by reducing the amount of code you write. Rails achieves this by being an "opinionated framework", which means that the developers believe that there is a certain approach - often called "The Rails Way" - that is ideal for building most types of web applications. By adhering to this approach, Rails allows you to focus primarily on the logic, and fills in much of the boilerplate code for you.

The Rails Way

One of the core tenets of Rails is "Convention over Configuration", which means that many decisions regarding the structure of your Rails application are made for you in advance, so that there is less effort needed to come up with your own set of best practices. This is perhaps most evident in Rails' decision to follow a Model-View-Controller (MVC) architecture by default.

Here's how a Rails application is structured:

Figure 1. Model-View-Controller Framework source

Let us follow the diagram, and trace the control flow as a Rails app responds to a request by the client. In this example, the client is making a query for all users in the database.

Router

When Rails receives the URL, it first looks up the A URI (Uniform Resource Identifier) is a string of characters that are used to identify a resource. URLs are a subset of URIs. URI in the routes.rb file, which defines, by default, all the routes in the application. Each valid URI is mapped to a function located inside a Controller, that is then invoked to provide a response to the request.

In order to achieve this, Rails exposes a convenient abstraction called a "Resource". A resource can refer to an object upon which you can perform CRUD (create, read, update, and delete) operations. These operations are often invoked via a HTTP request of the appropriate request verb (POST, GET, PUT, and DELETE respectively). In order to declare a resource called user, you would mention it inside your routes.rb in the following manner:

resources :users

Now, you can check all the routes that you have in your application by running rails routes in the command line. This should include:

    Prefix Verb   URI Pattern                          Controller#Action
     users GET    /users(.:format)                     users#index
           POST   /users(.:format)                     users#create
  new_user GET    /users/new(.:format)                 users#new
 edit_user GET    /users/:id/edit(.:format)            users#edit
      user GET    /users/:id(.:format)                 users#show
           PATCH  /users/:id(.:format)                 users#update
           PUT    /users/:id(.:format)                 users#update
           DELETE /users/:id(.:format)                 users#destroy

As can be seen, the common routes that you would need for the resources have already been mapped to their respective URI's. This is equivalent to manually declaring the 7 resourceful routes for the user resource. By convention, Rails will then look for a controller called users_controller.rb and invoke the name of the function mapped to the particular URI and Verb. More information about Rails routes and how they work can be found here.

Controller

In the standard Rails convention, once inside the application directory, if you navigate to app/controllers/, you should be able to see all the controllers in your project. Rails will look for the controller with the <name>_controller.rb, where <name> would be replaced by the result under the Controller heading in the list of routes above, in this case, users. It will then look for the function name under the Action heading mapped to the URI function inside the Controller, and then call it. For example, if someone makes a GET request to /users, Rails will then respond by calling the users#index controller action (refer to the code below).

class UsersController < ApplicationController
  def index
    # implementation goes here
  end
end

users_controller.rb with an index method

Model

The Model is very similar to a Class (à la Object-Oriented Programming), and is a useful abstraction for representing and encapsulating objects in your application. This is also known as the Active Record pattern in Rails. In this pattern, the Models are mapped to tables in the database, and you can query a model's fields directly in Ruby, instead of writing your own SQL queries. Models also have generic methods such as find, all, create, save, update, delete included in them by default, so you don't need to implement them yourself.

In our diagram, the Controller calls the all method in the User model, which is, by convention in a file called User.rb, which is stored along with all other models at app/models/. The model then acts as an intermediary between the application and the database, and returns the results of the corresponding query.

View

After the Controller has fetched all the data necessary and applied whatever transformations are needed, it passes in the requisite fields to its view. The View is what the client sees, and is a collection of .erb (Embedded RuBy) files, which are basically just HTML files with lines of Ruby code embedded in it to modify its appearance and behavior. When the controller is generated, a corresponding folder of views is made inside app/views/. All the .erb files are named after the controller action and stored in folders named after the controller. Following the same example above, Rails will serve up the app/views/users/index.html.erb view to the client when executing the users#index controller action, thus concluding the request-response cycle.

Why Rails

Many popular and very technically mature websites started off using, and continue to use Rails. These include: GitHub, Airbnb, Twitter, Hulu, Shopify, and Twitch, among others. Some of the key features that makes Rails the framework of choice for these users are:

Fast Iteration Speed for Product Developement

As an opinionated framework, Rails helps to automate trivial tasks by following certain conventions, which can make the development process faster. For example, in a Rails project that is connected to a database, each class is mapped to a table, as described in the Active Record Pattern, and the table name is just a pluralized version of the class's name - the User class becomes the Users table, the Person class becomes the People table, etc. This means that you don't need to spend any time deliberating on the name of the tables, nor do you have to juggle multiple concepts while mapping out the database and application, as Rails will automatically connect the two and spare you from deliberating about the exact name of the table. A similar approach is used to handle resources, which also automates much of the process.

The automation of these mundane tasks could make development a more enjoyable (more on that in a bit) and faster process. All of these optimizations can make developing an application between 30-40% faster on Rails [source]. It also makes Rails relatively easy to learn, as you do not need to understand HTTP verbs to make a basic Rails application (while mastery of the topic will certainly help). Furthermore, programmers experienced in Rails usually find it easier to start on existing Rails projects, since the conventions followed are usually the same, giving you the same directory structure and route naming conventions everywhere.

Optimized for Programmer Happiness

One of the primary tenets of Ruby was to make it the "Least Surprising Language", where the language was designed to feel intuitive and perform exactly as a developer would expect. In Rails, even more syntactic sugar is added on top of its Ruby foundations, in an effort to optimize for programmer happiness. As a result, Rails code tends to look very "neat" and feels inherently "nice" to write (as subjective as that sounds).

For example, in order to fetch yesterday's date in Python, one may have to write

import datetime
datetime.datetime.now() - datetime.timedelta(days = 1)

The same action in Rails is done using

1.day.ago

Similarly, Rails also added a new method to access an array element. Instead of following the C convention of using square-brackets to access the second element in the array "elements" like

elements[1]

Rails developers can use ordinal representation to access the same element with the command

elements.second

And while this may not reduce the amount one has to type, most Rails enthusiasts agree that it makes the code much more readable, easy to work with, and therefore happiness-inducing.

Large and Active Community of Developers

The Ruby language has a very passionate and extensive community of developers, and there is a vast array of libraries (called "Gems"), that can be used with your Rails projects. These libraries provide a lot of functionalities, and can be used to add flexibility to Rails projects when required.

Furthermore, as a result of its popularity, it is very likely that any problems you encounter with Rails will have a solution that you can find on Google or StackOverflow. For any new problems, you are also likely to receive a response very quickly, because the Ruby community prides itself for being welcoming and helpful.

Drawbacks

Rails might not be best suited for all use cases, and there are a few areas that you should consider when deciding on your web framework of choice.

Lack of Flexibility

Although Rails is highly optimized for most types of web applications, there are inevitably some cases where following an MVC architecture isn't suitable. In those cases, it might be better for developers to use frameworks that allow for more flexibility. You may also find Rails' defaults very stifling if you wish to have a different directory structure, or give your routes different names. While there are ways for you to configure Rails to your needs, it might be less painful to avoid those conventions from the beginning.

Performance

While newer releases have combated this problem to a large degree, many of the old releases of Rails have had a reputation for being very bloated and slow to respond. Applications in other frameworks, or purely client-side applications with a minimal backend, tend to have a much lower delay between an action and a response. While it should be noted that the vast majority of delays in applications occur due to poor optimization, and not the inherent speed of the framework, Rails still remains more bulky and slower than many of its competitors.

Learning Curve

Rails' tendency to do things "automagically" can also be very confusing to new users, who might be more used to configuring routes and directory structures themselves. You may also find it challenging to switch to other frameworks, where you may be required to configure routes and write SQL queries yourself, instead of having the framework do it for you. You will also have to learn the Ruby language, and it might be harder considering that most front-end applications are in JavaScript and its variants, and you will have to juggle multiple languages while you develop your application.

Getting Started

To install Rails on your computer, you will first need to install Ruby. After this, you simply need to run:

gem install rails

gem is a package manager for Ruby that allows you to install Rails (as well as all the other Gems in Ruby). Once you're done with the setup, you can start developing applications right away. Here are some resources you might find helpful:

  1. The Ruby Guides are a must read, as you cannot use Rails without knowing the underlying language.
  2. The Rails Guides are the definitive guide for Rails and you can be assured that it will always be up to date with the latest releases.
  3. Go Rails also has great screencast tutorials that really hold your hand through the multitude of features Rails has, and can be used by more visual learners.
  4. Rails Tutorial is a book with a lot of advanced topics, so if you really want to study Rails in depth, consider this.