This has always bothered me and the best answer I've come up with is
"It depends"! There are at least three, but probably more, scenarios.
1. A "Basecamp" style application where you log in to an account (that
has people/user, invoices, companies, venders etc). Each account
appears to have it own database. How do you keep Foo's account from
seeing Bar's invoices? You must have a belongs to relation on (at
least) each major path. If you have a deeply nested structure, but use
the sane approach of shallow routing you have to deal with things like
"/invoices/2342" in the url and make sure that invoice ID 2342 belongs
to a customer that belongs to your account. That leads me to believe
that you have to have a before filter on almost everything that checks
that resource belongs to that account. That leads to the question on
what is the best way to handle that (which is one way your question
could be interpreted).
Do make it easy on myself and put account_id in EVERY resource?
(before filter has what it needs - I have account_id in current_user)
Do I put account_id in in the root of every major nested path and get
the root resource for any query on a child resource? (before filter
can get what it needs)
Getting the related resources is fairly easy
(current_user.account.companies or @account.companies), but that does
not stop someone from modifying the id in the url.
2. An "Accountant" type application where the user can access all the
accounts. This is probably not much different than the "Basecamp"
approach, it is just a super user that probably does not want Foo's
invoices mixed with Bar's invoices (but may want the option - which is
3) and start with account. Url modification is probably not a concern
since they have access to everything anyhow.
3. The account relations are there, but everything is mixed together
and you need filters or scopes to separate them when they need to be
This probably didn't answer your question (or mine), but the before
filter is probably the key - it controls what rules you are trying to
apply. Now we just have to get an answer to the second part of your
question. What is the most efficient way?
I still don't know what the best approach is for the "Basecamp" type
@resources = current_user.account.resources # for index or get many
@resource = current_user.account.resources.find(params[:id]) # for
@resource = Resource.find(params[:id])
if !@resource.account_id?(current_user.account_id) # some method in
model that compares resource account_id = users account_id
redirect_to "Unauthorized access"