Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
make action and fragment caching of rails 3.0.9 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.key?(:layout) ? 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.key?(:layout)
result = result.dup if result.frozen?
fragment = result.delete(:layout)
self.cached_content_for = (self.cached_content_for || {}).merge(result){|_, v1, v2| v1 + v2 }
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?
if controller.cached_content_for.is_a?(Hash)
controller.cached_content_for.each { |k, v| content_for(k, v) }
controller.cached_content_for = {}
end
return_value = _render_template_without_cached_content_for(template, layout, options)
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 ||= []
@_subset_content_for.push(Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new })
@cache_level = @_subset_content_for.size
yield
ensure
@_subset_content_for.pop
@cache_level = @_subset_content_for.size
end
def cache_level
@cache_level ||= 1
@cache_level - 1
end
# Added to support implementation of action caching
def content_to_cache #:nodoc:#
cache_this = (@_subset_content_for && @_subset_content_for[cache_level]) || @_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
if content && @_subset_content_for && @_subset_content_for[cache_level]
@_subset_content_for[cache_level][name] << content
# также нужно передать этот контент всем родительским кеш-блокам
(cache_level - 1).downto(0) do |i|
@_subset_content_for[i][name] << content
end
end
@_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)
if controller.cached_content_for.is_a?(Hash)
controller.cached_content_for.each { |k, v| content_for(k, v) }
controller.cached_content_for = {}
end
fragment
else
pos = output_buffer.length
hash_to_cache = nil
cache_with_content_for do
yield
if output_buffer.is_a?(ActionView::OutputBuffer)
safe_output_buffer = output_buffer.to_str
fragment = safe_output_buffer.slice!(pos..-1)
self.output_buffer = ActionView::OutputBuffer.new(safe_output_buffer)
else
fragment = output_buffer.slice!(pos..-1)
end
hash_to_cache = {:layout => fragment}.merge(content_to_cache)
end
controller.write_fragment(name, hash_to_cache, options)
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment