Saturday, December 09, 2006

Pimping a Controller

I've recently started working on a REST based rails project. I've been thinking about the Rails model associations, and in particular has_many :through, which is used to help expose the join between two models. Given the following three models:



class User < ActiveRecord::Base
has_many :permissions
has_many :roles, :through => :permissions
end
class Permission < ActiveRecord::Base
belongs_to :role
belongs_to :user
end
class Role < ActiveRecord::Base
has_many :permissions
has_many :users, :through => :permissions
end



I want to be able to look at permissions in a few different ways (and roles and users for that matter). Now, I can modify my PermissionsController to set the object that will be used for all find calls. By default, the PermissionsController will use Permission.find, but if I swap the Permission object for @finder_object, I can set that @finder_object before_filter. See below:



class PermissionsController < ActiveRecord::Base
before_filter :determine_finder_object

def index
@permissions = @finder_object.find(:all)
# respond to stuff, though you might want to use different templates
# based on the finder object
end

def determine_finder_object
if params[:user_id]
@finder_object = User.find(params[:user_id]).permissions
elsif params[:role_id]
@finder_object = Role.find(params[:role_id]).permissions
else
@finder_object = Permission
end
end
end



Now, is this the best practice? I don't know, however, it feels like its something to explore. One noticeable caveat is that if you are using a REST-based controller, you will want to look at all of your Object.new calls and think about replacing them with @finder_object.build.

As an aside, the above controller would probably end up with several chunks of code that were:


if params[:user_id]
#do something for users
elsif params[:role_id]
#do something for roles
else
#do something in the generic
end


What can I do to encapsulate this particular logic? I dug up the code from the :respond_to controller instance method, and created a construct for:



determine do |d|
d.user_id { #do something for users}
d.role_id { #do something for roles}
end



I'm not satisfied with it, but am wondering what I might be able to do.

No comments: