Find, Find, Find, Find, I don't think so...
As explained in previous posts, Rails controllers have 7 default actions (index, new, create, show, edit, update, destroy). Four of these seven actions make the same find call, Model.find(params[:id]) and this tutorial is to tidy that up so you’re not repeating yourself over four different actions. To clean this up we’ll just call a before filter:
class ForumsController < ApplicationController
before_filter :find_forum
# Actions go here
private
def find_forum
@forum = Forum.find(params[:id])
end
end
Now you may be thinking, “Why are we doing that? That’s 5 lines!”. Think about if you wanted to change the find statement, and now you’ll begin to picture why. Changing one line is much easier than changing four. For example, if I wanted to find forums by their slugs instead of an ID I would simply change @forum = Forum.find(params[:id]) to @forum = Forum.find_by_slug(params[:id]). Of course, for this to work with the restful routes helpers the way we expect it to (e.g. forum_path(@forum) -> /forums/the-first-forum), we’ll need to re-define #to_param in our model:
class Forum
def to_param
slug
end
end
Common Lookups
Sometimes you’ll have data initialised for your forms and you’ll want to initialise this data multiple times. Instead of repeating yourself like this:
class ForumsController
def new
@forum = Forum.new
@something_special = SomethingSpecial.find(:all, :order => "id DESC")
end
def create
@forum = Forum.new(params[:forum])
if @forum.save
flash[:success] = "A forum has been created."
redirect_to @forum
else
flash[:failure] = "A forum could not be created."
@something_special = SomethingSpecial.find(:all, :order => "id DESC")
render :action => "new"
end
end
end
You could instead have:
class ForumsController
def new
@forum = Forum.new
common_lookups
end
def create
@forum = Forum.new(params[:forum])
if @forum.save
flash[:success] = "A forum has been created."
redirect_to @forum
else
flash[:failure] = "A forum could not be created."
common_lookups
render :action => "new"
end
end
private
def common_lookups
@something_special = SomethingSpecial.find(:all, :order => "id DESC")
end
end
Shorter Routing
One last thing that I’d like to show you is shorter routing. Ever since the restful routing helpers were added, routing to specific controllers and their actions has become easier and easier. Rails 2.0 makes it extremely easy, but first we’ll see how far we’ve come:
- <%= link_to @forum, { :controller => "forums", :action => "show", :id => @forum.id } %>
- <%= link_to @forum, forum_path(@forum) %>
- <%= link_to @forum, forum_path %>
- <%= link_to @forum, @forum %>
- <%= link_to @forum %>
As long as there’s a #to_s method in the Forum model it will insert that as the phrase shown to the user for the link. All of the above should produce the same URL, with the exception of the first which will produce /forums/show/1, and going down the list they’re just shorter ways of writing the same thing. If you had nested routes such as forum_topic_path(@forum, @topic) you could do <%= link_to @topic, [@forum, @topic] %> as the extremely short version of it. The reason why we can’t do just <%= link_to [@forum, @topic] %> is because this will show the to_s version of @forum, followed immediately by the to_s version of @topic.