Reputation: 1239
My model holds expenses, including user and project references..
class Expense < ActiveRecord::Base
attr_accessible :amount, :expense_date, :description, :project_id, :user_id
belongs_to :project
belongs_to :user
end
The ExpensesController handles basic CRUD operations for the expenses.
I now am needing to build an administrators version of this same page, a new view preferably, which can include the different views of the data, by user, by project, etc, and can also edit data that the user cannot.
My question is: Do a build a second controller to handle the administrative perspective of the data, -- or do I setup conditions inside of every method, to detect the originating view and form, and then conditions to redirect them back to where they belong?
If I do build a second controller, how do I properly setup the form_for so that it knows what controller to go to?
Thanks!
PS - If anyone has any books about how to properly put together a rails app, I feel like I know the pieces ant parts, but I'm getting stuck on the big picture implementation. I learned rails with Michael Hartl's guide, prior to that I was a PHP developer.
Upvotes: 2
Views: 865
Reputation: 457
IMHO, if security is a big concern for your app then using an admin namespace and separate controllers is the best way to make sure you don't leave any gaps. It's also just simpler and lower stress.
I would have a directory structure like so:
/app/controllers/application_controller.rb
/app/controllers/admin_controller.rb - inherits from application_controller
/app/controllers/expenses_controller.rb - non-admin, inherits from application_controller
/app/controllers/admin/expenses_controller.rb - inherits from admin_controller
Your views would be similarly separated/duplicated:
/app/views/expenses/* - non-admin expenses views
/app/views/admin/expenses/* - admin expenses views
In application_controller you'd put the Devise methods to authenticate_user and CanCan method to check_authorization (which throws an exception if authorization is not checked at some point in the controller action). In admin_controller you have more strict filters to make sure the user is an admin. Then you can get even more fine-grained in the specific controllers and their actions.
Of course each controller only has to define the actions it really needs and you don't have to duplicate views. Maybe the non-admin expenses_controller has index, show, new, create, while the admin one has only edit, update, and destroy. Then in the 'show' view you'd still have code that add links to the 'edit' action if the user is an admin.
Edit - Routes
With the above example, your routes.rb would look something like:
resources :expenses, :only => [:index, :show, :new, :create]
namespace :admin do
resources :expenses, :only => [:edit, :update, :destroy]
end
So you still use expenses_path()
for the index and expense_path(foo)
for show. A form on the admin page, however, would post to admin_expense_path(@expense)
.
Upvotes: 3
Reputation: 913
You can use Devise gem for authentication. You can create somekind of "namespace" for administrative part of your application.
I my opinion, creating another controller depends on how many views and actions your admin will access.
About the doubt about form_for action, it will be managed by your routes and paths that you configure in your form_for params.
Upvotes: 1
Reputation: 245
If you wish to add another Controller I would suggest having common code in a module and importing them in each Controller. Each controller would also have an adequate before filter to check for adequate rights.
But I would prefer having one controller and one set of views since I think it avoids code duplication and/or confusion. Pass the user as a local variable to the view and check for administrator rights when you need to decide the URL for the form_for or whether to hide or show some part.
If the views differ significantly, check for admin rights in the controller and render either the user view or the admin view.
You can even create a special partial for the admin part of the view and decide whether to render it in the view or not, sending the appropriate data in the params or not.
Upvotes: 2