Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 126 lines (105 sloc) 3.868 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
require 'active_support/concern'
require 'active_support/cache'

module Cell
  module Caching
    extend ActiveSupport::Concern

    included do
      class_attribute :version_procs, :conditional_procs, :cache_options
      self.version_procs = {}
      self.conditional_procs = {}
      self.cache_options = {}
    end

    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
      #
      # The +:if+ option lets you define a conditional proc or instance method. If it doesn't
      # return a true value, caching for that state is skipped.
      #
      # cache :show, :if => proc { |cell, options| options[:enable_cache] }
      #
      # 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 <tt>cells/cart/show/user/1</tt>.
      #
      # 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 <em>appended</em> to the state cache key.
      # * You may return a string, a hash, an array, ActiveSupport::Caching will compile it.
      #
      # == Inheritance
      # Please note that cache configuration is inherited to derived cells.
      def cache(state, *args, &block)
        options = args.extract_options!

        self.conditional_procs = conditional_procs.merge(state => options.delete(:if))
        self.version_procs = version_procs.merge(state => (args.first || block))
        self.cache_options = cache_options.merge(state => options)
      end

      # Computes the complete, namespaced cache key for +state+.
      def state_cache_key(state, key_parts={})
        expand_cache_key([controller_path, state, key_parts])
      end

      def expire_cache_key_for(key, cache_store, *args)
        cache_store.delete(key, *args)
      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
    end


    def render_state(state, *args)
      return super(state, *args) unless cache?(state, *args)
      
      key = self.class.state_cache_key(state, call_state_versioner(state, *args))
      options = self.class.cache_options[state]
      
      cache_store.fetch(key, options) do
        super(state, *args)
      end
    end
    
    def cache_configured?
      @cache_configured
    end
    attr_writer :cache_configured
    
    attr_accessor :cache_store # we want to use DI to set a cache store in cell/rails.
      
    def cache?(state, *args)
      cache_configured? and state_cached?(state) and call_state_conditional(state, *args)
    end
    
  protected
    def state_cached?(state)
      self.class.version_procs.has_key?(state)
    end
      
    def call_proc_or_method(state, method, *args)
      return method.call(self, *args) if method.kind_of?(Proc)
      send(method, *args)
    end
    
    def call_state_versioner(state, *args)
      method = self.class.version_procs[state] or return
      call_proc_or_method(state, method, *args)
    end
    
    def call_state_conditional(state, *args)
      method = self.class.conditional_procs[state] or return true
      call_proc_or_method(state, method, *args)
    end
    
  end
end
Something went wrong with that request. Please try again.