ngmoco-cache-money 0.2.23 → 0.2.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +30 -34
 - data/README.markdown +30 -34
 - data/config/environment.rb +0 -8
 - data/config/memcached.yml +0 -2
 - data/init.rb +1 -1
 - data/lib/cache_money.rb +55 -36
 - data/lib/cash/adapter/memcache_client.rb +36 -0
 - data/lib/cash/adapter/memcached.rb +131 -0
 - data/lib/cash/adapter/redis.rb +152 -0
 - data/lib/cash/config.rb +7 -4
 - data/lib/cash/finders.rb +15 -3
 - data/lib/cash/index.rb +3 -6
 - data/lib/cash/local.rb +1 -1
 - data/lib/cash/lock.rb +2 -2
 - data/lib/cash/mock.rb +16 -12
 - data/lib/cash/query/abstract.rb +29 -3
 - data/lib/cash/version.rb +3 -0
 - data/lib/cash/write_through.rb +9 -5
 - data/lib/mem_cached_support_store.rb +1 -1
 - data/rails/init.rb +1 -40
 - data/spec/cash/calculations_spec.rb +11 -0
 - data/spec/cash/finders_spec.rb +4 -4
 - data/spec/cash/lock_spec.rb +30 -24
 - data/spec/cash/marshal_spec.rb +1 -1
 - data/spec/cash/transactional_spec.rb +19 -17
 - data/spec/cash/without_caching_spec.rb +32 -0
 - data/spec/cash/write_through_spec.rb +7 -0
 - data/spec/spec_helper.rb +32 -6
 - metadata +136 -45
 - data/lib/memcached_wrapper.rb +0 -263
 - data/spec/memcached_wrapper_test.rb +0 -209
 
    
        data/README
    CHANGED
    
    | 
         @@ -117,9 +117,7 @@ Nested transactions are fully supported, with partial rollback and (apparent) pa 
     | 
|
| 
       117 
117 
     | 
    
         | 
| 
       118 
118 
     | 
    
         
             
            ### Mocks ###
         
     | 
| 
       119 
119 
     | 
    
         | 
| 
       120 
     | 
    
         
            -
            For your unit tests, it is faster to use a Memcached mock than the real deal.  
     | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
       122 
     | 
    
         
            -
                $memcache = Cash::Mock.new
         
     | 
| 
      
 120 
     | 
    
         
            +
            For your unit tests, it is faster to use a Memcached mock than the real deal. In your test environment, initialize the repository with an instance of Cash::Mock.
         
     | 
| 
       123 
121 
     | 
    
         | 
| 
       124 
122 
     | 
    
         
             
            ### Locks ###
         
     | 
| 
       125 
123 
     | 
    
         | 
| 
         @@ -133,39 +131,41 @@ In most cases locks are unnecessary; the transactional Memcached client will tak 
     | 
|
| 
       133 
131 
     | 
    
         | 
| 
       134 
132 
     | 
    
         
             
            Sometimes your code will request the same cache key twice in one request. You can avoid a round trip to the Memcached server by using a local, per-request cache. Add this to your initializer:
         
     | 
| 
       135 
133 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
      
 134 
     | 
    
         
            +
                $memcache = MemcachedWrapper.new(config[:servers].gsub(' ', '').split(','), config)
         
     | 
| 
      
 135 
     | 
    
         
            +
                $local = Cash::Local.new($memcache)
         
     | 
| 
      
 136 
     | 
    
         
            +
                $lock  = Cash::Lock.new($memcache)
         
     | 
| 
      
 137 
     | 
    
         
            +
                $cache = Cash::Transactional.new($local, $lock)
         
     | 
| 
      
 138 
     | 
    
         
            +
                
         
     | 
| 
       139 
139 
     | 
    
         
             
            ## Installation ##
         
     | 
| 
       140 
140 
     | 
    
         | 
| 
       141 
     | 
    
         
            -
            #### Step 0: Install MemCached
         
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
141 
     | 
    
         
             
            #### Step 1: Get the GEM ####
         
     | 
| 
       144 
142 
     | 
    
         | 
| 
       145 
     | 
    
         
            -
                % gem sources -a http://gems.github.com
         
     | 
| 
       146 
143 
     | 
    
         
             
                % sudo gem install ngmoco-cache-money
         
     | 
| 
       147 
144 
     | 
    
         | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
      
 145 
     | 
    
         
            +
                Add the gem you your Gemfile:
         
     | 
| 
      
 146 
     | 
    
         
            +
                gem 'ngmoco-cache-money', :lib => 'cache_money'
         
     | 
| 
      
 147 
     | 
    
         
            +
                
         
     | 
