Subscribe to RSS

Build an app with Ruby on Rails resources

Resources have been introduced in Rails 1.2. Basically this allows your application to expose content in different formats. Normal users with browsers get a nice HTML page while applications get easy to use XML code. Besides that Rails implements HTTP requests as GET, POST, PUT and DELETE.

All well, but how can you put this to your advantage when developing a Ruby on Rails application?

This article shows you how you can use Rails Resources as building blocks for your application.

Let’s say you need to develop a simple portal application with news items and events. The news should have focus on the frontpage, but events should also be accessable on the frontpage.

Before I do anything more, I’ll create the a new rails project and use an sqlite3 database for ease of use.

$ rails --database=sqlite3 portal

Creating the building blocks

To start with the news I generate a scaffold_resource named article. This will give me an articles controller and an article model. This is the first building block.

$ ./script/generate scaffold_resource article title:string body:text

Before you migrate your database, edit the migration in db/migrations/001_create_articles.rb:

class CreateArticles < ActiveRecord::Migration
  def self.up
    create_table :articles do |t|
      t.column :title, :string, :null => false, :default => "", :limit => 100
      t.column :body, :text, :null => false, :default => ""
 
      t.column :created_at, :datetime
      t.column :updated_at, :datetime
    end
  end
 
  def self.down
    drop_table :articles
  end
end

Next we create the second block named ‘event’.

$ ./script/generate scaffold_resource event title:string happens_on:date description:text

and the migration is edited to look like this:

class CreateEvents < ActiveRecord::Migration
  def self.up
    create_table :events do |t|
      t.column :title, :string, :null => false, :limit => 100, :default => ""
      t.column :happens_on, :date, :null => false
      t.column :description, :text, :null => false, :default => ""
 
      t.column :created_at, :datetime
      t.column :updated_at, :datetime
    end
  end
 
  def self.down
    drop_table :events
  end
end

Migrate your database next:

$ rake db:migrate

You may now start your webserver and enjoy all the scaffold goodness. You can CRUD articles and events now! Everything is still in scaffold from, but it’s a good start.

Adding some routing magic

You’ll never display all news items on your frontpage. You probably want the ten latest items and the newest first. For this to integrate nicely with the article resource we’ll add a new route and a simple method to the articles controller.

In app/controllers/articles_controller.rb add the following method:

  def latest
    @articles = Article.find(:all, :limit => 10, :order => "created_at DESC")
 
    respond_to do |format|
      format.html # latest.rhtml
      format.xml  { render :xml => @articles.to_xml }
    end
  end

and in config/routes.rb you should replace

map.resources :articles

with

map.resources :articles,
  :collection => {
    :latest => :get
  }

You may also want to create a file named app/views/articles/latest.rhtml. This may, for now, be a copy of app/views/articles/index.rhtml.

Now you can access http://localhost:3000/articles;latest to get the ten latest articles, newest first.

Let’s do the same for events, but in this case we want the five next events in our database.

app/controllers/events_controller.rb:

  def upcoming
    @events = Event.find(:all, :limit => 5, :order => "happens_on", :conditions => ['happens_on >= ?', Time.now])
 
    respond_to do |format|
      format.html # upcoming.rhtml
      format.xml  { render :xml => @events.to_xml }
    end
  end

config/routes.rb:

  map.resources :events,
    :collection => {
      :upcoming => :get
    }

Again, copy app/views/events/index.rhtml to app/views/events/upcoming.rhtml for now. http://localhost:3000/events;upcoming now shows the next five events that are going to happen.

Make things more sexy

Okay, now let’s spice up the latest.rhtml and upcoming.rhtml views. You may copy/paste the following:

app/views/articles/latest.rhtml:

<h1>Latest articles</h1>
 
< % for article in @articles %>
<div class="article">
	<h2>< %= link_to article.title, article_url(article) %></h2>
	<p>< %=h article.body %></p>
</div>
< % end %>

app/views/articles/upcoming.rhtml:

<h1>Upcoming events</h1>
 
<ul>
< % for event in @events %>
	<li>< %= event.happens_on.strftime("%e %b %Y") %> &mdash; < %= link_to event.title, event_url(event) %>
< % end %>
</li></ul>

This will make those special collections look a bit nicer.

Putting it all together

Now you have your building blocks ready, start building! I prefer to create a special controller that handles special pages like the frontpage.

$ ./script/generate controller pagemill

Add a simple method to the controller to show a frontpage. All the magic will happen in the view, so you controller basically looks like this:

class PagemillController < ApplicationController
  def frontpage
  end
end

Now create a file app/views/pagemill/frontpage.rhtml

<div style="float: right; border: 3px solid silver; padding: 10px; margin: 10px;">
	< %= render_component :controller => 'events', :action => 'upcoming' %>
 
 
<h1>Portal Frontpage</h1>
 
<p>Some standard talk about this portal goes here.</p>
 
< %= render_component :controller => 'articles', :action => 'latest' %></div>

Only two things to do, first remove the default public/index.html

$ rm pubic/index.html

and add a default route in config/routes.rb

map.connect '', :controller => "pagemill", :action => "frontpage"

Enjoy your hard work

You can now enjoy your hard work by surfing to http://localhost:3000.

What’s next?

There’s still a lot to do:

  • Add some more style and colours (use the CSS, Luke)
  • Change the templates I specified here to suit your needs.
  • Include additional information for your articles and events.
  • Change the templates when viewing an article or event.
  • Add some basic navigation
  • Protect your site by adding users and permissions

Too lazy to copy/paste?

You may download this rails project. I’ve used Rails 1.2.2 on Mac OS X and SQlite3. Of course you can use other databases like MySQL if you like.

Please share the love of this post by bookmarking it, and sharing it with others. Thanks!

  • Digg
  • del.icio.us
  • description
  • Reddit
  • Technorati
  • BlinkList
  • E-mail this story to a friend!
  • Facebook
  • Live
  • MisterWong
  • Netvouz
  • NewsVine
  • Slashdot
  • SphereIt

4 Comments

  1. Dave Porter
    Posted 24 March, 2007 at 04:58 | Permalink

    Hi there,

    Just working through the article and found two things:

    1. Typo on the two migration rb files - missing | after t ( |t shoud be |t| )
    ( not in downloaded zip file ! )

    2. I had to take out the default value for the body and description text fields to allow rake db:migrate work.

    cheers, Dave

  2. Dave Porter
    Posted 24 March, 2007 at 05:12 | Permalink

    Same problem here:
    respond_to do |format
    ( missing closing | )
    Dave

  3. Posted 24 March, 2007 at 08:56 | Permalink

    @Dave: This may be a rendering problem in your browser. The trailing pipes (|) are there. (You may verify it by looking at the page code).

    The snippets here are one-on-one copies from the provided source code.

    The rendering problem often occurs when scrolling.

  4. Dave Porter
    Posted 24 March, 2007 at 11:53 | Permalink

    Ah - just realised….

    The trailing pipe disapears when I click:
    ‘Plain Text’ & then highlight everything.

    This happens in FF2 & IE7

    cheers, Dave
    p.s. Any idea why the default values for the text field fails ?

2 Trackbacks

  1. By Ruby Brasil - » Montando uma aplica on 21 March, 2007 at 17:22

    [...] um artigo bem detalhando mostrando como montar uma aplica

  2. By sideline.ca » In Search of REST on 5 April, 2007 at 05:32

    [...] Build an app with Ruby on Rails resources [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*