Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
# José Valim <jose.valim@gmail.com>
# http://josevalim.blogspot.com/
# http://www.pagestacker.com/
#
# This render_sibling paste is Rails 2.1 compatible.
# For Rails 2.2 render_sibling, please check here: http://gist.github.com/19631
module RenderSiblingHelper
# What?
#
# Since render_component was deprecated in Rails 2.2, this helper is a
# lightweight implementation of what is wrongly called "render_component",
# being used in Pagestacker.
#
# How?
#
# It's a view helper (render_sibling) that uses the controller which is
# rendering the current action to render a new action (sibling) from
# the same controller (and only from the same controller). Since we don't
# create another controller, it's faster than render_component.
#
# Even more, since we are reusing the controller, all instance variables
# set to render the first action are already set when rendering the second
# action (sibling). For this reason, filters are not executed again.
#
# For example, if you use a filter that set @current_user, using components
# this filter is called twice (querying the database twice also). Using
# render_sibling the filter is executed only once but the variable is set
# in both actions.
#
# render_sibling also allows you to control which variables are sent to the
# template rendered in the second action (sibling), using the options :assigns.
# Following the previous example, @current_user variable was set on the first
# render, then if you want it to be available on the second template, you have
# to send :assigns => [ :current_user ]. But if you want to include all assigns
# from the first request, you can do :assigns => :all.
#
# Besides the parameters (params) send to render the first action,
# you can pass new parameters to the second action (sibling) using the option
# :params (just as in render_component).
#
# Finally, when compared to partials, it's just a little bit slower, but it's
# semantically better, since the code is actually in the action and not in
# filters and helpers. Anyway, partials must be used in other situations than
# render_simbling, which must represent a great alternative for "render_component".
#
# Usage?
#
# Add the method at the end of this file to your application_helper.rb and
# then you can call on your views:
#
# render_sibling(action, :params => {:addtional_parameter => :cool}, :assigns => :all)
#
# More?
#
# Benchmark files are available here: http://gist.github.com/19642
#
# And if you want to read more about partials and components, I suggest this
# article by Charlie Savage:
#
# http://cfis.savagexi.com/2008/02/11/reusing-rails-controllers-is-a-good-thing
#
def render_sibling(action, options = {})
# Add new parameters to the controller
controller.request.parameters.merge!(options[:params] || {})
# Set assigns options and add _request
options[:assigns] = Array(options[:assigns])
options[:assigns] << '_request'
# Save current assigns keys
old_assigns = controller.assigns.keys
controller.assigns.clear
# Set new action_name in the controller
previous_action_name = controller.action_name
controller.action_name = action.to_s
# Set performed_render to true to raise errors when render is called
controller.instance_variable_set('@performed_render', true)
controller.__send__(action) rescue
# Add new variables to assigns
controller.instance_variable_set('@variables_added', false)
controller.__send__(:add_variables_to_assigns)
# Get the assigns we should send to the new template
new_assigns = if options[:assigns].delete(:all)
controller.assigns.keys + old_assigns
else
controller.assigns.keys + options[:assigns]
end
# Render sibling
view = ActionView::Base.new(controller.class.view_paths, controller.assigns.slice(*new_assigns), controller)
view.extend controller.class.master_helper_module
action = "_#{action}" if options[:as_partial]
response = view.render_file(controller.__send__(:default_template_name, action), true, {})
# Restore previous action_name and return response
controller.action_name = previous_action_name
return response
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.