Sep 30

Date published: 9-30-2013

Rails version: 4.0.0

What does automagically mean?

One of the first descriptions I heard about Rails was that it handled things automagically to make programming web sites quicker and easier. I’d never heard the term before, so I looked it up online and found this definition: automatically; in a way that is hidden from or not understood by the user, and in that sense, apparently “magical.” That was the way I felt when I worked through tutorials, and even though I’d written the program myself, I had no idea what it was doing to make it work in the background. The worst part was that even after learning how to do something, I couldn’t just sit down and write my own program using what I’d just learned. Usually the new program I was trying to make would break in some way, and I wouldn’t know what was broken or how to fix it because too much was happening automagically for me to know where to even start.

It took a while, but eventually I felt more comfortable with the process of creating a Rails application. The goal of this tutorial is to explain the model-view-controller paradigm and what Rails is doing in the background. Rails was built around the idea of placing convention over configuration. This means that if you follow the rules that Rails has in place, you can focus on creating content instead of spending your time messing with configuration files and making sure each part of your program knows where the other parts are. This is great if you already understand the conventions and how to use them, but for a beginner, there is a lot to learn before you’ll feel comfortable making your own applications.

One of the reasons I didn’t really understand what was going on in the background was because I was using the generators provided by Rails. The generators are great if you know what you’re doing already, but because they created so much and put it in places I didn’t know to look, I feel they were one of the major reasons I didn’t understand what was going on. So this tutorial will walk you through creating a basic site for leaving messages with minimal use of the Rails generators. Hopefully this will give you the experience of creating each part of the application, so you’ll know where to place each piece Rails needs and why it goes there.

Graffiti wall

So what is the site we’re going to make? I’m calling it a graffiti wall, where anyone can come along and write a message, and the latest messages will be “written over” older messages. We’ll need a way to store the messages written by the users and display them back to other users that visit the site. Rails uses the model-view-controller paradigm, so let’s break it down to see what we’ll need to do for each part of the site. The model portion of our application is what we need to store in the database. In our case, we’ll want to store information about the messages that users leave. The controller handles pulling data out of the database so we can show it to the user. The views will hold all the HTML code to show the messages. In addition to setting up the model, views, and controller, we’ll modify the routes.rb file so that Rails knows where to find all this new functionality.

Graffiti_wall

Graffiti wall

Before we get started, you should make a copy of the template site because we’ll be modifying it to create our graffiti wall. You can either visit Github, download a zip file with the code, and rename it or type the following into the terminal:

$ git clone https://github.com/wordplay/template_bootstrap_site.git
Cloning into 'template_bootstrap_site'...

$ cp -r template_bootstrap_site/ mvc_tutorial
$ cd mvc_tutorial/
$ bundle install

Now that you’re in the project directory, start the Rails server and load http://localhost:3000/ in your browser and make sure it’s working correctly.

Update the module information

This step is completely optional, but I do it to make sure applications I base off of my template have their own namespace. For this tutorial, it doesn’t matter much, so if you want to skip this step, scroll down to the next section, “Create the model.” If you plan on basing other projects off of a template, these steps will make sure each application is separate, which could save you from some debugging headaches in the long run if you have different applications running with the same module names.

We’ll want to make a few little changes to the project to distinguish it from our template project. Open the config/application.rb file and change the module name to MvcTutorial:

module MvcTutorial
  class Application < Rails::Application

There are several files that already expect the name of the application to be TemplateBootstrapSite that will have to be changed to MvcTutorial. Search and replace it in the following files:

  • Rakefile
  • config/environment.rb
  • config/routes.rb
  • config/environments/development.rb
  • config/environments/production.rb
  • config/environments/test.rb
  • config/initializers/secret_token.rb
  • config/initializers/session_store.rb

Finally, in the session_store.rb file, change the key to ‘_mvc_tutorial_session’ so session information will be saved in a different location than the template site. Now that you’ve changed all those files, make sure to save them all, shut down the Rails server if it is active, and restart the server. Load a few pages and make sure there are no errors being reported from changing the application module name. When you’re satisfied that it is working alright, commit your changes in Git:

$ git add .
$ git commit -m "Changed module name to MvcTutorial"

Create the model

