An introduction to Rails - MVC, routing and ActiveRecord

This week I started Tealeaf Academy’s second, Rails-focused course and I’m pumped to now start building “real” applications that I can have live on the web. Throughout the course I’ll be building Postit!, a simplified Reddit clone with all functionality coded from scratch without the use of external libraries or gems. I’ll use the code from this app to illustrate my examples below.

In my post about my experiences with Tealeaf’s first course I took the approach of providing an overview of what was covered in the course, rather than covering any of the main concepts in any great detail. Given that the second and third courses focus on learning Rails inside out and back to front I’ll provide a lot more detail on each of the key topics covered so this will be useful to anyone taking this course, or indeed to anyone learning Rails generally. Converting my hurried notes into something more coherent should also help to sear this stuff into my brain.

The Structure of a Rails Application

To generate a new Rails app, you use the new application generator which is simply called with rails new and the name for your app. For example:

  $ rails new Postit!

creates a new app named Postit! with the files and folders you need to get started.

The default directory structure of a Rails app looks like this (adapted from RailsGuides):

File/Folder Contents
app/ This is the folder where you will work most of the time. It contains the controllers, models, views, helpers, mailers and assets for your application.
bin/ Contains the rails script that starts your app and can contain other scripts you use to setup, deploy or run your application.
config/ As the name suggests, this directory holds the configuration files for your application, including your database configuration (in database.yml), your Rails environment structure (environment.rb), and routing of incoming web requests (routes.rb). You can also specify environment-specific configurations for each of the three Rails environments (test, development and production) here.
db/ All database related files go into this folder including the schema and migrations. If you’re using SQLite, the database file (*.sqlite3) will be in this folder.
lib/ Extended modules for your application.
log/ Application log files.
public/ The only folder seen by the world as-is. Contains static files and compiled assets.
test/ Files for testing your application.
tmp/ Temporary files (like cache, pid, and session files).
vendor/ Libraries provided by third-party vendors (like security libraries or database utilities beyond the basic Rails distribution) go here.
Gemfile, Gemfile.lock These files allow you to specify what gem dependencies are needed for your application. These files are used by the Bundler gem.
Rakefile Handles all the Rake tasks inside your application.
README This is one of the most important files in your application as it provides some basic detail about your app to others, such as what it does and how to set it up. It’s also displayed when users visit your app’s repository on GitHub.

MVC Overview

Rails uses the model-view-controller (MVC) architectural pattern. This pattern divides an application into three interconnected parts which separates the logic of the application from the rest of the user interface.

  • The controller receives requests from the router and connects the model with the view – it processes the data from the model and then passes this onto the view. The view is then rendered and displayed in the user’s browser.

  • The model represents the information and the data from the database. It is independent from the database and validates data before it is saved to the database.

  • The view is what the user sees. It requests information from the model that it uses to generate an output representation to the user. Inside the view you will find (most of the time) HTML with embedded Ruby code. In Rails, views are implemented using ERb (Embedded Ruby) by default.

An overview of the MVC architecture
The MVC architecture in Rails

Routing & Resources

The router is at the front door of your application. When a request is received, the router uses the HTTP verb (such as GET or POST), URL and parameters of the request to determine where to route it and then routes this to the appropriate controller and action - for example, with a Posts controller in your app, a request to localhost:3000/posts will be directed to the index action of the Posts controller by default. Once the request is routed to the controller, the controller can either render a view (the default action) or redirect the request.

The routing table for your app can be viewed by running rake routes in the console or at localhost:3000/rails/info/routes.

Routes for your app are configured in config/routes.rb. Rails provides a resources helper method which can be called in your routes file to generate the required routes automatically.

In the case of my Postit! app, the code:

PostitTemplate::Application.routes.draw do
  resources :posts
end

will create routes for the seven default actions for the Posts controller (index, create, new, edit, show, update, and destroy).

The resulting routing table looks like this:

   Prefix Verb   URI Pattern               Controller#Action
     root GET    /                         posts#index
    posts GET    /posts(.:format)          posts#index
          POST   /posts(.:format)          posts#create
 new_post GET    /posts/new(.:format)      posts#new
edit_post GET    /posts/:id/edit(.:format) posts#edit
     post GET    /posts/:id(.:format)      posts#show
          PATCH  /posts/:id(.:format)      posts#update
          PUT    /posts/:id(.:format)      posts#update
          DELETE /posts/:id(.:format)      posts#destroy

In the Postit! app I want to prevent users deleting posts. To achieve this, the route to the destroy action of the Posts controller needs to be removed. This can be achieved by adding except: :destroy to the call to the resources helper:

PostitTemplate::Application.routes.draw do
  resources :posts, except: :destroy
end

You can about Rails routing in lot more detail at The Odin Project.

ActiveRecord

ActiveRecord is the ORM (Object Relational Mapping) library used in Rails applications by default. It is the Model in MVC, which is the layer responsible for representing business data and logic. ActiveRecord simplifies the use of databases in Rails applications by mapping each row of the database to an object and allowing you to create or retrieve data from your database in an object-oriented fashion without having to write complex SQL statements. For example, to retrieve an array of all User objects from your database you simply use User.all.

ActiveRecord models can be connected through associations, which enable you to tie objects in different models together to express relationships like “a Comment belongs to a Commenter”.

Rails supports six types of associations:

  • belongs_to
  • has_one
  • has_many
  • has_many :through
  • has_one :through
  • has_and_belongs_to_many

The primary associations used between the models in the Postit! app are has_many and has_many :through:

has_many (One to Many or 1:M) association

A has_many association indicates a one-to-many connection with another model. In the Postit! app, a User has many Posts and Comments:

class User < ActiveRecord::Base
  has_many :posts
  has_many :comments
end

has_many :through (Many to Many or M:M) association

The has_many :through association allows you to connect two models through a third model (a “join model”). In the case of the Postit! app, the Post and Category models are connected through the PostCategory model:

class Post < ActiveRecord::Base
  has_many :post_categories
  has_many :categories, through: :post_categories
end