Optionally Nested Controllers
Published 16 December 07 by Justin French
I’ve seen this asked a few times in #rubyonrails and #roro IRC channels, so rather than knock up a code example every time, maybe I can just point people here.
The Problem
Some people like nested resource URLs like /people/21/images/45, instead of flat URLs like /images/45, and some people like to have both accessible as options. Rails’ Routing can do this easy. Some people consider these two different resources, so they’ll have an ImagesController and a PeopleImagesController. Others think that’s horribly repetitive for what they’re doing with their code, so they want it all to go through one ImagesController with an optional parent object or scoping.
This is for those people, scratching their heads, wondering how that’s done cleanly.
A Solution (though most certainly not the only solution)
class ImagesController < ApplicationController
def index
@images = parent.find(:all)
end
def show
@image = parent.find(params[:id])
end
def new
@image = parent.new
end
# ...
protected
def parent
params[:person_id] ?
Person.find(params[:person_id]).images :
Image
end
end
But what does it do?
Given the URL /people/24/images/..., params[:person_id] will be set. If it's found @parent returns an images collection scoped to that person upon which we can perform our find, new, create (etc) methods on. If it’s not present, we just do them directly on the Image class.
It’s scoped to the person if there is one in the URL, and isn’t if there isn’t.
There are many ways to tackle this kind of problem, and plugins like make_resourceful do similar things and are a great kick in the right direction.
Before you go…
Here’s some links to my most popular posts: