require 'active_support/concern' require 'active_support/cache' module Cell module Caching extend ActiveSupport::Concern module ClassMethods # Caches the rendered view of +state+. # # Examples: # # This will cache forever. # # class CartCell < Cell::Base # cache :show # # You can also pass options to the caching engine as known from Rails caching. # # cache :show, :expires_in => 10.minutes # # If you need your own granular cache keys, pass a versioner block. # # cache :show do |cell, options| # "user/#{cell.options[:id]}" # end # # This will result in a cache key like cells/cart/show/user/1. # # Alternatively, use an instance method. # # cache :show, :versioner # def versioner(options) # "user/#{options[:id]}" # end # # Two things to mention here. # * The return value of the method/block is appended to the state cache key. # * You may return a string, a hash, an array, ActiveSupport::Caching will compile it. def cache(state, *args, &block) options = args.extract_options! version_procs[state] = args.first || block cache_options[state] = options end def version_procs @version_procs ||= {} end def cache_options @cache_options ||= {} end def cache_store # DISCUSS: move to instance level and delegate to #config/#parent_controller. # This would allow convenient cache settings per cell (if needed). ::ActionController::Base.cache_store end # Computes the complete, namespaced cache key for +state+. def state_cache_key(state, key_parts={}) expand_cache_key([cell_name, state, key_parts]) end def expire_cache_key(key, *args) cache_store.delete(key, *args) end def cache?(state) # DISCUSS: why is it private? ActionController::Base.send(:cache_configured?) and state_cached?(state) end protected # Compiles cache key and adds :cells namespace to +key+, according to the # ActiveSupport::Cache.expand_cache_key API. def expand_cache_key(key) ::ActiveSupport::Cache.expand_cache_key(key, :cells) end def state_cached?(state) version_procs.has_key?(state) end end def render_state(state, *args) return super(state, *args) unless self.class.cache?(state) key = self.class.state_cache_key(state, call_state_versioner(state, *args)) options = self.class.cache_options[state] self.class.cache_store.fetch(key, options) do super(state, *args) end end protected def call_state_versioner(state, *args) version_proc = self.class.version_procs[state] or return return version_proc.call(self, *args) if version_proc.kind_of?(Proc) state_accepts_args?(state) ? send(version_proc, *args) : send(version_proc) end end end