how to have many controllers under one admin page?

While many people dislike the concept of putting admin controllers into their own namespace, I don’t. So I’ll let you in on how it’s done along with some handy routing tricks I’ve picked up along the way.

  1. You’ll need to create the controller with script/generate controller Admin/Whatevers [where Whatever is the controller name.] which creates the controller in the subdirectory [app/controllers/admin]. It also puts it into the namespace Admin, the fullname being Admin::WhateverController.

  2. I find it’s easiest to go ahead and create another controller Admin::CommonController [created by script/generate controller Admin/Common] and have all the Admin controllers descend from it. That way you can have a common layout defined once in Admin::CommonController and it trickles down instead of adding it manually to each controller. It also can serve as a holding place for common code. For example, in Admin::CommonController…

def go_home
redirect to :action => :index

That’s something you type a lot. As a function in Admin::CommonController, now all the descendant controllers have access to it and you can write “go_home and return” instead of “redirect_to :action => :index” over and over. I find this to be one of the biggest benefits to using an Admin:: namespace.

  1. Anyhow, now about routing. If you’re using REST-y routing then there’s a super awesome trick to keep the named routes for admin controllers from conflicting down the road with non-admin controllers as well.

map.with options :name_prefix => “admin_”, :path_prefix => “admin/” do |m|
m.resources :whatevers, :controller => “admin/whatevers”

The benefits are more obvious when there’s a handful of controllers, natch! A heads ups though… Named routes will obviously have admin_ prefixed to them. This creates routes like admin_whatevers_url and the kinda oddly named admin_new_whatever_url instead of the more English new_admin_whatever_url. Just remember that the name_prefix truly is a prefix.

3a. I usually also define a method “index” on Admin::CommonController that redirects to the actual controller you want as a default admin controller for logins. That way "
" will get routed to “”. You will have to add a route [map.admin “/admin”, :controller => “admin/common”, :action => “index”] for it as well. Of course, you don’t have to redirect. You could use it as a dashboard of sorts. Do whatever there. You’re the boss. But usually [because I’m trying to embrace the CRUD and REST] I’m redirected to another controller.

3b, If you’re not all RESTed up [and why not??], you can still use that map.with_options to DRY some of the routing.

map.with_options :controller => “admin/whatevers” do |m|
“admin/whatevers”, :action => “index”
m.admin_new_whatever “admin/whatevers/new”, :action => “new”
# And so on…

I’m a big believer in the named routes and not using the :controller/:action/:id default, which can inadvertently expose methods you might not want exposed all the time. Though you should be using “private” to protect those kinds of methods anyhow. But the named route benefits are reason enough.

Anyhow, I hoped that helped.


Just making sure I pointed out that [for example]
Admin::CategoriesController needs to descend from
Admin::CommonController and not ApplicationController

class Admin::CategoriesController < Admin::CommonController # in admin/categories

and admin/common does descend from application

class Admin::CommonController < ApplicationController