On Mon, Mar 2, 2009 at 11:35 PM, Stephen Eley <sfe...@gmail.com> wrote: > On Mon, Mar 2, 2009 at 5:16 PM, Zach Dennis <zach.den...@gmail.com> wrote: >> >> Forgot to mention what we did do. We ended up with the following... >> >> def index >> if user.has_role?("admin") >> user.in_role("admin").invoices >> elsif user.has_role?("associate") >> user.in_role("associate").invoices >> else >> raise AccessDenied >> end >> end > > That seems sort of backwards to me. These aren't the user's invoices, > right? They're just invoices which the user happens to be allowed to > see? Chaining it this way makes it look like the invoices *belong* to > the role, and seems put the user up front instead of the invoices.
I agree, it could be better. > You also have conditional branching with hardcoded values, making the > controller brittle, and some redundancy with the controller asking the > model for a value and then passing the value right back to the model. I would like to push down the conditional logic to a lower part of the app and there probably is a way that I haven't found yet with the technique I'm using. Right now I'm exploring some trade-offs. Put more emphasis on the responsibility of a role or on the responsibility of the model? I've been down the model route, and would like to see where the role route takes me. :) > > Can a model have more than one role? If it can, you have a problem > here because the 'if' will only ever *act* on one role. If it can't, > life gets simple: > > [controller] > def index > @invoices = Invoice.by_role(user) > rescue AccessDenied > flash[:warning] = "Nope. Sorry. Nice try." > redirect_to :back > end > > [Invoice model] > def by_role(user) > case user.role > when "admin" > [whatever] > when "associate" > [whatever] > else > raise AccessDenied > end > end > > ...That could probably still be made more elegant. I'm not a huge fan > of case statements, and I have in my head some idea that you could > have named scopes for each role and use "send" to pick the right > scope. But that could be overdesigning it, and in any case I don't > really know what each role has to return in your business case. I've been down this path many times. It has worked well when the privilege/role set was limited and fairly simple, but seems to leave models convoluted for anything else. That's what sparked me to explore concretely identifying the Role in my app, and allowing it to make decisions that are unique to it, rather than sprinkling them throughout the models. > > The important takeaway here is that the Invoice is responsible for > figuring out what to return, based on user data passed to it at > runtime; the User doesn't have to have special logic to know how to > query every other model in the system. To clarify, the User doesn't know how-to query anything. All it knows is if it can fulfill a Role. If it can it returns the appropriate role object. Each role is responsible for knowing what it can access, but it doesn't do the nitty gritty work. It calls methods on other models. For the Invoice example, the associate role calls Invoice.by_area(user.area), whereas the Admin role calls Invoice.all in each of their respective #invoices methods. Some of what has piqued my interest in exploring apps that place more emphasis on Roles and Privileges is that it it's difficult to understand what it means to be an admin when what it means to be able to to be an "admin" or "supervisor" or a "team supervisor" or an "employee" is sprinkled throughout the app. So far I have enjoyed having the responsibility of an admin in one spot, even though that Admin object doesn't deal with the details. It just knows how-to make a decision and then it tells another object to do it. It's kind of like inserting a thin-layer of roles and privileges between the application layer and the domain layer. I'd say these sit on the top end of the domain layer. As you pointed out earlier, the API for #in_role could use a little love. -- Zach Dennis http://www.continuousthinking.com http://www.mutuallyhuman.com _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users