| 
      
 148 
     | 
    
         
            +
            #### Step 2: Configure cache client
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
            In your environment, create a cache client instance configured for your cache servers.
         
     | 
| 
      
 151 
     | 
    
         
            +
              
         
     | 
| 
      
 152 
     | 
    
         
            +
                $memcached = Memcached.new( ...servers..., ...options...)
         
     | 
| 
       149 
153 
     | 
    
         | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
      
 154 
     | 
    
         
            +
            Currently supported cache clients are: memcached, memcache-client
         
     | 
| 
       151 
155 
     | 
    
         | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
                  ttl: 604800
         
     | 
| 
       154 
     | 
    
         
            -
                  namespace: ...
         
     | 
| 
       155 
     | 
    
         
            -
                  sessions: false
         
     | 
| 
       156 
     | 
    
         
            -
                  debug: false
         
     | 
| 
       157 
     | 
    
         
            -
                  servers: localhost:11211
         
     | 
| 
       158 
     | 
    
         
            -
                  cache_money: true
         
     | 
| 
      
 156 
     | 
    
         
            +
            #### Step 3: Configure Caching
         
     | 
| 
       159 
157 
     | 
    
         | 
| 
       160 
     | 
    
         
            -
             
     | 
| 
       161 
     | 
    
         
            -
                   ....
         
     | 
| 
      
 158 
     | 
    
         
            +
            Add the following to an initializer:
         
     | 
| 
       162 
159 
     | 
    
         | 
| 
       163 
     | 
    
         
            -
             
     | 
| 
       164 
     | 
    
         
            -
             config.gem "ngmoco-cache-money",
         
     | 
| 
       165 
     | 
    
         
            -
               :lib => "cache_money",
         
     | 
| 
       166 
     | 
    
         
            -
               :source => 'http://gems.github.com',
         
     | 
| 
       167 
     | 
    
         
            -
               :version => '0.2.9'
         
     | 
| 
      
 160 
     | 
    
         
            +
                Cash.configure :repository => $memcached, :adapter => :memcached
         
     | 
| 
       168 
161 
     | 
    
         | 
| 
      
 162 
     | 
    
         
            +
            Supported adapters are :memcache_client, :memcached. :memcached is assumed and is only compatible with Memcached clients.
         
     | 
| 
      
 163 
     | 
    
         
            +
            Local or transactional semantics may be disabled by setting :local => false or :transactional => false.
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
            Caching can be disabled on a per-environment basis in the environment's initializer:
         
     | 
| 
      
 166 
     | 
    
         
            +
                
         
     | 
| 
      
 167 
     | 
    
         
            +
                Cash.enabled = false
         
     | 
| 
      
 168 
     | 
    
         
            +
                
         
     | 
| 
       169 
169 
     | 
    
         
             
            #### Step 4: Add indices to your ActiveRecord models ####
         
     | 
| 
       170 
170 
     | 
    
         | 
| 
       171 
171 
     | 
    
         
             
            Queries like `User.find(1)` will use the cache automatically. For more complex queries you must add indices on the attributes that you will query on. For example, a query like `User.find(:all, :conditions => {:name => 'bob'})` will require an index like:
         
     | 
| 
         @@ -187,24 +187,20 @@ There may be times where you only want to cache some of your models instead of e 
     | 
|
| 
       187 
187 
     | 
    
         
             
            In that case, you can omit the following from your `config/initializers/cache_money.rb`
         
     | 
| 
       188 
188 
     | 
    
         | 
| 
       189 
189 
     | 
    
         
             
            	class ActiveRecord::Base
         
     | 
| 
       190 
     | 
    
         
            -
            	  is_cached 
     | 
| 
      
 190 
     | 
    
         
            +
            	  is_cached
         
     | 
| 
       191 
191 
     | 
    
         
             
            	end
         
     | 
| 
       192 
192 
     | 
    
         | 
| 
       193 
193 
     | 
    
         
             
            After that is removed, you can simple put this at the top of your models you wish to cache:
         
     | 
| 
       194 
194 
     | 
    
         | 
| 
       195 
     | 
    
         
            -
            	is_cached 
     | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
     | 
    
         
            -
            Just make sure that you put that line before any of your index directives.
         
     | 
| 
       198 
     | 
    
         
            -
             
     | 
| 
       199 
     | 
    
         
            -
            ## Version ##
         
     | 
| 
      
 195 
     | 
    
         
            +
            	is_cached
         
     | 
| 
       200 
196 
     | 
    
         | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
      
 197 
     | 
    
         
            +
            Just make sure that you put that line before any of your index directives. Note that all subclasses of a cached model are also cached.
         
     | 
| 
       202 
198 
     | 
    
         | 
| 
       203 
199 
     | 
    
         
             
            ## Acknowledgments ##
         
     | 
| 
       204 
200 
     | 
    
         | 
| 
       205 
201 
     | 
    
         
             
            Thanks to
         
     | 
| 
       206 
202 
     | 
    
         | 
| 
       207 
203 
     | 
    
         
             
             * Twitter for commissioning the development of this library and supporting the effort to open-source it.
         
     | 
| 
       208 
     | 
    
         
            -
             * Sam Luckenbill for pairing with  
     | 
| 
      
 204 
     | 
    
         
            +
             * Sam Luckenbill for pairing with Nick on most of the hard stuff.
         
     | 
| 
       209 
205 
     | 
    
         
             
             * Matthew and Chris for pairing a few days, offering useful feedback on the readability of the code, and the initial implementation of the Memcached mock.
         
     | 
| 
       210 
     | 
    
         
            -
             * Evan Weaver for helping to reason-through software and testing strategies to deal with replication lag, and the initial implementation of the Memcached lock.
         
     | 
| 
      
 206 
     | 
    
         
            +
             * Evan Weaver for helping to reason-through software and testing strategies to deal with replication lag, and the initial implementation of the Memcached lock.
         
     | 
    
        data/README.markdown
    CHANGED
    
    | 
         @@ -117,9 +117,7 @@ Nested transactions are fully supported, with partial rollback and (apparent) pa 
     | 
|
| 
       117 
117 
     | 
    
         | 
| 
       118 
118 
     | 
    
         
             
            ### Mocks ###
         
     | 
| 
       119 
119 
     | 
    
         | 
| 
       120 
     | 
    
         
            -
            For your unit tests, it is faster to use a Memcached mock than the real deal.  
     | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
       122 
     | 
    
         
            -
                $memcache = Cash::Mock.new
         
     | 
| 
      
 120 
     | 
    
         
            +
            For your unit tests, it is faster to use a Memcached mock than the real deal. In your test environment, initialize the repository with an instance of Cash::Mock.
         
     | 
| 
       123 
121 
     | 
    
         | 
| 
       124 
122 
     | 
    
         
             
            ### Locks ###
         
     | 
| 
       125 
123 
     | 
    
         | 
| 
         @@ -133,39 +131,41 @@ In most cases locks are unnecessary; the transactional Memcached client will tak 
     | 
|
| 
       133 
131 
     | 
    
         | 
| 
       134 
132 
     | 
    
         
             
            Sometimes your code will request the same cache key twice in one request. You can avoid a round trip to the Memcached server by using a local, per-request cache. Add this to your initializer:
         
     | 
| 
       135 
133 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
      
 134 
     | 
    
         
            +
                $memcache = MemcachedWrapper.new(config[:servers].gsub(' ', '').split(','), config)
         
     | 
| 
      
 135 
     | 
    
         
            +
                $local = Cash::Local.new($memcache)
         
     | 
| 
      
 136 
     | 
    
         
            +
                $lock  = Cash::Lock.new($memcache)
         
     | 
| 
      
 137 
     | 
    
         
            +
                $cache = Cash::Transactional.new($local, $lock)
         
     | 
| 
      
 138 
     | 
    
         
            +
                
         
     | 
| 
       139 
139 
     | 
    
         
             
            ## Installation ##
         
     | 
| 
       140 
140 
     | 
    
         | 
| 
       141 
     | 
    
         
            -
            #### Step 0: Install MemCached
         
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
141 
     | 
    
         
             
            #### Step 1: Get the GEM ####
         
     | 
| 
       144 
142 
     | 
    
         | 
| 
       145 
     | 
    
         
            -
                % gem sources -a http://gems.github.com
         
     | 
| 
       146 
143 
     | 
    
         
             
                % sudo gem install ngmoco-cache-money
         
     | 
| 
       147 
144 
     | 
    
         | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
      
 145 
     | 
    
         
            +
                Add the gem you your Gemfile:
         
     | 
| 
      
 146 
     | 
    
         
            +
                gem 'ngmoco-cache-money', :lib => 'cache_money'
         
     | 
| 
      
 147 
     | 
    
         
            +
                
         
     | 
| 
      
 148 
     | 
    
         
            +
            #### Step 2: Configure cache client
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
            In your environment, create a cache client instance configured for your cache servers.
         
     | 
| 
      
 151 
     | 
    
         
            +
              
         
     | 
| 
      
 152 
     | 
    
         
            +
                $memcached = Memcached.new( ...servers..., ...options...)
         
     | 
| 
       149 
153 
     | 
    
         | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
      
 154 
     | 
    
         
            +
            Currently supported cache clients are: memcached, memcache-client
         
     | 
| 
       151 
155 
     | 
    
         | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
                  ttl: 604800
         
     | 
| 
       154 
     | 
    
         
            -
                  namespace: ...
         
     | 
| 
       155 
     | 
    
         
            -
                  sessions: false
         
     | 
| 
       156 
     | 
    
         
            -
                  debug: false
         
     | 
| 
       157 
     | 
    
         
            -
                  servers: localhost:11211
         
     | 
| 
       158 
     | 
    
         
            -
                  cache_money: true
         
     | 
| 
      
 156 
     | 
    
         
            +
            #### Step 3: Configure Caching
         
     | 
| 
       159 
157 
     | 
    
         | 
| 
       160 
     | 
    
         
            -
             
     | 
| 
       161 
     | 
    
         
            -
                   ....
         
     | 
| 
      
 158 
     | 
    
         
            +
            Add the following to an initializer:
         
     | 
| 
       162 
159 
     | 
    
         | 
| 
       163 
     | 
    
         
            -
             
     | 
| 
       164 
     | 
    
         
            -
             config.gem "ngmoco-cache-money",
         
     | 
| 
       165 
     | 
    
         
            -
               :lib => "cache_money",
         
     | 
| 
       166 
     | 
    
         
            -
               :source => 'http://gems.github.com',
         
     | 
| 
       167 
     | 
    
         
            -
               :version => '0.2.9'
         
     | 
| 
      
 160 
     | 
    
         
            +
                Cash.configure :repository => $memcached, :adapter => :memcached
         
     | 
| 
       168 
161 
     | 
    
         | 
| 
      
 162 
     | 
    
         
            +
            Supported adapters are :memcache_client, :memcached. :memcached is assumed and is only compatible with Memcached clients.
         
     | 
| 
      
 163 
     | 
    
         
            +
            Local or transactional semantics may be disabled by setting :local => false or :transactional => false.
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
            Caching can be disabled on a per-environment basis in the environment's initializer:
         
     | 
| 
      
 166 
     | 
    
         
            +
                
         
     | 
| 
      
 167 
     | 
    
         
            +
                Cash.enabled = false
         
     | 
| 
      
 168 
     | 
    
         
            +
                
         
     | 
| 
       169 
169 
     | 
    
         
             
            #### Step 4: Add indices to your ActiveRecord models ####
         
     | 
| 
       170 
170 
     | 
    
         | 
| 
       171 
171 
     | 
    
         
             
            Queries like `User.find(1)` will use the cache automatically. For more complex queries you must add indices on the attributes that you will query on. For example, a query like `User.find(:all, :conditions => {:name => 'bob'})` will require an index like:
         
     | 
| 
         @@ -187,24 +187,20 @@ There may be times where you only want to cache some of your models instead of e 
     | 
|
| 
       187 
187 
     | 
    
         
             
            In that case, you can omit the following from your `config/initializers/cache_money.rb`
         
     | 
| 
       188 
188 
     | 
    
         | 
| 
       189 
189 
     | 
    
         
             
            	class ActiveRecord::Base
         
     | 
| 
       190 
     | 
    
         
            -
            	  is_cached 
     | 
| 
      
 190 
     | 
    
         
            +
            	  is_cached
         
     | 
| 
       191 
191 
     | 
    
         
             
            	end
         
     | 
| 
       192 
192 
     | 
    
         | 
| 
       193 
193 
     | 
    
         
             
            After that is removed, you can simple put this at the top of your models you wish to cache:
         
     | 
| 
       194 
194 
     | 
    
         | 
| 
       195 
     | 
    
         
            -
            	is_cached 
     | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
     | 
    
         
            -
            Just make sure that you put that line before any of your index directives.
         
     | 
| 
       198 
     | 
    
         
            -
             
     | 
| 
       199 
     | 
    
         
            -
            ## Version ##
         
     | 
| 
      
 195 
     | 
    
         
            +
            	is_cached
         
     | 
| 
       200 
196 
     | 
    
         | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
      
 197 
     | 
    
         
            +
            Just make sure that you put that line before any of your index directives. Note that all subclasses of a cached model are also cached.
         
     | 
| 
       202 
198 
     | 
    
         | 
| 
       203 
199 
     | 
    
         
             
            ## Acknowledgments ##
         
     | 
| 
       204 
200 
     | 
    
         | 
| 
       205 
201 
     | 
    
         
             
            Thanks to
         
     | 
| 
       206 
202 
     | 
    
         | 
| 
       207 
203 
     | 
    
         
             
             * Twitter for commissioning the development of this library and supporting the effort to open-source it.
         
     | 
| 
       208 
     | 
    
         
            -
             * Sam Luckenbill for pairing with  
     | 
| 
      
 204 
     | 
    
         
            +
             * Sam Luckenbill for pairing with Nick on most of the hard stuff.
         
     | 