The first thing we need to do is create the model so we can store our messages in the database. Before we can create the model, we need to know what we want to save in the database. In order to save a message, we need the message text and the name of the person who posted the message. To create the model, run the following in the terminal window:

$ rails generate model message name:string message_text:text
      invoke  active_record
      create    db/migrate/20130924161621_create_messages.rb
      create    app/models/message.rb

If you’re looking at that and thinking, “I thought we weren’t supposed to use generators!” then you have a good point. If you don’t want to use the generator, you can always create these files yourself, but I like to let Rails handle creating the migration files due to the timestamp that is used so Rails knows what order to modify the database. I also like using the generator because I know exactly what I want as far as the different fields in the database that should be created. As long as I know what the generator is doing, I think it’s ok to use, and in this case I can see that it created two files, the migration and the message model. The model is mostly empty, and we’ll be filling it later, so let’s look at the migration file at db/migrate/XXXX_create_messages.rb:

class CreateMessages < ActiveRecord::Migration
  def change
    create_table :messages do |t|
      t.string :name
      t.text :message_text

      t.timestamps
    end
  end
end

So what’s happening in this file? Migration files store information about how to incrementally change the database when we make changes to how the data should be stored. In this case, we’re telling it to create a table called messages and add fields for both the name and message_text. Rails automatically generates some fields to hold timestamps for when objects in the database are created and modified, and although it’s not shown here, it will also generate a field to hold a numerical id to differentiate between individual entries in the database. Now we need to run the migration to create the table in the database, so go back to the terminal window and type in:

$ rake db:migrate
==  CreateMessages: migrating =================================================
-- create_table(:messages)
   -> 0.0023s
==  CreateMessages: migrated (0.0024s) ========================================
$ annotate
Annotated (1): Message

The annotate command is provided by the annotate gem which should be installed. It should have modified our model file to add comments at the beginning of the file to show the schema of the messages table which will be useful when we setup our model. Open up the app/models/message.rb file, and it should look like this:

# == Schema Information
#
# Table name: messages
#
#  id           :integer          not null, primary key
#  name         :string(255)
#  message_text :text
#  created_at   :datetime
#  updated_at   :datetime
#

class Message < ActiveRecord::Base
end

We’re going to add everything we need for the model in this file. Type the following into the file:

class Message < ActiveRecord::Base
  validates :name, presence: true
  validates :message_text, presence: true, length: { minimum: 3, maximum: 250 }
end

So what are we doing here? We have validators for both our fields in the database. For the name, we’re checking to make sure that something has been entered, but it can be anything as long as it’s not empty. For the message text, we’re checking that something is there, but we’ve added the restriction that it should be at least 3 characters but no more than 250. Since it’s meant to be simulating graffiti, we want some kind of meaningful message but don’t want anything overly long.

That’s all we have to do for our model. Despite it being called model-view-controller, I’ve found it better to create the controller before creating the views. We can decide what actions we want to support when we create the controller, and that will give us a good idea what views we need to create.

Create the controller

We’re going to manually create the controller instead of using a Rails generator. There are seven actions that are common to most Rails controllers: new, create, index, show, edit, update, and destroy. We’re going to use all these actions in our application, so create a file called app/controllers/messages_controller.rb and type in the following:

class MessagesController < ApplicationController
  def new
  end

  def create
  end

  def index
  end

  def show
  end

  def edit
  end

  def update
  end

  def destroy
  end

  private
  def message_params
    params.require(:message).permit(:name, :message_text)
  end
end

That lays out the seven different actions we’re going to need and adds an extra private method to handle permissions for creating and updating information in the database. Let’s fill in those methods with what we’ll need in order to show the views.

The new and create actions work together to allow the user to create a new message in the database. The new action will be called before showing a web page where the user can type in their name and message and should initialize a new message object so that we can save that information when we get it from the user. When the user has typed in a name and message and clicks the button to create the message, the create action will be called with that information. We should use it to populate our message object and save it to the database. Add the following to the messages_controller:

  def new
    @message = Message.new
  end

  def create
    @message = Message.new(message_params)

    if @message.save
      redirect_to @message
    else
      render 'new'
    end
  end

The index and show actions are used to display information about messages already saved in the database. The index action will show a list of all the messages, so for it we need to pull all of the messages from the database. The show action will show only a specified message, so we need to pull just that message out of the database.  Fill in the index and show methods with the following:

  def index
    @messages = Message.all
  end

  def show
    @message = Message.find(params[:id])
  end

The edit and update actions work together to update information about a single post in the database. Similar to the new/create pair of actions, the edit action is called when a web page is shown to the user with the current information stored in the message, and the update action is called when the user clicks the button on the form. Fill in the actions with the following:

  def edit
    @message = Message.find(params[:id])
  end

  def update
    @message = Message.find(params[:id])

    if @message.update(message_params)
      redirect_to(@message)
    else
      render 'edit'
    end
  end

The destroy action is called to delete a message from the database. We’ll find the message in the database, destroy it, and then go back to the index page. Fill in the destroy method with this code:

  def destroy
    @message = Message.find(params[:id])
    @message.destroy

    redirect_to messages_path
  end

We’ve created all the different actions in the controller that Rails will look for, but we have one last thing to do so that the server knows that they exist. We need to edit the config/routes.rb file to add a route for our messages controller. Open the file and add the highlighted line:

  # You can have the root of your site routed with "root"
  root 'static_pages#home'
  get '/about', to: 'static_pages#about'

  resources :messages

By default when you add a route as a resource, Rails will automatically route to the seven different actions without us having to define them individually. This is fine because we want to use all those actions in our application. We can check that the routes have been properly defined by typing the following command in the console:

$ rake routes
      Prefix Verb   URI Pattern                  Controller#Action
        root GET    /                            static_pages#home
       about GET    /about(.:format)             static_pages#about
    messages GET    /messages(.:format)          messages#index
             POST   /messages(.:format)          messages#create
 new_message GET    /messages/new(.:format)      messages#new
edit_message GET    /messages/:id/edit(.:format) messages#edit
     message GET    /messages/:id(.:format)      messages#show
             PATCH  /messages/:id(.:format)      messages#update
             PUT    /messages/:id(.:format)      messages#update
             DELETE /messages/:id(.:format)      messages#destroy

Now that we’ve got our controller setup and Rails recognizing the routes to the different pages, the only thing left is to create the views to show to the user. Note the four routes highlighted above that correspond to the index, new, edit, and show actions. Because those routes have a GET verb and prefix associated with them, we’ll have to create views to show when the user tries to get those pages.

Create the views

We need to create views to go with four of the seven actions we defined because those actions are called when a web page needs to be displayed. In creating our forms for entering new messages or updating current messages, we’re going to need a way to display any errors that result. Let’s create a partial for showing error messages at app/views/shared/_error_messages.html.erb:

<% if object.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-error">
      The form contains <%= pluralize(object.errors.count, "error") %>.
    </div>
    <ul>
    <% object.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

Now that we have a way to display our errors, let’s create the view that we’ll show when we want to create new messages. Create a new file at app/views/messages/new.html.erb and enter the following code:

<% provide(:title, "New Message") %>
<h1>New Message</h1>
<%= form_for :message, url: messages_path do |f| %>
  <%= render 'shared/error_messages', object: @message %>

  <p>
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </p>

  <p>
    <%= f.label :message_text %><br>
    <%= f.text_area :message_text %>
  </p>

  <p>
    <%= f.submit %>
  </p>
<% end %>

Notice that we’re making a call to render the error_messages partial we just made and passing in @message as the object. The partial will handle showing all our error messages to the user if they don’t enter a name or message correctly. The form for editing a message is almost identical, so create a file named app/views/messages/edit.html.erb and enter this code:

<% provide(:title, "Update Message") %>
<h1>Update Message</h1>
<%= form_for :message, url: message_path(@message), method: :patch do |f| %>
  <%= render 'shared/error_messages', object: @message %>

  <p>
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </p>

  <p>
    <%= f.label :message_text %><br>
    <%= f.text_area :message_text %>
  </p>

  <p>
    <%= f.submit %>
  </p>
<% end %>

Now that we can create new messages and edit them, we need a way to show them to the user. We’ll make the view for the show action first because it’s very basic. Create a new file at app/views/messages/show.html.erb and enter this code:

<% provide(:title, "Message") %>
<h1>Message</h1>
<div class="row">
  <div class="span4">
    <p>Message by</p>
    <p><%= @message.name %></p>
  </div>
  <div class="span8">
    <p>Message text</p>
    <p><%= @message.message_text %></p>
  </div>
</div>

This will show us individual messages, but we need one place to view all the messages and give us convenient links to edit and delete them. The index action is meant to show all the messages, so we’ll add some links to help in editing and deleting messages. Create a new file at app/views/messages/index.html.erb and enter the following:

<% provide(:title, "All Messages") %>
<h1>All Messages</h1>
<div class="row">
  <div class="span3">Name</div>
  <div class="span6">Message</div>
  <div class="span3">Links</div>
</div>
<% @messages.each do |message| %>
  <div class="row">
    <div class="span3"><%= message.name %></div>
    <div class="span6"><%= message.message_text %></div>
    <div class="span3">
      <%= link_to "Show", message %>
      <%= link_to "Edit", edit_message_path(message) %>
      <%= link_to "Delete", message_path(message),
                    method: :delete,
                    data: { confirm: 'Really delete this message?' } %>
    </div>
  </div>
<% end %>
<% if @messages.empty? %>
  <div class="row">
    There are no messages to show.
  </div>
<% end %>

That takes care of all the views we need to interact with the messages. In order to use the pages, we have to type the path into the browser ourselves, so let’s add links to our header that will make it easier to use. Open the app/views/layouts/_header.html.erb file and change the highlighted lines:

<header>
  <div class="navbar navbar-inverse navbar-static-top">
    <div class="navbar-inner">
      <div class="container">
        <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </a>
        <%= link_to "graffiti", root_path, class: "brand" %>
        <div class="nav-collapse collapse">
          <ul class="nav">
            <li><%= link_to "All Messages", messages_path %></li>
            <li><%= link_to "New Message", new_message_path %></li>
          </ul>
          <ul class="nav pull-right">
            <li><%= link_to "About", about_path %></li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</header>

Now we can easily go to the pages that show us what messages we have, create new messages, and edit or delete our current messages.

Show the graffiti

There’s one last problem that we need to fix. Currently we’re not showing the messages in the form of graffiti that we wanted when we started making the application. We want to show a limited number of messages with the latest messages showing at the top and newer messages pushing older messages off the page. Let’s modify our home page to do this, since it’s the first page the user will see when loading the site. The first change we need to make is to app/controllers/static_pages_controller.rb since we’ll need to know what the latest messages are. Add the following code to the home action:

  def home
    @messages = Message.last(5).reverse
  end

So what’s happening in this line of code? We’re assigning an instance variable to take an array of the last five entries in the Message table and reverse them. This way we should get the latest entry as the first object in the array, which is what we want. Open the app/views/static_pages/home.html.erb file and replace what’s there with the following:

<% provide(:title, "Home Page") %>
<% @messages.each do |message| %>
  <div class="row">
    <div class="span3">
      <div style="text-align:right"><%= message.name %> says:</div>
    </div>
    <div class="span9">
      <div class="well"><%= message.message_text %></div>
    </div>
  </div>
<% end %>
<% if @messages.empty? %>
  <div class="row">
    <div class="well">There are no messages to show.</div>
  </div>
<% end %>

That’s all we need! Now if we reload the home page, we should see our graffiti. If you haven’t already created any messages, create several and see how it changes the output on the home page. There’s one more little flow problem though. I’d like to have it send me back to the home page whenever I create a new message so I can see it showing up on the wall. Open app/controllers/messages_controller.rb and change the highlighted line:

  def create
    @message = Message.new(message_params)

    if @message.save
      redirect_to root_path
    else
      render 'new'
    end
  end

Now when we enter a new message, it sends us back to the home page, and we can easily see older messages dropping off. The only thing left to do is save our work. Open the terminal window and type in:

$ git add .
$ git commit -m "Created model, views, and controller for messages"

That’s it for this tutorial. I would encourage you to think up some similar functionality you might see in a web page and try to create a version of it using what you’ve learned in this tutorial. The source code for this tutorial can be found on Github at:

http://github.com/wordplay/mvc_tutorial

Leave a Reply

preload preload preload