views:

266

answers:

5

Hello everyone,

in my application, I have a "User" model. Each user can have multiple (email) addresses which are defined in the model "Address":

Class User < ActiveRecord::Base
  has_many :addresses


  def is_authorized(op)
     # returns true or false
  end

  def is_owned_by(user)
     # returns true or false
  end
end

Class Address < ActiveRecord::Base
  belongs_to :user
end

Inside the AddressController class, the currently logged in user is available in the "@user" instance variable. The controller prevents ordinary users from editing, deleting, viewing etc. addresses which don't belong to them - but he does allow an administrative user to edit those. The AddressController class can ask the AddressModel if the user currently logged in is performing normal or superuser operations.

This all works nicely and database updates are made as expected, however, I'd really like to have different HTML views depending on the mode of operation. I can only think of two ways to achieve that:

  1. Make the mode of operation (normal/privileged) known in the AddressController class (using an instance variable, e.g. @privileged) and use an "if" statement in the view.
  2. Use something like an "after_filter" in the address controller to render a different layout.

If it is possible to display the results of executing a single controller in two completely different layouts, depending on it's mode of operation, what is a good way to achieve that?

Thanks in advance Stefan

+1  A: 

You can simply call the render method manually at the end of your controller action:

if @privileged
    render :action => 'show_privileged'
else
    render :action => 'show'
end

This will render app/views/myview/show_privileged.html.erb or app/views/myview/show.html.erb. Alternatively, you can use the :template option to give an explicit template file to the render method.

Dave Ray
Thank you, Dave.
cite
A: 

You can specify which view to use to display the result of an action in the action itself. You can also specify which layout to use too. So, for example:

def my_action
  if @user.is_authorised(...)
    render :action => 'admin_action', :layout => 'admin'
  else
    render :action => 'non_admin_action', :layout => 'non_admin'
  end
end

This will render either admin_action.html.erb or non_admin_action.html.erb depending on the returned value from is_authorised. The :layout option is, er, optional and refers a layout in views/layouts. There are various other options the render call which you can find in the documentation for render.

Shadwell
Thank you. But it seems that if I do this, I'd leave the path of "convention over configuration". I'm a little worried - this is my first Rails application - should I already diverge from what everybody else seems to do?
cite
Well, arguably convention is to render one view for an action, but I can see what you're getting at. If you are just looking for different layouts then Staelen's solution is a good one. If you really do want to render different views depending on logic in the action then I'm not sure that's close enough to the basic convention to get away with not specifying which view you want to render.
Shadwell
I think I will try Staelen's solution for the layout and your one for the template to be rendered. Thanks again.
cite
A: 

You can specify the layout of the view for that particular controller, or the whole application in the application controller by:

class SomeController < ApplicationController
  layout :set_layout

  def set_layout
    @user.is_authorized(...) ? "privileged_layout" : "normal_layout"
  end

  ...
end

You can try to figure it out here: http://guides.rubyonrails.org/layouts_and_rendering.html#using-render, under 2.2.12 Finding Layouts

Hope this helps =)

Staelen
Oh, this is a nice idea. Thank you very much!
cite
+2  A: 

If this is the only controller in your app where you're if/else'ing all over the place that's probably fine. If you start doing this type of logic everywhere that should tell you that you're doing too much at once.

The answer you accepted (which is fine and works!) has a different layout and a different view, to me that says the controller is doing too much - I'd split this out into an admin controller.

Andy Gaskell
This. You were right. I tried to do too many things at once - the idea of having all those actions restfully available in two modes of operation was too intriguing.I wrote a dedicated admin controller. Thanks.
cite
A: 

You should put administrative actions in an an administrative namespace and restrict it there. Create a directory called admin in your controllers directory and add an _application_controller.rb_ in there:

class Admin::ApplicationController < ApplicationController
  before_filter :check_authorized

  private
    def check_authorized?
      if !logged_in? || !current_user.admin?
        flash[:notice] = "You've been very bad. Go away.
        redirect_to root_path
      end
    end
 end

Now you can put controllers into this namespace and make them inherit from Admin::ApplicationController too.

Ryan Bigg