Learn Rails - Model, View, Controller

Question Click to View Answer

In this quiz, we will make a RESTful MVC without using any Rails generators other than the migration generator. Start by creating a new Rails application.

$ rails new restful_mvc

Generate an empty migration to make the posts table.

$ rails generate migration CreatePosts

Update the migration file, so the posts table will have body and user_id columns when it is created. Then create the table in the database.

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.text :body
      t.integer :user_id
      t.timestamps
    end
  end
end

From the terminal run:

$ rake db:migrate

Create the post model and make it so that only the body attribute can be updated with mass assignment.

Create the file post.rb and put it in the models directory. Update it with the following code:

class Post < ActiveRecord::Base
  attr_accessible :body
end

Why do we want to make it so only the body attribute can be updated with mass assignment? What does mass assignment mean?

Mass assignment is when a user passes a hash of values that are stored in the database. We don't want to let users update values that they should not be updating to prevent malicious hacking. In this case, we want our controller to set the user_id attribute (not the user), so user_id should not be attr_accessible.

Add a validation to the model to check that there is a post body before saving to the database.

validates :body, :presence => true

Alternate solution:

validates_presence_of :body

Add a validation to the model to check that the post body is at least 5 characters before saving to the database.

validates :body, :length => { :minimum => 5 }

Create the posts controller file and add the new action.

Add the posts_controller.rb file to the controllers directory and add the following code:

class PostsController < ApplicationController
  def new
    @post = Post.new
  end
end

Create the posts view file for the new action. The code in the view corresponding to the new action should render a partial called "form".

Add the posts directory as a subdirectory of the views directory. Add the new.html.erb file to the posts directory (the file path is app/views/posts/new.html.erb). Add the following code to the new.html.erb file:

<%= render 'form' %>

Add routes to access the RESTful actions of the posts controller.

Add the following code to config/routes.rb:

resources :posts

Create the form partial that will be used to make new posts.

Add the following code to views/posts/_form.html.erb:

<%= form_for @post do |f| %>
  <%= f.label :body %>
  <%= f.text_field :body %>
  <%= f.submit %>
<% end %>

Add the create action that will save the information from the form partial to the database. Set the user_id attribute equal to 1 before saving to the database.

In controllers/posts_controller.rb:

def create
  @post = Post.new(params[:post])
  @post.user_id = 1
  if @post.save
    redirect_to @post
  end
end

Create a show action and view that will display the body of the post.

# controllers/posts_controller.rb:
def show
  @post = Post.find(params[:id])  
end  

# views/posts/show.html.erb:
<%= @post.body %>

Create a numbered list that shows post body for each post. Make sure there are a few posts in your database for this step.

# posts_controller.rb:
def index
  @posts = Post.all    
end  

# views/posts/index.html.erb:
<ol>
<% @posts.each do |post| %>
  <li><%= post.body %></li>
<% end %>
</ol>

Update the posts controller so posts can be edited and add an edit link to the post show page.

# posts_controller.rb:
def edit
  @post = Post.find(params[:id])    
end  

def update
  @post = Post.find(params[:id])
  if @post.update_attributes(params[:post])
    redirect_to @post
  end
end

# views/posts/edit.html.erb:
<%= render "form" %>

# views/posts/show.html.erb:
<%= link_to 'Edit', edit_post_path(@post) %>

Make an action so posts can be deleted and add a delete link to the post show page.

# posts_controller.rb:
def destroy
  @post = Post.find(params[:id])
  @post.destroy
  redirect_to posts_path
end

# views/posts/show.html.erb:
<%= link_to "Delete", post_path(@post), :method => :delete, :confirm => "Are you sure you want to delete the post?" %>