In my previous learning projects I always used a single controller, but now I wonder if that is good practice or even always possible.
In all RESTful Rails tutorials the
Use two current if there are two modules 1] Admin 2] User
Say
class BookUserController < ApplicationController
#Here layout is used will be of layouts/application.html.erb
#Here all the methods which user can will be present
end
class BookAdminController < ApplicationController
layout 'admin' #here we set the layout is layouts/admin.html.erb
end
IF Only one page you want to show admin you can use single controller and two methods
class BookUserController < ApplicationController
layout 'admin', :only=>['index_admin']
def index_admin
end
def index
end
end
OR
class BookUserController < ApplicationController
def index_admin
render :action=>'index_admin', :layout => false
end
def index
end
end
I asked myself this question almost every time when I get a new project. I usually choose one of the two solutions:
1). Single Controller, Single View
I almost never choose this solution now a days unless the project is really simple, and only one or two types of users. If you get multiple user types it is better to use solution # 2. Although this solution may be appealing because you think you save yourself some time by writing less code, but in the end, your controller and view will grow in complexity. Not to mention all the edge cases you have to consider. This usually means bugs.
My company once had to rescue a failed project, it had 3 user types. (admin, business, and member). They used solution #1. The code was in a horrible condition, ( and that's why we were asked to rescue this project) We were jokingly saying it is not MVC, it was MMM. (Model-Model-Model) This is because business logic was not properly extracted and put into models, but spread in controllers and views as well.
2). Multiple Controller, Multiple Views
I use this solution more and more these days. I usually namespace the controllers with user types. For example:
In "app/controllers"
class BookController < ApplicationController
end
and in "app/controllers/admin"
class Admin::BookController < Admin::BaseController
end
I only need to consider regular users when I fill in BookController, and only need to consider admin users when I fill in Admin::BookController
I'm not sure if there are better ways, but this is what I learned from a dozen projects I've done so far...
What I do in such a situation changed quite a bit lately. The current approach is as follows:
I separate controllers based on access requirements. This gives me a clear mental model and a very easy way to check for access control (and test it).
I even go as far as to separate 'your own access' to models into separate controller. I also usually keep the controller's name just put it into a separate namespace.
This approach is also makes it very easy to use standard restuful controller implementations like InheritedResources.
Note that you can reuse many of the views if same functionality is required for different kinds of access.
So I'd have something like this:
### lets start with routes
# this is basically guest access level. you can only list it and see details
map.resources :books, :only => [:index, :show]
namespace :my do |my|
# this will require at least login.
# index and show will be basically same as at the guest level. we can reuse the views
my.resources :books
end
namespace :admin do |admin|
# this will require admin login
admin.resources :books
end
# now the controllers
# this might be just enough of a controller code :). the rest goes into model.
class BooksController < InheritedResources::Base
actions :index, :show
end
module My
class BooksController < InheritedResources::Base
before_filter :require_user
protected
def begin_of_association_chain
# this will force resources to be found on current_user.books.
# so if you go to /my/books/123 and book 123 is not your book you will get 404
current_user
end
end
end
module Admin
class BooksController < InheritedResources::Base
before_filter :require_admin
# this controller is essentially unrestricted. you can get/edit/update any book
# and you can have separate view template for index too
end
end
## Views
# well, this is the part that I like the least in this approach, but
# I think the good outweight the bad.
# I see 2 ways to reuse the views w/o writing lots of ugly customization code for the InheritedResources
# 1) render 'parent' views inside templates. i.e. like:
# my/books/index.html.haml:
!= render :file => "/books/index"
# or just link the templates on the filesystem level with symlinks.
# (or if all of them are the same you can link the directory)
When I need a clearly separated administration area, I tend to go for a solution with two controllers for one resource. An Admin::BooksController in the admin/ sub directory for the admin interface with all actions and a public BooksController that only has index and show methods depending on needs.