views:

89

answers:

4

I'm having an issue coming up with a good way to do the following. I have a very generic Org model and User model. Org has_many :users, and User belongs_to :org.

I am trying to find a way of showing a list of users that is not restricted by Org, but also show a list of User's that is restricted by Org. I know I could nest the routes, and just have two different routes like

map.resources :users
map.resources :orgs, :has_many => :users

The problem is that they both go back to the same actions in the User controller. The controller code starts to get very messy because I am having to check for the existence of an :org_id param. Then I have to decide whether to return the normal results of a find call on User, or a find that is scoped to an Org. I'm not sure what the best solution is here, or what the best practice is. If someone with some knowledge on this could please enlighten me, it would be great.

+1  A: 

Not really the answer you were probably looking for on a technical level but I use a plugin for my projects that takes care of that mess for me. Take a look at make_resourceful.

make_resourceful do
 actions :all
 belongs_to :org
end

It will figure out the rest for you, no need to define your standard crud action. It will even detect scoping and scope it for you. (unless that's an other plugin i'm using I forgot about)

Maran
That looks really useful, and it does have a solution for my problem. Thanks a lot! I'm hoping that someone might drop an answer that won't need a plugin though.
ohdeargod
+1  A: 

I use resource_controller plugin for most cases. With it, you just put:

class UsersController < ApplicationController
  resource_controller
  belongs_to :org
end

It works with nested and not-nested resources.

If you don't want to use additional plugin, your controller still won't be very complicated.

class UsersController < ApplicationController
  def index
    @org = Org.find(params[:org_id]) unless params[:org_id].blank?
    @users = params[:org_id].blank? ? User.all : @org.users
    ...
  end
end
klew
+1  A: 

Another way of doing this without a plugin would be to use named_scope. You can create a named scope in User that filters by org_id if it's not empty.

class User < ActiveRecord::Base
  belongs_to :org
  named_scope :by_org, lambda{|org| org.blank? ? {} : { :conditions => ['org_id = ?', org] }}
end

And in the controller just use your named scope. That way if you eventually supply more filter options in the controller you don't need to duplicate them:

class UsersController < ApplicationController
  def index
    @users = User.by_org(params[:org_id]).all
    ...
  end
end
ry
cleanest solutions by far. Thank you!
ohdeargod
A: 

What I usually do is this:

class UsersController < ApplicationController
  def index
    root = Org.find(params[:org_id]) if params[:org_id]
    root = User if root.nil?
    @users = root.all(:conditions => {...}, :order => "...")
  end
end

I'm basically doing a tree-walk. As conditions are added, I simply change the root of the #find call. When I'm done evaluating conditions, I call the final #find / #first / #all method and I'm done.

This also works if you have multiple named scopes:

class UsersController < ApplicationController
  def index
    root = Org.find(params[:org_id]) if params[:org_id]
    root = User if root.nil?
    root = root.named(params[:name]) if params[:name]
    root = root.registered_after(params[:registered_at]) if params[:registered_at]
    # more conditions, as required
    @users = root.all(:conditions => {...}, :order => "...")
  end
end
François Beausoleil