1. Technology
You can opt-out at any time. Please refer to our privacy policy for contact information.

An Introduction to Rails: a Quick Blog Part 3

By

An Introduction to Rails: a Quick Blog Part 3
An Introduction to Rails: a Quick Blog Part 3

In part 1 of this series we installed Ruby and generated an empty Rails project. In part 2 we got a (technically) complete blog up and running, even if it was just a scaffold. But in this final part, we're going to make things look a bit more "blogish" and implement some simple security to prevent anyone from making new blog posts.

This is the part where we start to deviate from Ruby development and start getting into HTML. If you want to know more about this, check out the excellent About.com Guide to Web Design. However, don't stray too far, because it's not pure HTML, it's erb.

Authentication

But first, we'll implement an extremely simple authentication method. Open up your Posts controller in app/controllers/posts_controller.rb. Here you'll find every action you can do to a post. The index method, by default, gets all posts and displays them in a table, show shows just one post, and new, edit, create, update and destroy do as described. A typical reader of your blog is only going to need to access the index and show actions, they don't need to access the rest.

So let's hide those actions behind an HTTP authentication password prompt. This is built into Rails, and is generally not how things are done in a final product, for that you'll want a plugin like Devise, with users stored in a database and passwords stored in secure hashed form. You shouldn't be storing passwords in cleartext like this in a final product. This can be done with a single line (split into 3 for readability) at the top of the controller (though still inside the controller class) that we want to protect.


class PostsController < ApplicationController
  http_basic_authenticate_with :name => 'admin',
    :password => 'qwerty',
    :except => [:index, :show]

  # GET /posts
  # GET /posts.json
  def index
    #  …

The http_basic_authenticate_with takes two arguments by default, a :name and :password, these should be self explanatory. The :except keyword argument is an array of actions to not authenticate. Why a list of things not to authenticate? This is referred to a "whitelist," where all actions are automatically protected and only the methods you specify are left open. The security implications of forgetting to list a method in a "blacklist" could be huge. Forgetting to open a method up in a whitelist may break the application, but it won't leave anything open accidentally.

So, once you edit this file to add this line (you don't even have to restart the server), try clicking on the New Post link. You should be seeing an HTTP authentication dialog. Enter the username and password you specified and it should work, enter something else and it won't work.

The Blog Look

OK, let's make this look a bit more like a blog. We'll keep the table for now, and fix something that every blog must have: new posts at the top. Right now the default ordering is used, where things are sorted by ID. This usually means the first created is the first in the list. This is the opposite of what we want. So take a look at the index action in the Posts controller (the same file we edited above). Even if you don't know anything about Rails, it's still pretty obvious what this does. The first line, @posts = Post.all, gets all posts from the database using ActiveRecord (isn't this easier than an SQL query) and stores them in an instance variable called @posts. This instance variable will be available in the view, so this is a way to pass data from the controller to the view (really, the only way).

But we want the reverse of this. There are two ways to do this. The first way is to just use the Ruby reverse method, since what ActiveRecord returns is an Enumerable. So the first line becomes @posts = Post.all.reverse. The second way would be to give the all method an :order argument. This will generate an order_by SQL clause, and is more efficient. But since there aren't many objects here, either will work equally well. Doing that would make it look like @posts = Post.all(:order => "id desc").


  def index
    @posts = Post.all(:order => "id desc")

    respond_to do |format|
      format.html # index.html.erb
      format.json { render :json => @posts }
    end
  end

Great, now they're in the reverse order, let's clean up the view a bit. We don't want that table, we want a blog view with header titles and bodies in paragraphs and such. So we want to open up the index action's view, this is in app/views/posts/index.html.erb. This is an ERB file, something not unlike PHP. Here you'll find HTML mixed with Ruby inside special tags. But there's not much Ruby here, and you typically won't find much Ruby inside of ERB files, it's considered bad form to mix too much Ruby into your HTML. So let's just move some of the HTML around. Take out the table, add in a div and a header and it's starting to look pretty good already.


<% @posts.each do |post| %>
  <div class="post">
    <h1><%= link_to post.title, post %></h1>
    <p><%= simple_format post.body %></p>
    <ul>
      <li><%= link_to 'Edit', edit_post_path(post) %></li>
      <li><%= link_to 'Destroy', post, :method => :delete, :data => { :confirm => 'Are you sure?' } %></li>
    </ul>
  </div>
<% end %>

<%= link_to 'New Post', new_post_path %>

As you can see, this is almost the same exact thing as before, but the table removed and divs put in. The main difference in the formatting here is the header and link to the show action at the top of every post and the call to simple_format. The simple_format method is a helper, it makes plain text into paragraph tags, otherwise we would have to manually parse the blog post and insert our own paragraph tags. Why not simply write in the paragraph tags manually when writing the blog post? By default, all output is escaped. We would actually have to take more steps to take this protection off. Another example of Rails being secure by default, you have to go out of your way to make a mistake.

At this point the blog is looking quite "bloggy." A little bit of CSS could fix this up, right now the blog posts extend over the entire width of the page. Most blogs only display things in the center of the screen. To help this out, we'll edit the layout of the page a little bit. If you'll notice, the view we just edited had no header or body tags, it wasn't the whole thing. Since the scaffold generator didn't generate a specific layout for the Posts controller, it'll be using the application layout in app/views/layouts/application.html.erb. Here you'll see everything that goes around what's rendered in a view. The stuff that gets rendered in a view is the <%= yield %> line, so we'll simply add a header with the name of the blog and a div around that to denote the content. We'll also hack in some CSS to center it all. I say "hack" because this is not typically where CSS goes, but it's good enough for now.


<!DOCTYPE html>
<html>
<head>
  <title>Blog</title>
  <%= stylesheet_link_tag    "application", :media => "all" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
  <style type="text/css">

  #content {
    width: 760px;
    text-align: left;
    margin-left: auto;
    margin-right: auto;
  }
  </style>
</head>
<body>
<div id="content">
<h1>Awesome Blog!</h1>
<hr />
<%= yield %>
</div>
</body>
</html>

And there you have it, a functional blog.

  1. About.com
  2. Technology
  3. Ruby
  4. Ruby on Rails
  5. Ruby on Rails 3 Tutorials
  6. An Introduction to Rails: a Quick Blog Part 3

©2014 About.com. All rights reserved.