require 'infopark_component_cache/component' require 'infopark_component_cache/cache_storage' require 'infopark_component_cache/guards/value_present' require 'infopark_component_cache/guards/last_changed' require 'infopark_component_cache/guards/obj_count' require 'infopark_component_cache/guards/valid_from' require 'infopark_component_cache/guards/valid_until' module InfoparkComponentCache # @author Tomasz Przedmojski # This class provides user-level access to component cache. # # @example Do some expensive computation on a page # InfoparkComponentCache::ComponentCache.new(@obj, 'processed_body', :page => 14) do # expensive_process_body @obj.body # end # # By default ComponentCache comes with a set of Guards, i.e. # classes that check the consistency of cache and invalidate it, # should it be neccesary. # @see ComponentCache#initialize class ComponentCache attr_reader :component, :guards # First three parameters are used to construct # the component # @see Component # # @param [Array] guards list of guard # classes used when deciding whether cache is valid # when left empty the default set is used: # @see Guard::ValuePresent # @see Guard::LastChanged # @see Guard::ObjCount def initialize(obj, name, params={}, guards=[]) @component = Component.new(obj, name, params) if guards.empty? @guards = [ Guards::ValuePresent.new(@component), Guards::LastChanged.new(@component), Guards::ObjCount.new(@component), Guards::ValidFrom.new(@component), Guards::ValidUntil.new(@component) ] else @guards = guards.map do |klass| klass.new(@component) end end end # Checks if cache is valid (in consistent state). # It delegates work to specified #guards # # For any unexpected case it returns true. # # @return true if cache is valid and in consistent state def expired? return !guards.all?(&:consistent?) rescue => exception raise exception if Rails.env.test? return true end # Checks if the cache is in consistent state and cached value # can be returned. # In such case it returns cached value. # Otherwise it evaluates passed block and updates the cache. # # @see {#expired?} # @yieldreturn value to be used in case of cache miss # @return cached value or the return value of the block def fetch(&block) if expired? value = yield begin cache.write(component.cache_key, value) ensure_consistency! return value rescue => exception raise exception if Rails.env.test? return value end else cache.read(component.cache_key) end end # @private # @return [CacheStorage] instance of CacheStorage to use def cache CacheStorage.instance end # @private def ensure_consistency! guards.each(&:guard!) end end end