Rails set layout from within a before_filter metho

2020-02-26 04:19发布

问题:

Is it possible to reset a default layout from within a before_filter method in Rails 3?

I have the following as my contacts_controller.rb:

class ContactsController < ApplicationController
  before_filter :admin_required, :only => [:index, :show]
  def show
    @contact = Contact.find(params[:id])
    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @contact }
    end
  end
  [...]
end

And the following in my application_controller.rb

class ApplicationController < ActionController::Base
  layout 'usual_layout'
  private
  def admin_required
    if !authorized?          # please, ignore it. this is not important
      redirect_to[...]
      return false
    else
      layout 'admin'  [???]  # this is where I would like to define a new layout
      return true
    end
  end
end

I know I could just put...

layout 'admin', :only => [:index, :show]

... right after "before_filter" in "ContactsController", but, as I already have a bunch of other controllers with many actions properly being filtered as admin-required ones, it would be much easer if I could just reset the layout from "usual_layout" to "admin" inside the "admin_required" method.

BTW, by putting...

layout 'admin'

...inside "admin_required" (as I tried in the code above), I get an undefined method error message. It seems to work only outside of defs, just like I did for "usual_layout".

Thanks in advance.

回答1:

From Rails guides, 2.2.13.2 Choosing Layouts at Runtime:

class ProductsController < ApplicationController
  layout :products_layout

  private

  def products_layout
    @current_user.special? ? "special" : "products"
  end
end


回答2:

If for some reason you cannot modify the existing controller and/or just want to do this in a before filter you can use self.class.layout :special here is an example:

class ProductsController < ApplicationController
  layout :products
  before_filter :set_special_layout

  private

  def set_special_layout
    self.class.layout :special if @current_user.special?
  end
end

It's just another way to do essential the same thing. More options make for happier programmers!!



回答3:

The modern way of doing this is to use a proc,

layout proc { |controller| user.logged_in? "layout1" : "layout2" }