Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

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

View gist:2485272
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
tibbon commented

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.

Owner
dignoe commented

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.

tibbon commented

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).

Owner
dignoe commented

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.