Set layout from responder? - ruby-on-rails

I am trying to figure out how to set the layout from a custom made responder. I want to use the request.xhr? to set the layout for render to 'ajax'. Does anybody know how to do that?
I am using Rails 3 and I have a responder like this:
module AjaxLayoutResponder
def to_html
if request.xhr?
# do something here to change layout...
end
super
end
end
It seem to me like a responder is the best way to accomplish this 'ajax' layout switching.

I disagree that a responder is the way to go. Here's an easy solution that I use in most of my projects (however I just set the ajax layout to nil):
In application_controller.rb
layout :set_layout
def set_layout
request.xhr? 'ajax' : 'application'
end

you could simply do this:
module AjaxLayoutResponder
def to_html
if request.xhr?
options[:layout] = 'ajax'
end
super
end
end
because what is called at the end of responder execution is:
# from https://github.com/plataformatec/responders/blob/master/lib/action_controller/responder.rb
def default_render
if #default_response
#default_response.call(options)
else
controller.render(options)
end
end

Related

Disable layout rendering in controller filter

Using pjax, trying to disable layout if request.headers['X-PJAX'] is true.
Instead of placing this logic in all my routes, is there a way to setup a filter that does this?
after_filter lambda {
if request.headers['X-PJAX']
# disable rendering with layout
end
}
def show
render layout: application
end
The following should work
SomeController < ApplicationController
layout choose_layout
def actions
.
.
.
private
def choose_layout
request.headers['X-PJAX'].present? ? false : 'application'
end
end
If you only ever use the application layout, I guess you could put it in the Application controller to have it applied to all controllers automatically.

Disable auto rendering in rails

What I need is to disable the automatic page (HTML) rendering in rails and override it with a after_action method. What I'm trying to achieve is an equivalent of CakePHP $this->autoRender = false;
application_controller.rb
class ApplicationController < ActionController::Base
after_action :custom_render
layout nil # Tried this but didn't worked
def custom_render
render #[...]
end
end
some_controller.rb
class SomeController < ApplicationController
def index
# No rendering here
end
end
As shown in the code I tried to add a layout nil to prevent all actions from rendering, but that doesn't seem to affect the behaviour of the action.
Haven't checked whether it works with Rails 4, but this patch works for Rails 5.
According to the code of BasicImplicitRender and ImplicitRender, send_action of is BasicImplicitRender responsible for calling default_render
Documentation says:
For API controllers, the implicit response is always 204 No Content.
For all other controllers, we use ... heuristics to decide whether to
render a template, raise an error for a missing template, or respond with
204 No Content ...
So I suppose redefining default_render method will serve you purpose.
In your controller:
def a
# uses `default_render` unless you call `render` method explicitly
end
def b
render plain: 'Custom text for b' # `default_render` won't be called
end
private
# This does the trick
#
def default_render
render plain: 'Text'
end
You may also hack send_action just like it is done in Rails so as to even skip default_render call at all:
module ActionController
module BasicImplicitRender # :nodoc:
def send_action(method, *args)
# super.tap { default_render unless performed? }
super
end
end
end
To disable rendering (well return nothing) issue.
def index
render :nothing
end
But it's too late to do anything, as it will return response with empty body.
To disable layout:
def index
render layout: false
end
This will render you view without a layout, issue (render layout: 'my_custom_layout') to render default view but with different layout.
We don't know what you want, but the simplest solution is just to render a specific view, f.i.:
def index
render 'my_custom_file.'
end
There are really many options: http://guides.rubyonrails.org/layouts_and_rendering.html#using-render
EDIT - as requested in a comment
class ApplicationController < ActionController::Base
before_action :set_user_template
# ...
def set_user_template
template_name = current_user.template_name
self.class.layout "#{template_name}/application"
end
end

Dynamically add around_filter

I am working on adding some methods to a controller dynamically.
def self.add_command(method)
define_method(method) do
# Do something
end
end
add_command :method_name
I would like to add an around filter for these methods and only these, but do it dynamically. When I simply try to add an around_filter call in the add_command method it doesn't get triggered.
How is it possible to dynamically add an around filter?
def self.add_command(method)
define_method(method) do
# Do something
end
class_eval do
around_filter method.to_sym
end
end

Override rails helper route methods to set layout from a GET parameter

I'm trying to change the layout of my application according to a GET parameter passed in the URL of each request : &layout=name_of_the_layout.
And in my application_controller :
class ApplicationController < ActionController::Base
layout :get_layout_from_params
private
def get_layout_from_params
if params['layout']
params['layout']
else
'application'
end
end
end
It works fine, but to "persist" the layout when the the user navigates in the application, I need to add this parameter on each rails route helper in my views (even for POST requests in forms…):
ressource_path(#ressource, :layout => get_layout_from_url())
where get_layout_from_url() is a helper that checks if the params['layout'] is set in the URL, validates then returns it.
This is definitely not DRY... How can i override every route helper to include this behavior without writing any additional code in my views? I would like to call the standard rails methods in my views : ressource_path(#ressource), ...
Or is there a smarter way to achieve this?
PS : Im using rails 3.2.3
Thanks !
There was the old and deprecated default_url_options now replaced by url_options :
class ApplicationController < ActionController::Base
def url_options
{ :layout => validate_layout }.merge(super)
end
def validate_layout
# some stuff reusable
params[:layout]
end
end
If it doesn't fit, it is close.
I think you would better store it in session instead of appending it to the url for every request.
In rails, you could use session in a very simple way:
def get_layout_from_params
if params['layout']
session['layout'] = params['layout']
else
session['layout'] || 'application'
end
end
If there is a params['layout'], it means the user is going to change the layout, so you assign it to the session.
If there is no params['layout'] given, then it checks if there is session['layout'], return it or return 'application' if session['layout'] is false or nil.
--- edit ---
the following version even shorter, see if you like it or not:
def get_layout_from_params
session['layout'] = params['layout'] || session['layout'] || 'application'
end

Rails for ActionMailer - How to disable a Layout for a certain mailer

I have a user_mailer with a layout.
For one of my actionmailer methods I want the mailer to NOT use the default layout. But I can't see to find a setting for No Layout.
Any ideas?
Simply specify in your mailer:
layout false
You can also append :only => my_action (or :except) to limit the methods it applies to, like so:
layout false, :only => 'email_method_no_layout'
(applicable API documentation)
I did it by using a small function, looking at the action name and return the correct mailer layout to be used:
class TransactionMailer < ApplicationMailer
layout :select_layout
def confirmation_email contact
#code
end
private
def select_layout
if action_name == 'confirmation_email'
false
else
'mailer'
end
end
end
The layout method can accept the name of a method; use the method to determine whether to show a layout and return that name or false.
layout :choose_layout
...
private
def choose_layout
if something
return false
else
return 'application'
end
end
You could also be very sketchy and do this before the mail( ) call at the end of the specific mailer action instead:
#_action_has_layout = false

Resources