August 5, 2007...9:53 pm

DRY-ing your Rails controller with filters

Jump to Comments

Ever noticed how all CRUD methods on each controller starts with the same line: @item = Model.find(params[:id]) ?

Hey! why not use filter so we don’t repeat ourselves ?


class PostsController < ApplicationController
  before_filter :find_post,  :o nly => [:show, :edit, :update, :destroy]

  def index
    @posts = Post.find(:all)
  end

  def show
  end

  def new
    @post = Post.new
  end

  def edit
  end

  def create
    @post = Post.new(params[:post])

    if @post.save
      flash[:notice] = 'Post was successfully created.'
      redirect_to post_url(@post)
    else
      render :action => "new"
    end
  end

  def update
    if @post.update_attributes(params[:post])
      flash[:notice] = 'Post was successfully updated.'
      redirect_to post_url(@post)
    else
      render :action => "edit"
    end
  end

  def destroy
    @post.destroy

    redirect_to posts_url
  end

  private
    def find_post
      @post = Post.find(params[:id])
    end
end

The cool thing about this is, if you decide to switch to using model name in the url, you’ll only have one line to change!

It’s a small step for Rails, but a great step towards honouring the DRY principle:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
- The Programatic Programmer

DRY-fulness we salute you!

8 Comments

  • had a quick look at the link, i think it’s better to use params[:name] directly, ie Model.find_by_name(params[:name]).

    i think site.com/posts/#{id}-#{permalink} is ugly

    of course, you will have to make strings url-safe somewhere, but the result is much better for me

  • Heri: you cant use “name” unless you are sure that it is going to be unique AND doesnt have any of the forbiden caracters (spaces, comma, etc). If you make your “name” field URL safe, you lose a lot of flexibility in its use. Hence the reason to use permalink.

    Personnaly, I prefer to use:

    site.com/posts/#{permalink}

    when I know that something is going to be unique because it looks better in the URL.

    Another use of the before filter is to know if the user is the owner of the content display in the page when it gives him some special rights. I use something like:

    @owner = @post.user.id == current_user.id ? true : false

    Before filters are great, but when you use more than one, you have to be careful of their order.

  • I was talking about using site.com/posts/#{permalink} in fact.

    Checkout http://svn.techno-weenie.net/projects/plugins/permalink_fu/ for escaping the permalink field.

    Because if you go with #{id}-#{permalink} you don’t even need to change your finder. to_i will truncate the last part.

    Thanks for the comments and extra tips Heri and Alain!

  • [...] DRY Resources Once again, I’ll be responding to one of Marc’s great posts. Today, he’s talking about keeping your controllers DRY, using [...]

  • [...] Colorize your code in your blog posts (with Ruby of course) Published August 10th, 2007 ruby A couple of people asked me how I highlighted my code in a previous post. [...]

  • Hey there, one way around making it safe is doing something like a rescue (that is, if you don’t like showing users error pages). I personally prefer using an around filter:

    around_filter :do_something

    protected

    def do_something
    if Model.find_by_name(params[:name]) rescue nil
    yield
    else
    redirect_to :action => :error
    end

    end

  • Wow, nice tip Ikai, I really like it better then the rescue_action_in_public aproach. I haven’t found any usage for around_filter before.

    Thanks for sharing this!

  • I follow your blog for a long time and must tell you that your articles are always valuable to readers.


Leave a Reply