Take the 2-minute tour ×

I have an application that scopes everything under a Project. All the views require the project to be available. This allows me to have a current_project method:

current_project = current_user.projects.find(params[:project_id)

While this is convenient, it means my routes always have to be scoped under project, and I can't use shallow routing. This can be nasty when you have four levels of nesting.

resources :projects do
  resources :posts do
    resources :attachments
    resources :comments do
      resources :attachments

This much nesting means complicated routes and complicated tests:

form_for [@project, @post, @comment, @attachment] ...

It also means a lot of duplication since polymorphic models like attachments must work for posts and comments that are nested at different levels.

form_for [@project, @attachable, @attachment] # ... for posts
form_for [@project, @post, @attachable, @attachment] # ... for comments

So I switched to using shallow routes. It's such a relief, but this has another nasty side effect:

The current_project is no longer easily attainable. To get it for an Attachment, for example, I need to go through three parents:


And since all the views need it, I need do this in almost every action.

@project = @post.project
@project = @comment.post.project
@project = @attachment.comment.post.project

Is there some middle ground where I can have a convenient current_project method without having to nest my routes 4 levels? I don't want to use subdomains either.

share|improve this question
is it too obvious to suggest adding methods so you can do @comment.project and @attachment.project? –  tokland Feb 20 at 17:35
Is the :project_id param available after logging in ? You could use it to persist it in the session and create some hook in your ApplicationController that brings an instance variable containing the Project object found through the id. Do you think it could be done in your scenario ? I could provide an answer pointing to this process. –  Guilherme Franco Feb 20 at 17:36
@GuilhermeFranco No, it's not. They are usually greeted with a list of projects that they usually select from. –  Jumbalaya Wanton Feb 20 at 17:55
@tokland that's one way to do it, but it doesn't solve the problem. It just hides it. I'm still reaching through three models, but I've hidden the implementation instead. –  Jumbalaya Wanton Feb 20 at 17:56
@GuilhermeFranco suppose I made the :project_id persist in the session, what answer did you have in mind? –  Jumbalaya Wanton Mar 7 at 13:25
show 1 more comment

3 Answers

Looking at this more, i think it would be better to save current_project_id into the users table, so that when someone switches project you update their record. Then, your current_project method is just

def current_project
  @current_project ||= current_user.current_project

You'll need to add

belongs_to :current_project, :class_name => "Project"

and, on any page load, you always know which project you're dealing with, just by looking in the users table with this method.

share|improve this answer
Thanks for this suggestion. I think I'll try it. I'm not sure how it plays out with friendly forwarding, or automatically accessing a link via the address bar. –  Jumbalaya Wanton Mar 7 at 13:23
add comment
#in app/controllers/application.rb
def current_project
  unless @current_project
    @current_project = (@post && @post.project) || (@comment && @comment.post.project) || (@attachment && @attachment.comment.post.project)

I use the instance variable @current_project inside the method so that if current_project is called multiple times in a single action/render it only has to do the calculation once: the subsequent times it will just read it out of @current_project.

share|improve this answer
I have to say, that's not ideal, but at least it puts everything in one place. That said, this method requires you add new logic for each object... let me mull it over. –  Jumbalaya Wanton Feb 20 at 17:58
add comment

I would recommend adding a before_action that loads @current_project to each controller.

share|improve this answer
I'm already sort of trying that. I was hoping for something cleaner. –  Jumbalaya Wanton Feb 20 at 22:29
add comment

Your Answer


