Skip to content

Instantly share code, notes, and snippets.

@fastjames
Forked from dignoe/gist:2485272
Created May 18, 2012 15:52
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save fastjames/2725983 to your computer and use it in GitHub Desktop.
Save fastjames/2725983 to your computer and use it in GitHub Desktop.
Patch to get content_for working with action caching and fragment caching in Rails 3.1
module ActionController
class Metal
attr_internal :cached_content_for
end
module Caching
module Actions
def _save_fragment(name, options)
return unless caching_allowed?
content = response_body
content = content.join if content.is_a?(Array)
content = cached_content_for.merge(:layout => content) if cached_content_for.is_a?(Hash)
write_fragment(name, content, options)
end
end
module Fragments
def write_fragment_with_content_to_cache(key, content, options = nil)
# return_content = write_fragment_without_content_to_cache(key, content, options)
# return_content.is_a?(Hash) ? return_content[:layout] : return_content
return content unless cache_configured?
key = fragment_cache_key(key)
instrument_fragment_cache :write_fragment, key do
cache_store.write(key, content, options)
end
content.is_a?(Hash) ? content[:layout] : content
end
def read_fragment_with_content_to_cache(key, options = nil)
result = read_fragment_without_content_to_cache(key, options)
if (result.is_a?(Hash))
result = result.dup if result.frozen?
fragment = result.delete(:layout)
self.cached_content_for = {}.merge(result)#(self.cached_content_for || {}).merge(result)
result = fragment
end
result.respond_to?(:html_safe) ? result.html_safe : result
end
alias_method_chain :write_fragment, :content_to_cache
alias_method_chain :read_fragment, :content_to_cache
end
end
end
module ActionView
class TemplateRenderer < AbstractRenderer
# Added to support implementation of action caching
def render_template_with_cached_content_for(template, layout_name = nil, locals = {})
controller = @view.controller
if controller.respond_to?('caching_allowed?') && controller.caching_allowed? && controller.is_a?(ApplicationController)
controller.cached_content_for.each { |k, v| @view.content_for(k, v) unless @view.content_for?(k) } if controller.cached_content_for.is_a?(Hash)
return_value = render_template_without_cached_content_for(template, layout_name, locals)
controller.cached_content_for = @view.content_to_cache
elsif
return_value = render_template_without_cached_content_for(template, layout_name, locals)
end
return_value
end
alias_method_chain :render_template, :cached_content_for
end
module Helpers
module CaptureHelper
# Added to support implementation of fragment caching
def cache_with_content_for #:nodoc:#
@_content_for_to_cache = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
yield
ensure
@_content_for_to_cache = Hash.new
end
# Added to support implementation of action caching
def content_to_cache #:nodoc:#
cache_this = @_content_for_to_cache || @view_flow.content.except(:layout)
# cache_this = @view_flow.except(:layout)
cache_this.dup.tap {|h| h.default = nil }
end
# Overwrite content_for to support fragment caching
def content_for(name, content = nil, &block)
content = capture(&block) if block_given?
if content
if @_content_for_to_cache
@_content_for_to_cache[name] ||= Array.new
@_content_for_to_cache[name] << content
end
@view_flow.append(name, content)
nil
else
@view_flow.get(name)
end
end
end
module CacheHelper
def fragment_for(name = {}, options = nil, &block) #:nodoc:
if fragment = controller.read_fragment(name, options)
controller.cached_content_for.each { |k, v| content_for(k, v) } if controller.cached_content_for.is_a?(Hash)
fragment
else
pos = output_buffer.length
hash_to_cache = nil
cache_with_content_for do
yield
output_safe = output_buffer.html_safe?
fragment = output_buffer.slice!(pos..-1)
if output_safe
self.output_buffer = output_buffer.class.new(output_buffer)
end
hash_to_cache = {:layout => fragment}.merge(@_content_for_to_cache)
end
controller.write_fragment(name, hash_to_cache, options)
end
end
end
end
end
@ilanstern
Copy link

Hi there! Have you tested the above code? If so, how to implement it (i.e. where to add the module)?

@TylerRick
Copy link

Has anyone extracted this to a gem or submitted a PR to Rails core? We still need this for current versions of Rails!

@TylerRick
Copy link

I've submitted a pull request here, in case you're interested: rails/rails#39600

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment