module LaserLemon module CacheFlow def self.included(base) base.class_eval do class_inheritable_accessor :cache_serialization self.cache_serialization = false extend ClassMethods include InstanceMethods alias_method_chain :to_xml, :cache_flow alias_method_chain :to_json, :cache_flow end end module ClassMethods def cache_flow(value = true) if block_given? old_value = cache_serialization begin self.cache_serialization = value yield ensure self.cache_serialization = old_value end else self.cache_serialization = value end end end module InstanceMethods def to_xml_with_cache_flow(options = {}, &block) if cache_xml?(options, &block) serializer = ActiveRecord::XmlSerializer.new(self, options) builder = serializer.builder key = xml_cache_key(serializer.options) fetch = Rails.cache.fetch(key) do target = builder.target! builder.instance_eval{ @target = '' } value = block_given? ? serializer.to_s(&block) : serializer.to_s builder.instance_eval{ @target = target } value end builder.instance_eval{ @target << fetch } else to_xml_without_cache_flow(options, &block) end end def to_json_with_cache_flow(options = {}) if cache_json?(options) key = json_cache_key(options) Rails.cache.fetch(key) do value = to_json_without_cache_flow(options) value.duplicable? ? value.dup : value end else to_json_without_cache_flow(options) end end private def cache_xml?(options, &block) case when block_given?, options.has_key?(:procs) then false else cache_serialization?(options) end end def cache_json?(options) cache_serialization?(options) end def cache_serialization?(options) case when options.has_key?(:cache) then options.delete(:cache) else cache_serialization end end def xml_cache_key(options) level = options.has_key?(:builder) ? options[:builder].instance_eval{ @level } : nil serialization_cache_key(:xml, options.except(:builder).merge(:level => level)) end def json_cache_key(options) serialization_cache_key(:json, options.merge(:include_root_in_json => include_root_in_json)) end def serialization_cache_key(extension, options) query = options.except(:cache, :skip_instruct).reject{|k,v| v.nil? }.to_query key = ["#{cache_key}.#{extension}", query].delete_if(&:blank?).join('?') if cache_key_limit && (key.size > cache_key_limit) "#{cache_key}.#{extension}?#{hashed_serialization_query(query)}" else key end end def cache_key_limit case Rails.cache.class.name.demodulize.underscore.to_sym when :mem_cache_store then 250 end end def hashed_serialization_query(query) require 'digest/sha1' unless defined?(Digest) && defined?(Digest::SHA1) "cache_flow=#{Digest::SHA1.hexdigest(query)}" end end end end ActiveRecord::Base.send(:include, LaserLemon::CacheFlow)