DRY up controllers with params_to_objects
Posted: September 6th, 2008 | Author: Daniel Higginbotham | Filed under: Programming, Rails | 6 Comments »The following allows you to get rid of the numerous “Model.find(params[:id])” calls in your controllers. I’m pretty sure I’ve seen similar solutions out there, so if anyone wants to link to those in the comments that’d be helpful.
# Example
class BooksController < ApplicationController
params_to_objects :library, :book
def show
# if this method corresponds to /libraries/{id}/books/{id}
# then @book and @library are automatically created
end
end
# Put the following in ApplicationController
class << self
# you can use the *_filter options of :only, etc
# also, if you have namespaced model, you can use
# params_to_objects "namespace/model"
def params_to_objects(*names)
options = names.reject{|o|!o.is_a? Hash} || []
names = names - options
first_name = names.shift
params_to_object(first_name, :id, options)
names.each do |name|
safe_name = name.to_s.gsub("/", "_")
params_to_object(name, "#{safe_name}_id", options)
end
end
def params_to_object(name, id, options)
safe_name = name.to_s.gsub("/", "_")
module_eval <<-"end;"
def set_instance_variable_#{safe_name}
@#{safe_name} = "#{name.to_s}".classify.constantize[params['#{id.to_s}']] if params['#{id.to_s}']
end
end;
before_filter "set_instance_variable_#{safe_name}".to_sym, *options
end
end

A question about ruby.
Why did you put a class << self in application controller? Why not just put the function definition there and let it be inherited?
I like the (quite different) approach in
http://stephencelis.com/archive/2008/9/rails-controllers-views-and-variables
Like the author, I’ve never liked using instance variables in the way that idiomatic Rails does. I haven’t tried it myself yet but I’d be interested to see what you think.
make_resourcefulwill also do this for you, if you use it and put abelongs_toinside themake_resourceful doblock.Thanks for the tip Daniel. I thought I had seen make_resourceful before but couldn’t remember the name of it.
@Mike – Stephen’s solution is neat, but personally I think it’s actually better to use instance variables. In my code I mostly use instance variables for active record objects, whereas I use methods galore. The little @ thus helps me find the active record objects quicker. I’m also biased towards using instance variables because I was introduced to Ruby through Rails, so my brain considers that the “right” way to do things.
There’s one more thing that’s hard for me to explain… instance variables have a more static “feel” to them, even a kind of solidity that free-floating methods don’t. Wish I could explain better than that :)
@nobody – the short answer is that the
params_to_objectsmethod needs to be a class method.params_to_objectscreatesbefore_filter‘s on the fly.before_filterneeds to be able to apply to more than controller action (method), and for it to do so it needs to be a class method. I hope this answers your question – I’m not sure how familiar you are with Ruby, so I’m not sure what level of detail to get into.Thanks for the explanation. The clears it up. I am a ruby newbie and I get pretty confused with meta programming especially inside of rails.