Skip to content

Instantly share code, notes, and snippets.

@stackng
Created March 29, 2011 06:30
Show Gist options
  • Save stackng/891895 to your computer and use it in GitHub Desktop.
Save stackng/891895 to your computer and use it in GitHub Desktop.
make action and fragment caching of rails3 compatible with content_for
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
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 = (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
module Rendering
# Added to support implementation of action caching
def _render_template_with_cached_content_for(template, layout = nil, options = {})
if controller.respond_to?('caching_allowed?') && controller.caching_allowed?
controller.cached_content_for.each { |k, v| content_for(k, v) unless content_for?(k) } if controller.cached_content_for.is_a?(Hash)
return_value = _render_template_without_cached_content_for(template, layout, options)
controller.cached_content_for = content_to_cache
elsif
return_value = _render_template_without_cached_content_for(template, layout, options)
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:#
@_subset_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
yield
ensure
@_subset_content_for = nil
end
# Added to support implementation of action caching
def content_to_cache #:nodoc:#
cache_this = @_subset_content_for || @_content_for.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?
@_content_for[name] << content if content
@_subset_content_for[name] << content if content && @_subset_content_for
@_content_for[name] unless content
end
end
module CacheHelper
def fragment_for(name = {}, options = nil, &block) #:nodoc:
if controller.fragment_exist?(name, options)
fragment = controller.read_fragment(name, options)
controller.cached_content_for.each { |k, v| content_for(k, v) unless content_for?(k) } if controller.cached_content_for.is_a?(Hash)
fragment
else
pos = output_buffer.length
hash_to_cache = nil
cache_with_content_for do
yield
fragment = output_buffer.slice!(pos..-1)
hash_to_cache = {:layout => fragment}.merge(content_to_cache)
end
controller.write_fragment(name, hash_to_cache, options)
end
end
end
end
end
@TylerRick
Copy link

TylerRick commented Jun 10, 2020

All right, I finally got this working on Rails 5.2 (which is the version I'm still on).

Here is my fork/patch , and a demo app that uses it.

Now to submit a merge request to rails (see feature proposal) ...

@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