Rails3 get current layout name inside view - layout

I have the answer for the Rails 2.X but not for Rails 3. How can I read the name of a current layout rendered inside a view.
My Rails2 question: Rails Layout name inside view
Thx.

Getting this to work in Rails 3.2 is a little more complicated than previously outlined. If your controller explicitly declares a layout, then the result of controller.send(:_layout) is a String, but otherwise it's an ActionView::Template. Try this:
module ApplicationHelper
def current_layout
layout = controller.send(:_layout)
if layout.instance_of? String
layout
else
File.basename(layout.identifier).split('.').first
end
end
end

in rails 5
This works for me:
def current_layout
layout = controller.class.send(:_layout)
if layout.nil?
default_layout
elsif layout.instance_of? String or layout.instance_of? Symbol
layout
else
File.basename(layout.identifier).split('.').first
end
end

For Rails 4:
controller.send(:_layout)
=> 'application'
For Rails 3.2:
controller.send(:_layout)
=> #<ActionView::Template:0x000000082bb788>
But controller.send(:_layout).identifier returns the fullpath:
/home/davidm/Documentos/Devel/myapp/app/views/layouts/application.haml

I think it should be in core, but for now you can make a helper method:
def current_layout
controller.send :_layout
end
it will return currently used layout name

I have used in Rails4 at view pages and got reuslt.
controller.send(:_layout)
I hope this help.

For rails 5:
controller.class.send(:_layout)
This does NOT work:
controller.send(:_layout)

You can do what I've done in my Ajax gem for Rails which is to wrap the _render_layout method:
ActionView::Base.class_eval do
def _render_layout_with_tracking(layout, locals, &block)
controller.instance_variable_set(:#_rendered_layout, layout)
_render_layout_without_tracking(layout, locals, &block)
end
alias_method_chain :_render_layout, :tracking
end
Then you can access the value that was set from your view (I'm pretty sure you have access to the controller there...) or in your controller in an after_filter, which is what I do.
I've written a custom RSpec 2 matcher which can be used to test layout rendering in Rails 3.

All the approaches in the previous answers try to guess the name via private methods, but there's no need to guess and can be easily accomplished with the public API:
class ApplicationController
layout :set_layout
attr_reader :layout_name
helper_method :layout_name
private
def set_layout
#layout_name = "application"
end
end
Override in any controller that won't use the standard layout:
class MyController < ApplicationController
private
def set_layout
#layout_name = "my_layout"
end
end
And now in your views:
<%= layout_name %>

Related

ActiveAdmin not decorating show view

I am using ActiveAdmin on my Rails project. I use Draper as decorator but I don't understand why the show view is not decorated. According to the documentation it should be working just adding the decorate_with MyDecorator in my ActiveAdmin resource.
Here is my code:
ActiveAdmin.register Home do
...
decorate_with HomeDecorator
show do
attributes_table do # not being decorated
row :content
row :status
row :image
row :author_with_avatar
end
end
end
Does someone know what is wrong with my code ?
When I've run into this problem, it's because I've customized the controller's find_resource method. In that case be sure to return a manually decorated object:
def find_resource
MyModel.find_by_some_unique_finder(params[:id]).decorate
end
If all else fails you can force it using this approach:
show
attributes_table_for model.decorate do
row :decorator_method
row(:custom_label) { |m| m.decorator_method }
See http://activeadmin.info/docs/6-show-pages.html for more details

Cucumber steps fail for nested controller

I'm trying to implement a feature in my Rails 3 site using BDD:
Feature: Patents Administration
Scenario: Patents index
Given I am on the admin patents page
Then I should see "Patents"
And the title should be "Wavetronix - Patents"
And here are the corresponding steps:
Given /^I am on the (.*?) page$/ do |text|
visit eval("#{text.downcase.gsub(/\s/, '_')}_path(locale: 'en')")
end
Then /^I should see "(.*?)"$/ do |text|
page.must_have_selector('h1', text: text)
end
Then /^the title should be "(.*?)"$/ do |text|
page.must_have_selector('title', text: text)
end
The first step fails as expected: I need to implement Admin::PatentsController:
module Admin
class PatentsController < BaseController
before_filter :find_patent
def index
end
private
def find_patent
#patent = Patent.find(params[:id]) if params[:id]
end
end
end
Because it inherits from Admin::BaseController—which has its own index action and view:
module Admin
class BaseController < ApplicationController
filter_access_to :index
def index
end
end
end
Admin::PatentsController inherits that action and view as well. When I override the BaseController implementation by explicitly defining an index action and view for the PatentsController, I can see the change in the browser—that it picks up the new index action and view—but the Cucumber step fails because it appears to still be looking at the BaseController index action and view.
I created a gist with more code for reference.
Is this a bug? Is there a better way to test this?
Bonehead: I am not accounting for existing authentication mechanism, which is redirecting because the test request isn't authenticated.

Properly rendering multiple layouts per controller in Rails

I've defined in my Users_controller:
layout "intro", only: [:new, :create]
Here's what my layout looks like:
Intro.html.haml
!!! 5
%html{lang:"en"}
%head
%title Intro
= stylesheet_link_tag "application", :media => "all"
= javascript_include_tag "application"
= csrf_meta_tags
%body{style:"margin: 0"}
%header
= yield
%footer= debug(params)
When I render a page that calls for intro as the layout, it gets nested inside my application.html.haml file which is not good.
Is there some way of avoiding this undesirable nesting of layouts?
Thanks in advance!
The problem was in my Controller. I was declaring multiple layout instances like so:
class UsersController < ApplicationController
layout "intro", only: [:new, :create]
layout "full_page", only: [:show]
...
end
Don't do this! The second declaration will take precedence and you won't get your desired affect.
Instead, if your layouts are simply action-specific, just declare it within the action like this:
def show
...
render layout: "full_page"
end
Or, if it's a bit more complex, you can use a symbol to defer the processing to a method at runtime like this:
class UsersController < ApplicationController
layout :determine_layout
...
private
def determine_layout
#current_user.admin? ? "admin" : "normal"
end
end

How to get the name of the current layout?

symfony getLayout does not seem to work when layout is set via view.yml. Is there anyway to get this within the controller's action class method
I recently needed this. You can do it but you just need to return the entire view.yml contents as an array:
$view_array = sfViewConfigHandler::getConfiguration(array(sfConfig::get('sf_app_config_dir').'/‌​view.yml'));
Just adjust the relative path from sf_app_config_dir (or use another marker) to get what you need.
It's not a trivial task. The view.yml, is not in the "scope" of the action.
Maybe, you can use setLayout in your action rather then in view.yml.
if you can't, for some reasons... you can try this method to reach datas in view.yml:
Is it possible to get a value from view.yml in an action
Execute the following code in the action. It works for both cases, layout set in the action or in the view.yml.
$controller = $this->getContext()->getController();
$view = $controller->getView($this->getModuleName(), $this->getActionName(), 'Success'); // viewName == 'Success' (default)
$layout_name = $view->getDecoratorTemplate(); // e.g expected: layout.php
Let us know if it works for you.

how to use a controller variable in my layout

I am trying to use a variable of my controller in my layout.
For example:
#posts = Post.all.count
In my layout I want to list the Post count, even when I open the index view of another controller.
Many thanks!!!
Two solutions:
Use <%= Post.all.count %> in your layout.
Add a before_filter in your ApplicationController that loads the variable.
class ApplicationController < ActionController::Base
before_filter :load_layout_variables
protected
def load_layout_variables
#posts = Post.all.count
end
end

Resources