| 
       209 
205 
     | 
    
         
             
             * Matthew and Chris for pairing a few days, offering useful feedback on the readability of the code, and the initial implementation of the Memcached mock.
         
     | 
| 
       210 
     | 
    
         
            -
             * Evan Weaver for helping to reason-through software and testing strategies to deal with replication lag, and the initial implementation of the Memcached lock.
         
     | 
| 
      
 206 
     | 
    
         
            +
             * Evan Weaver for helping to reason-through software and testing strategies to deal with replication lag, and the initial implementation of the Memcached lock.
         
     | 
    
        data/config/environment.rb
    CHANGED
    
    | 
         @@ -1,14 +1,6 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'rubygems'
         
     | 
| 
       2 
     | 
    
         
            -
            gem 'activesupport', '~> 2.3.0'
         
     | 
| 
       3 
     | 
    
         
            -
            gem 'activerecord', '~> 2.3.0'
         
     | 
| 
       4 
     | 
    
         
            -
            gem 'actionpack', '~> 2.3.0'
         
     | 
| 
       5 
     | 
    
         
            -
            gem 'rspec', '>= 1.3.0'
         
     | 
| 
       6 
     | 
    
         
            -
            gem 'jeweler', '~> 1.4.0'
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
1 
     | 
    
         
             
            require 'action_controller'
         
     | 
| 
       9 
2 
     | 
    
         
             
            require 'active_record'
         
     | 
| 
       10 
3 
     | 
    
         
             
            require 'active_record/session_store'
         
     | 
| 
       11 
     | 
    
         
            -
            require 'jeweler'
         
     | 
| 
       12 
4 
     | 
    
         | 
| 
       13 
