Skip to content

Instantly share code, notes, and snippets.

@kivanio
Forked from josevalim/render_sibling_helper.rb
Created October 25, 2008 02:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kivanio/19673 to your computer and use it in GitHub Desktop.
Save kivanio/19673 to your computer and use it in GitHub Desktop.
# José Valim <jose.valim@gmail.com>
# http://josevalim.blogspot.com/
# http://www.pagestacker.com/
module RenderSiblingHelper
# What?
#
# Since render_component was deprecated in Rails 2.2, this helper is a
# lightweight implementation of "components", 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 what is called
# "components".
#
# 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
# 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
else
controller.assigns.keys - old_assigns + 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
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