We have a site where we have a backend management interface, and a frontend that displays our information. We are using Devise to secure authentication.
The backend should allow for normal CRUD type editing of our model objects. The views and layout are also completely different than the frontend. What is the best practice for implementing this in Rails 3?
Our two approaches are:
An admin view folder houses all view specific code, as well as an admin folder in the controllers folder houses all controllers that control admin specific access.
A conditional logic system with one set of views and controllers, with if statements checking whether the user is in admin mode or not.
Which is more recommended, or if there is another approach we have missed, please let me know.
The first solution is better, however for these cases was created the namespaces and the best practice is to go with namespaces when you need relevant differentiation between user site and administration area. Read more about it here
Your directory structure should look like this:
controllers/
|--admin/
|--posts_controller.rb
In your routes you put everything you need into admin namespace:
namespace :admin do
resources :posts, :comments
end
Your controllers should have an admin folder, and a controller in the admin area will look like:
class Admin::PostsController < ApplicationController
end
You also should have an admin folder in your views, where you put the respective views:
views/
|--admin/
|--posts/
|--index.html.erb
|--...
You can also namespace your models, but it depends on your needs, it is good when you need to have different models with the same name. For example if you need different table for the admin users, and different table for normal users. Personally I wouldn't use model namespacing, just in very justified cases.
The second option I think can cause a lot of headache, you gonna be lost in the if statements, I don't recommend that at all.
Related
I have a model User (used for authentication purposes) which is connected to two other models, Customer and Seller via polymorphic association in Rails. A customer and seller must have different sign up forms because of their different data but can use a single login form because they're both Users. In order to manage the customer and seller resources separately, is it a good idea to create respective controllers and views for them, or should management of all Users be done in the UsersController?
It depends upon the design of the two views customers and sellers. If the design of both the views are different then I will encourage you to create two controllers customers_controller and sellers_controller and you may want to have a module for common actions like lib/authentication.rb.So
# customers_controller.rb
include Authentication
def some_action
end
and
# sellers_controller.rb
include Authentication
def other_action
end
Authentication file like
# lib/authentication.rb
module Authentication
def common_method
end
end
You can have two controllers and one module which you can write common methods/actions
As #madyrockss pointed out, STI is probably the way to go here because User.find(params[:login]) will return either an instance of Customer or Seller automatically.
I would encourage forgetting about the UsersController and views and encourage thinking in terms of seller and customer controllers/views (that is, don't share a single controller and views per action that can handle either customer or seller and has conditionals within view determining what to show). If the two users have different business rules, the whole thing starts to get messy real quick and becomes unwieldy to manage as business rules change.
For login/logout, if the form is going to be the same for both, then a single controller will suffice and I'd consider a different name than UsersController, such as SessionsController to be more semantically in line with what the controller's purpose is. You are not confined to a one-to-one Models <==> Controllers <==> Views which many beginning Rails developers fall into the trap of.
I have a rails application with 2 roles, say admin and user. But the thing is, The admin doesn't use a backend like ActiveAdmin for example. I want both Admin and User to see the same views, but depending on the role, I restrict what they can see. I'm using Cancan, but since for example both admin and user can see the product page, I end up with many conditions inside the view and controller actions stating for example if this is an admin show that, if not then show that instead.
So I don't really think that this is the "Rails way". I end up with many repeated code, and code inside the views which doesn't really support the idea of keeping the logic away from the views.
So my question is, What's the best way to implement such a scenario with many roles but the same views.
Thank you.
I'm thinking of two options currently, but I don't like either. One is to redirect the admin to another view, but this way most of the view is the same hence it's not DRY at all.
Option 2 is to use the exact same view, but add many conditions in the view, so I end up with a huge complex view with code. I'm trying to find a way that keeps things DRY yet simple, and keeps the views code free.
You can have the admin module under seperate namespace and users as the default namespace. You can extract the common code under partials and use the same in both admin and user module.
This way you can separate the code for user and admin, and if sometime in future if you decide to go for a different views for admin and user. It won;t be much of a task.
Have controllers as
app/controllers/admin/articles ------ for admin users
app/controllers/articles ---- for normal users
and
views
app/views/admin/articles
app/views/articles
app/views/shared
There are different possible approaches. A variation of the 'decorator' pattern would come to mind as described here
It's some time I last read it, but I think the basic idea is to put the logic in the model or helpers. As soon as a user logs in and you can see if he is an admin or normal user you have methods that return the necessary html or text.
A more simple approach would be to just have two partials for everything. Either as Ross says in a separate admin namespace or even simpler (but more work if you later need to change that) just like _user_view_something.html.erb and _admin_view_something.html.erb.
Somewhat similar thoughts go into the DCI pattern. This Blog gives some nice overview how this could play into Rails. The article is more about permissions, but again the basic idea is to supplement the user object with the necessary functions.
So you have the basic "do_something" method which renders something or places some information in the view and replace it with the one that fits to the role the actual user has.
I'm wondering what's Rails approach when it comes to creating a full-fledged admin control panel. And by full-fledged I mean a real control panel that could be used on a professional level, not personal/internal scaffolding.
I don't believe its stored in the same folder as the user interface as it's shown in the blog screencast or in Getting Started with Rails tutorial!
I'm certain the same model will be used everywhere but what about the views?
Please explain in details. I don't want to use my PHP mentality when starting my first real project in Rails.
Thanks.
here you can find a list of rails plugins/engines to manage admin interfaces:
http://ruby-toolbox.com/categories/rails_admin_interfaces.html
I've used a bunch of them (typus and rails_admin), not bad at all.
also check the recent released ActiveAdmin
http://activeadmin.info/
As hinted at by the previous answers namespacing is useful to keep admin functionality neatly separated from other user functionality, for example:
# In your routes
namespace :admin do
resources :stories
resources :editors
#etc.
end
Then you'd put all your admin controllers in an 'admin' subfolder and always require an admin to access them:
# app > controllers > admin > stories_controller.rb
class Admin::StoriesController < ApplicationController
before_filter :authenticate_admin! # assuming you're using devise
def index
#etc.
end
end
The advantage of this is you only have to check for admin in one place (the before_filter of an admin controller) rather than having to put conditionals everywhere in your normal controllers / views.
It depends on the nature of your application, but there's nothing wrong with having the admin functions in the same view as the user; Just test your permission role before showing certain part of the display.
If you really need an admin only interface, you could namespace some controllers and views. There are other posts that deal with how to do this, such as The Rails Way - Namespaces
I have models A,B,C,D, etc. I have my usual controllers/views/helpers for each of these models. These are accessed by a set of content authors in a form based application to populate data into the db. The content authors will also have categories like authors, publishers, super admins etc. Essentially we have built out a mini content management system.
A set of other users (unrelated to the above set) need to access data in some of these models. But the view for those are totally different. I also do not want these users to have the edit screens for the models they are allowed to view. Essentially these guys are end users who use the application as a read only/analytics data store.
How do I accomplish this? Should I create separate controllers and invoke these models for the user website? How do I ensure the website users do not have access to the cms screens? Any pointers, design principles, routing methods, gems for such an application?
How do I accomplish this? Should I create separate controllers and invoke these models for the user website?
I would create a different set of controllers for the backend and frontend. I would move the backend controller to a namespace. More Information on namespaces: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing
How do I ensure the website users do not have access to the cms screens? Any pointers, design principles, routing methods, gems for such an application?
For this you need some kind of authentication and authorization. Some examples:
authentication:
authlogic
devise
authorization:
cancan
declarative_authorization
aegis
acl9
There are some good screencasts on this matter:
Authlogic
Declarative Authorization
Authorization with CanCan
Introducing Devise
Customizing Devise
You need a layer of authentication.
The easiest way, and I'd say the most common one is to make separate controllers for each section, and add a before_filter method in each section authenticating and authorizing user to continue (usually a is_admin? method on the user model), or redirect back with an error message if the user is not allowed.
You can separate your controllers with namespaces (something like /admin/authors, /admin/books/1/edit and so on), and keep them RESTful this way.
If you need a more complex schema, you can use any of the authorization tools out there http://ruby-toolbox.com/categories/rails_authorization.html
Need some help gathering thoughts on this issue.
Our team is moving ahead with the idea that separating the authenticated and public sections of our app in two separate folders will allow us to be more organized and secured.
I have seen this approach for Admin apps within the site but never for authentication.
We are currently using Authlogic.
What would be the disadvantage of this?
Thanks for your help.
i don't think you will find any disadvantages with moving the controllers into subfolders. we actually do this by "namespace"/process. for example, we have an "order" subfolder containing all controllers related to an order.
i think having a clean folder structure inside your rails app directory increases maintainability.
We namespace controllers in this manner - typically we'll have a /admin and a /my folder for the admin and user account controllers respectively.
Additionally, in the subfolder we will have an application_controller.rb that each of the controllers in that subfolder derives from. So in the admin subfolder, we have an application_controller.rb that looks like this:
class Admin::AdminController < ApplicationController
permit "admin"
layout 'admin'
end
We'll then inherit from this controller in our admin controllers. The example uses the rails-authorization-plugin for roles.