5 
     | 
    
         
             
            ActiveRecord::Base.establish_connection(
         
     | 
| 
       14 
6 
     | 
    
         
             
              :adapter => 'sqlite3',
         
     | 
    
        data/config/memcached.yml
    CHANGED
    
    
    
        data/init.rb
    CHANGED
    
    | 
         @@ -1 +1 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require  
     | 
| 
      
 1 
     | 
    
         
            +
            require 'rails/init'
         
     | 
    
        data/lib/cache_money.rb
    CHANGED
    
    | 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'active_support'
         
     | 
| 
       2 
2 
     | 
    
         
             
            require 'active_record'
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
      
 4 
     | 
    
         
            +
            require 'cash/version'
         
     | 
| 
       4 
5 
     | 
    
         
             
            require 'cash/lock'
         
     | 
| 
       5 
6 
     | 
    
         
             
            require 'cash/transactional'
         
     | 
| 
       6 
7 
     | 
    
         
             
            require 'cash/write_through'
         
     | 
| 
         @@ -22,27 +23,31 @@ require 'cash/query/calculation' 
     | 
|
| 
       22 
23 
     | 
    
         
             
            require 'cash/util/array'
         
     | 
| 
       23 
24 
     | 
    
         
             
            require 'cash/util/marshal'
         
     | 
| 
       24 
25 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
               
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                 
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                if  
     | 
| 
       38 
     | 
    
         
            -
                   
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                   
     | 
| 
      
 26 
     | 
    
         
            +
            module Cash
         
     | 
| 
      
 27 
     | 
    
         
            +
              mattr_accessor :enabled
         
     | 
| 
      
 28 
     | 
    
         
            +
              self.enabled = true
         
     | 
| 
      
 29 
     | 
    
         
            +
              
         
     | 
| 
      
 30 
     | 
    
         
            +
              mattr_accessor :repository
         
     | 
| 
      
 31 
     | 
    
         
            +
              
         
     | 
| 
      
 32 
     | 
    
         
            +
              def self.configure(options = {})
         
     | 
| 
      
 33 
     | 
    
         
            +
                options.assert_valid_keys(:repository, :local, :transactional, :adapter, :default_ttl)
         
     | 
| 
      
 34 
     | 
    
         
            +
                cache = options[:repository] || raise(":repository is a required option")
         
     | 
| 
      
 35 
     | 
    
         
            +
                
         
     | 
| 
      
 36 
     | 
    
         
            +
                adapter = options.fetch(:adapter, :memcached)
         
     | 
| 
      
 37 
     | 
    
         
            +
                
         
     | 
| 
      
 38 
     | 
    
         
            +
                if adapter
         
     | 
| 
      
 39 
     | 
    
         
            +
                  require "cash/adapter/#{adapter.to_s}"
         
     | 
| 
      
 40 
     | 
    
         
            +
                  klass = "Cash::Adapter::#{adapter.to_s.camelize}".constantize
         
     | 
| 
      
 41 
     | 
    
         
            +
                  cache = klass.new(cache, :logger => Rails.logger, :default_ttl => options.fetch(:default_ttl, 1.day.to_i))
         
     | 
| 
       41 
42 
     | 
    
         
             
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
                
         
     | 
| 
      
 44 
     | 
    
         
            +
                lock  = Cash::Lock.new(cache)
         
     | 
| 
      
 45 
     | 
    
         
            +
                cache = Cash::Local.new(cache) if options.fetch(:local, true)
         
     | 
| 
      
 46 
     | 
    
         
            +
                cache = Cash::Transactional.new(cache, lock) if options.fetch(:transactional, true)
         
     | 
| 
      
 47 
     | 
    
         
            +
                
         
     | 
| 
      
 48 
     | 
    
         
            +
                self.repository = cache
         
     | 
| 
       42 
49 
     | 
    
         
             
              end
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
            module Cash
         
     | 
| 
      
 50 
     | 
    
         
            +
              
         
     | 
| 
       46 
51 
     | 
    
         
             
              def self.included(active_record_class)
         
     | 
| 
       47 
52 
     | 
    
         
             
                active_record_class.class_eval do
         
     | 
| 
       48 
53 
     | 
    
         
             
                  include Config, Accessor, WriteThrough, Finders
         
     | 
| 
         @@ -50,6 +55,12 @@ module Cash 
     | 
|
| 
       50 
55 
     | 
    
         
             
                end
         
     | 
| 
       51 
56 
     | 
    
         
             
              end
         
     | 
| 
       52 
57 
     | 
    
         | 
| 
      
 58 
     | 
    
         
            +
              private
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                def self.repository
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @@repository || raise("Cash.configure must be called when Cash.enabled is true")
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
              
         
     | 
| 
       53 
64 
     | 
    
         
             
              module ClassMethods
         
     | 
| 
       54 
65 
     | 
    
         
             
                def self.extended(active_record_class)
         
     | 
| 
       55 
66 
     | 
    
         
             
                  class << active_record_class
         
     | 
| 
         @@ -57,30 +68,38 @@ module Cash 
     | 
|
| 
       57 
68 
     | 
    
         
             
                  end
         
     | 
| 
       58 
69 
     | 
    
         
             
                end
         
     | 
| 
       59 
70 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
                def transaction_with_cache_transaction(*args)
         
     | 
| 
       61 
     | 
    
         
            -
                  if  
     | 
| 
       62 
     | 
    
         
            -
                     
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
      
 71 
     | 
    
         
            +
                def transaction_with_cache_transaction(*args, &block)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  if Cash.enabled
         
     | 
| 
      
 73 
     | 
    
         
            +
                    # Wrap both the db and cache transaction in another cache transaction so that the cache 
         
     | 
| 
      
 74 
     | 
    
         
            +
                    # gets written only after the database commit but can still flush the inner cache
         
     | 
| 
      
 75 
     | 
    
         
            +
                    # transaction if an AR::Rollback is issued.
         
     | 
| 
      
 76 
     | 
    
         
            +
                    Cash.repository.transaction do
         
     | 
| 
      
 77 
     | 
    
         
            +
                      transaction_without_cache_transaction(*args) do
         
     | 
| 
      
 78 
     | 
    
         
            +
                        Cash.repository.transaction { block.call }
         
     | 
| 
      
 79 
     | 
    
         
            +
                      end
         
     | 
| 
       64 
80 
     | 
    
         
             
                    end
         
     | 
| 
       65 
81 
     | 
    
         
             
                  else
         
     | 
| 
       66 
     | 
    
         
            -
                    transaction_without_cache_transaction(*args)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    transaction_without_cache_transaction(*args, &block)
         
     | 
| 
       67 
83 
     | 
    
         
             
                  end
         
     | 
| 
       68 
84 
     | 
    
         
             
                end
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                def cacheable?(*args)
         
     | 
| 
       71 
     | 
    
         
            -
                  true
         
     | 
| 
       72 
     | 
    
         
            -
                end
         
     | 
| 
       73 
85 
     | 
    
         
             
              end
         
     | 
| 
       74 
86 
     | 
    
         
             
            end
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            class ActiveRecord::Base
         
     | 
| 
      
 89 
     | 
    
         
            +
              include Cash
         
     | 
| 
      
 90 
     | 
    
         
            +
              
         
     | 
| 
      
 91 
     | 
    
         
            +
              def self.is_cached(options = {})
         
     | 
| 
      
 92 
     | 
    
         
            +
                options.assert_valid_keys(:ttl, :repository, :version)
         
     | 
| 
      
 93 
     | 
    
         
            +
                opts = options.dup
         
     | 
| 
      
 94 
     | 
    
         
            +
                opts[:repository] = Cash.repository unless opts.has_key?(:repository)
         
     | 
| 
      
 95 
     | 
    
         
            +
                Cash::Config.create(self, opts)
         
     | 
| 
       80 
96 
     | 
    
         
             
              end
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
              def <=>(other)
         
     | 
| 
      
 99 
     | 
    
         
            +
                if self.id == other.id then 
         
     | 
| 
      
 100 
     | 
    
         
            +
                  0
         
     | 
| 
      
 101 
     | 
    
         
            +
                else
         
     | 
| 
      
 102 
     | 
    
         
            +
                  self.id < other.id ? -1 : 1
         
     | 
| 
       84 
103 
     | 
    
         
             
                end
         
     | 
| 
       85 
104 
     | 
    
         
             
              end
         
     | 
| 
       86 
105 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'memcache'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Cash
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Adapter
         
     | 
| 
      
 5 
     | 
    
         
            +
                class MemcacheClient
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(repository, options = {})
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @repository = repository
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @logger = options[:logger]
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @default_ttl = options[:default_ttl] || raise(":default_ttl is a required option")
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def add(key, value, ttl=nil, raw=false)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @repository.add(key, value, ttl || @default_ttl, raw)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                  
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def set(key, value, ttl=nil, raw=false)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @repository.set(key, value, ttl || @default_ttl, raw)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def exception_classes
         
     | 
| 
      
 21 
     | 
    
         
            +
                    MemCache::MemCacheError
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def respond_to?(method)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    super || @repository.respond_to?(method)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  
         
     | 
| 
      
 28 
     | 
    
         
            +
                  private
         
     | 
| 
      
 29 
     | 
    
         
            +
                  
         
     | 
| 
      
 30 
     | 
    
         
            +
                    def method_missing(*args, &block)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      @repository.send(*args, &block)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,131 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'memcached'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Maps memcached methods and semantics to those of memcache-client
         
     | 
| 
      
 4 
     | 
    
         
            +
            module Cash
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Adapter
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Memcached
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(repository, options = {})
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @repository = repository
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @logger = options[:logger]
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @default_ttl = options[:default_ttl] || raise(":default_ttl is a required option")   
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
                  
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def add(key, value, ttl=nil, raw=false)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    wrap(key, not_stored) do
         
     | 
| 
      
 15 
     | 
    
         
            +
                      logger.debug("Memcached add: #{key.inspect}") if debug_logger?
         
     | 
| 
      
 16 
     | 
    
         
            +
                      @repository.add(key, raw ? value.to_s : value, ttl || @default_ttl, !raw)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      logger.debug("Memcached hit: #{key.inspect}") if debug_logger?
         
     | 
| 
      
 18 
     | 
    
         
            +
                      stored
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # Wraps Memcached#get so that it doesn't raise. This has the side-effect of preventing you from 
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # storing <tt>nil</tt> values.
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def get(key, raw=false)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    wrap(key) do
         
     | 
| 
      
 26 
     | 
    
         
            +
                      logger.debug("Memcached get: #{key.inspect}") if debug_logger?
         
     | 
| 
      
 27 
     | 
    
         
            +
                      value = wrap(key) { @repository.get(key, !raw) }
         
     | 
| 
      
 28 
     | 
    
         
            +
                      logger.debug("Memcached hit: #{key.inspect}") if debug_logger?
         
     | 
| 
      
 29 
     | 
    
         
            +
                      value
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def get_multi(*keys)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    wrap(keys, {}) do
         
     | 
| 
      
 35 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 36 
     | 
    
         
            +
                        keys.flatten!
         
     | 
| 
      
 37 
     | 
    
         
            +
                        logger.debug("Memcached get_multi: #{keys.inspect}") if debug_logger?
         
     | 
| 
      
 38 
     | 
    
         
            +
                        values = @repository.get(keys, true)
         
     | 
| 
      
 39 
     | 
    
         
            +
                        logger.debug("Memcached hit: #{keys.inspect}") if debug_logger?
         
     | 
| 
      
 40 
     | 
    
         
            +
                        values
         
     | 
| 
      
 41 
     | 
    
         
            +
                      rescue TypeError
         
     | 
| 
      
 42 
     | 
    
         
            +
                        log_error($!) if logger
         
     | 
| 
      
 43 
     | 
    
         
            +
                        keys.each { |key| delete(key) }
         
     | 
| 
      
 44 
     | 
    
         
            +
                        logger.debug("Memcached deleted: #{keys.inspect}") if debug_logger?
         
     | 
| 
      
 45 
     | 
    
         
            +
                        {}
         
     | 
| 
      
 46 
     | 
    
         
            +
                      end
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                  
         
     | 
| 
      
 50 
     | 
    
         
            +
                  def set(key, value, ttl=nil, raw=false)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    wrap(key, not_stored) do
         
     | 
| 
      
 52 
     | 
    
         
            +
                      logger.debug("Memcached set: #{key.inspect}") if debug_logger?
         
     | 
| 
      
 53 
     | 
    
         
            +
                      @repository.set(key, raw ? value.to_s : value, ttl || @default_ttl, !raw)
         
     | 
| 
      
 54 
     | 
    
         
            +
                      logger.debug("Memcached hit: #{key.inspect}") if debug_logger?
         
     | 
| 
      
 55 
     | 
    
         
            +
                      stored
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def delete(key)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    wrap(key, not_found) do
         
     | 
| 
      
 61 
     | 
    
         
            +
                      logger.debug("Memcached delete: #{key.inspect}") if debug_logger?
         
     | 
| 
      
 62 
     | 
    
         
            +
                      @repository.delete(key)
         
     | 
| 
      
 63 
     | 
    
         
            +
                      logger.debug("Memcached hit: #{key.inspect}") if debug_logger?
         
     | 
| 
      
 64 
     | 
    
         
            +
                      deleted
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  
         
     | 
| 
      
 68 
     | 
    
         
            +
                  def get_server_for_key(key)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    wrap(key) { @repository.server_by_key(key) }
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  def incr(key, value = 1)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    wrap(key) { @repository.incr(key, value) }
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  def decr(key, value = 1)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    wrap(key) { @repository.decr(key, value) }
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
                  
         
     | 
| 
      
 80 
     | 
    
         
            +
                  def flush_all
         
     | 
| 
      
 81 
     | 
    
         
            +
                    @repository.flush
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  
         
     | 
| 
      
 84 
     | 
    
         
            +
                  def exception_classes
         
     | 
| 
      
 85 
     | 
    
         
            +
                    ::Memcached::Error
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                  
         
     | 
| 
      
 88 
     | 
    
         
            +
                  private
         
     | 
| 
      
 89 
     | 
    
         
            +
                  
         
     | 
| 
      
 90 
     | 
    
         
            +
                    def logger
         
     | 
| 
      
 91 
     | 
    
         
            +
                      @logger
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
                    
         
     | 
| 
      
 94 
     | 
    
         
            +
                    def debug_logger?
         
     | 
| 
      
 95 
     | 
    
         
            +
                      logger && logger.respond_to?(:debug?) && logger.debug?
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
                    
         
     | 
| 
      
 98 
     | 
    
         
            +
                    def wrap(key, error_value = nil, options = {})
         
     | 
| 
      
 99 
     | 
    
         
            +
                      yield
         
     | 
| 
      
 100 
     | 
    
         
            +
                    rescue ::Memcached::NotStored
         
     | 
| 
      
 101 
     | 
    
         
            +
                      logger.debug("Memcached miss: #{key.inspect}") if debug_logger?
         
     | 
| 
      
 102 
     | 
    
         
            +
                      error_value
         
     | 
| 
      
 103 
     | 
    
         
            +
                    rescue ::Memcached::Error
         
     | 
| 
      
 104 
     | 
    
         
            +
                      log_error($!) if logger
         
     | 
| 
      
 105 
     | 
    
         
            +
                      raise if options[:reraise_error]
         
     | 
| 
      
 106 
     | 
    
         
            +
                      error_value
         
     | 
| 
      
 107 
     | 
    
         
            +
                    end
         
     | 
| 
      
 108 
     | 
    
         
            +
                    
         
     | 
| 
      
 109 
     | 
    
         
            +
                    def stored
         
     | 
| 
      
 110 
     | 
    
         
            +
                      "STORED\r\n"
         
     | 
| 
      
 111 
     | 
    
         
            +
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                    def deleted
         
     | 
| 
      
 114 
     | 
    
         
            +
                      "DELETED\r\n"
         
     | 
| 
      
 115 
     | 
    
         
            +
                    end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                    def not_stored
         
     | 
| 
      
 118 
     | 
    
         
            +
                      "NOT_STORED\r\n"
         
     | 
| 
      
 119 
     | 
    
         
            +
                    end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    def not_found
         
     | 
| 
      
 122 
     | 
    
         
            +
                      "NOT_FOUND\r\n"
         
     | 
| 
      
 123 
     | 
    
         
            +
                    end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    def log_error(err)
         
     | 
| 
      
 126 
     | 
    
         
            +
                      #logger.error("#{err}: \n\t#{err.backtrace.join("\n\t")}") if logger
         
     | 
| 
      
 127 
     | 
    
         
            +
                      logger.error("Memcached ERROR, #{err.class}: #{err}") if logger
         
     | 
| 
      
 128 
     | 
    
         
            +
                    end
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
            end
         
     |