public
Last active

Patch to get content_for working with action caching and fragment caching in Rails 3.1

  • Download Gist
gistfile1.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
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 = nil
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
@_content_for_to_cache[name] << content if @_content_for_to_cache
@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

I'm having problems with caching and fragments. Where should I put this? I'll give it a shot on Rails 3.2.2 right now.

We're only using the action caching part, so no guarantees on fragment caching working with this patch. If it doesn't, feel free to fork & update.

Awesome. Do I just drop this into the ApplicationController? Or elsewhere? I'm a bit green here. Will report back with results (and patches if I can).

Put it in a new file under /config/initializers. I named it action_caching_content_for_hack.rb.

I did need this for fragment caching, and it seems to work with only a few minor changes. Forked as https://gist.github.com/2725983

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.