views:

101

answers:

4

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 controllers have a show, an edit and an index view. If an authorized user is logged on, the edit view becomes available and the index view shows additional data manipulation controls, like a delete button or a link to the edit view.

Now I have a Rails application which falls exactly into this pattern, but the index view is not reusable:

  1. The normal user sees a flashy index page with lots of pictures, complex layout, no Javascript requirement, ...
  2. The Admin user index has a completly different minimalistic design, jQuery table and lots of additional data, ...

Now I'm not sure how to handle this case. I can think of the following:

  1. Single controller, single view: The view is split into two large blocks/partials using an if statement.
  2. Single controller, two views: index and index_admin.
  3. Two different controllers: BookController and BookAdminController

None of these solutions seems perfect, but for now I'm inclined to use the 3rd option.

What's the preferred way to do this?

+1  A: 

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
Salil
+3  A: 

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"

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...

Aaron Qian
+1  A: 

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)
Vitaly Kushner
A: 

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.

Hanse