Wednesday, August 8, 2012

Fetch the record

I've been working a lot with very standard controllers lately, with all the usual CRUD actions, and I've been implementing some useful before_filter methods to DRY things up. Mostly authentication methods, but also for fetching the record, instead of calling it in the various actions.
@user = User.find(params[:id])
So I created a simple method, stuck it in application_controller.rb, and thinned out my controller a bit more.

But then, once my apps got bigger and more complex, I found my application controller was getting chubby with record fetchers! So I started to look for a solution, and here is what I came up with. Create a single fetcher method and have it accept an argument to tell what model to refer to. This is how it looks at the moment:
  def fetch_record(record)
    begin
      eval "@#{record} = #{record.capitalize}.find(params[:id])"
    rescue NameError #raised when a non-existing model is called.
      render file: 'public/500.html', status: 500
    end
  end

The eval function is quite useful in this case, executing the string after it is evaluated.
A problem that arises is, how do you call a before_filter method with arguments?
Well, after digging around for a while, I found the solution is using a proc:
before_filter { |controller| controller.send(:fetch_record, "user") }
This will call that fetch_record method and send "user" as a parameter. However it will do so for every action, and that's not good.
The method before_filter itself may also receive a hash of options such as :only and :except, to restrict which actions will execute the called method. But in this case, the hash must be passed a little differently - in parentheses, before the proc:
before_filter(only: [:show, :edit, :update, :destroy]) { |controller| controller.send(:fetch_record, "product") }

I'm quite happy with this improvement so far, but I would be glad to hear if there are better practices out there you guys have seen or came up with.

No comments:

Post a Comment