arid_cache 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -17,6 +17,8 @@ tmtags
17
17
  coverage
18
18
  rdoc
19
19
  pkg
20
+ Gemfile.lock
21
+ .bundle/*
20
22
 
21
23
  ## PROJECT::SPECIFIC
22
24
  test/**/*.log
data/README.rdoc CHANGED
@@ -6,9 +6,29 @@ AridCache supports caching large, expensive ActiveRecord collections by caching
6
6
 
7
7
  AridCache simplifies caching by supporting auto-expiring cache keys - as well as common options like <tt>:expires_in</tt> - and provides methods to help you manage your caches at the global, model class, model instance and per-cache level.
8
8
 
9
+ == Changes
10
+
11
+ v1.0.5: Support <tt>:raw</tt> and <tt>:clear</tt> options.
12
+
9
13
  == Install
10
14
 
11
- Add this to your <tt>config/environment.rb</tt> file:
15
+ <b>Rails 3:</b>
16
+
17
+ Add the gem to your `Gemfile`
18
+
19
+ gem 'arid_cache'
20
+
21
+ Then
22
+
23
+ bundle install
24
+
25
+ For some reason AridCache is not being included into ActiveRecord, so add the following to an initializer to get around that until I fix it:
26
+
27
+ AridCache.init_rails
28
+
29
+ <b>Rails 2:</b>
30
+
31
+ Add the gem to your <tt>config/environment.rb</tt> file:
12
32
 
13
33
  config.gem 'arid_cache'
14
34
 
@@ -16,8 +36,6 @@ Then
16
36
 
17
37
  rake gems:install
18
38
 
19
- (Requires having GemCutter in your gem sources.)
20
-
21
39
  == Introduction
22
40
 
23
41
  The name AridCache comes from <b>A</b>ctive<b>R</b>ecord *ID* Cache. It's also very DRY...get it? :)
@@ -148,7 +166,7 @@ Or via the cache configuration:
148
166
  top_tracks(:auto_expire => true)
149
167
  end
150
168
 
151
- If you need to examine values in the cache yourself you can build the AridCache key by calling <tt>arid_cache_key('method')</tt> on your object. Using the examples above we would call,
169
+ If you need to examine values in the cache yourself you can build the AridCache key by calling <tt>arid_cache_key('method')</tt> on your object, whether it is a class or instance. Using the examples above we would call,
152
170
 
153
171
  Album.arid_cache_key('featured_albums') => arid-cache-album-featured_albums
154
172
  album.arid_cache_key('top_tracks') => arid-cache-albums/2-top_tracks
@@ -174,7 +192,12 @@ Alternatively you can pass a <b><tt>:force => true</tt></b> option in your <tt>c
174
192
  Album.cached_featured_albums(:force => true) => returns featured albums
175
193
  album.cached_top_tracks(:force => true) => returns top tracks
176
194
 
177
- You can pass an <b><tt>:expires_in</tt></b> option to your caches to manage your cache expiry (if your cache store supports this option).
195
+ If you just want to clear a cache without forcing a refresh pass <b><tt>:clear => true</tt></b>. The cached value will be deleted with no unnecessary queries or cache reads being performed. It is safe to pass this option even if there is nothing in the cache yet. The method returns the result of calling <tt>delete</tt> on your cache object. For example:
196
+
197
+ Album.cached_featured_albums(:clear => true) => returns false
198
+ Rails.cache.read(Album.arid_cache_key(:featured_albums)) => returns nil
199
+
200
+ You can pass an <b><tt>:expires_in</tt></b> option to your caches to manage your cache expiry (if your cache store supports this option, which most do).
178
201
 
179
202
  Album.cached_featured_albums(:expires_in => 1.day)
180
203
  album.cached_top_tracks(:expires_in => 1.day)
@@ -186,7 +209,7 @@ Or via the cache configuration,
186
209
  featured_albums
187
210
  end
188
211
 
189
- If you would like to be able to pass more options to your cache store (like <tt>:raw</tt>, <tt>:unless_exist</tt>, etc), just add them to the <tt>AridCache::CacheProxy::OPTIONS_FOR_CACHE</tt> class constant, for example
212
+ If you would like to be able to pass more options to your cache store (like <tt>:unless_exists</tt>, etc), just add them to the <tt>AridCache::CacheProxy::OPTIONS_FOR_CACHE</tt> class constant, for example
190
213
 
191
214
  AridCache::CacheProxy::OPTIONS_FOR_CACHE.push(:raw, :unless_exist)
192
215
 
@@ -224,6 +247,9 @@ An advantage of using AridCache is that since we already have the size of the co
224
247
 
225
248
  To paginate just pass a <tt>:page</tt> option in your call to <tt>cached_</tt>. If you don't pass a value for <tt>:per_page</tt> AridCache gets the value from <tt>Model.per_page</tt>, which is what <tt>WillPaginate</tt> uses.
226
249
 
250
+ The supported pagination options are:
251
+ :page, :per_page, :total_entries, :finder
252
+
227
253
  Some examples of pagination:
228
254
 
229
255
  User.cached_active(:page => 1, :per_page => 30)
@@ -263,14 +289,59 @@ You apply <tt>:limit</tt> and <tt>:offset</tt> options in a similar manner to th
263
289
 
264
290
  === Other Options to <tt>find</tt>
265
291
 
292
+ The supported options to <tt>find</tt> are:
293
+ :conditions, :include, :joins, :limit, :offset, :order,
294
+ :select, :readonly, :group, :having, :from, :lock
295
+
266
296
  You can pass options like <tt>:include</tt> (or any other valid <tt>find</tt> options) to augment the results of your cached query. Just because all of the options are supported, does not mean it's a good idea to use them, though. Take a look at your logs to see how AridCache is interacting with the cache and the database if you don't get the results you expect.
267
297
 
268
- For example, assume we have a <tt>named_scope :active</tt> on <tt>User</tt> which gives the active users. We can call:
298
+ For example, we could call:
299
+
300
+ User.cached_active(:page => 2, :per_page => 10, :include => :preferences)
301
+
302
+ To return page two of the active users, with the <tt>preferences</tt> association eager-loaded for all the users.
303
+
304
+ === Accessing the cached IDs directly
305
+
306
+ Sometimes you may want to access the cached list of record IDs without instantiating all the records. This can be useful, for example, to determine if a particular track belongs to a user's favorite tracks. If we have cached the list of favorite tracks, we just need to determine whether the track's ID appears in the cached list of IDs.
307
+
308
+ The cached result is a <tt>AridCache::CacheProxy::Result</tt> and can be accessed by passing the <b><tt>:raw => true</tt></b> option in your cached call. The <tt>AridCache::CacheProxy::Result</tt> is a type of <tt>Struct</tt> with methods to return the <tt>ids</tt>, <tt>count</tt> and <tt>klass</tt> of the cached records.
309
+
310
+ Note that passing the <tt>:raw</tt> option to your cache store is not supported, because the AridCache option shares the same name. If you really want to get the marshalled result from your cache you will have to use <tt>cache.read</tt>, manually passing in the AridCache key and <tt>:raw</tt> option.
311
+
312
+ Usage example:
269
313
 
270
- User.cached_active.paginate(:page => 2, :per_page => 10, :include => :preferences)
271
- User.cached_active(:limit => 10, :offset => 10, :include => :preferences)
314
+ user = User.first
315
+ user.cached_favorite_tracks => returns [#<Track:1>, #<Track:2>]
316
+ user.cached_favorite_tracks(:raw => true) => returns
317
+ {
318
+ :klass => "Track", # stored as a string
319
+ :count => 1,
320
+ :ids => [1, 2]
321
+ }
322
+ user.cached_favorite_tracks(:raw => true).ids => returns [1, 2]
272
323
 
273
- (These calls return similar results, except that the first call returns a <tt>WillPaginate::Collection</tt> and the second just returns an <tt>Array</tt>.)
324
+ The cache will be primed if it is empty, so you can be sure that it will always return a <tt>AridCache::CacheProxy::Result</tt>.
325
+
326
+ In some circumstances - like when you are querying on a named scope - if you have only requested a count, only the count is computed, which means the ids array is <tt>nil</tt>. When you call your cached method passing in <tt>:raw => true</tt> AridCache detects that the ids array has not yet been set, so in this case it will perform a query to seed the ids array before returning the result. This can be seen in the following example:
327
+
328
+ class User
329
+ named_scope :guests, :conditions => { :account_type => ['guest'] }
330
+ end
331
+
332
+ User.cached_guests_count => returns 4
333
+ Rails.cache.read(User.arid_cache_key(:guests)) => returns
334
+ {
335
+ :klass => "User",
336
+ :count => 4,
337
+ :ids => nil # notice the ids array is nil in the cache
338
+ }
339
+ User.cached_guests(:raw => true) => returns
340
+ {
341
+ :klass => "User",
342
+ :count => 4,
343
+ :ids => [2, 235, 236, 237] # the ids array is seeded before returning
344
+ }
274
345
 
275
346
  == Efficiency
276
347
 
@@ -281,13 +352,16 @@ For example, assume we have a <tt>named_scope :active</tt> on <tt>User</tt> whic
281
352
 
282
353
  == Compatibility
283
354
 
284
- Tested on Ruby 1.8.6, 1.8.7 and 1.9.1.
355
+ Tested on Ruby 1.8.6, 1.8.7, REE 1.8.7 and 1.9.1.
356
+ Tested in Rails 2.3.* and Rails 3
285
357
 
286
358
  For Ruby < 1.8.7 you probably want to include the following to extend the Array class with a <tt>count</tt> method. Otherwise your <tt>cached_<key>_count</tt> calls probably won't work:
287
359
 
288
360
  Array.class_eval { alias count size }
289
361
 
290
- The version of Rails shouldn't matter much, but it's working on 2.3.4.
362
+ For Rails 3 for some reason AridCache is not being included into ActiveRecord, so add the following to an initializer to get around that:
363
+
364
+ AridCache.init_rails
291
365
 
292
366
  == Resources & Metrics
293
367
 
@@ -300,14 +374,6 @@ The version of Rails shouldn't matter much, but it's working on 2.3.4.
300
374
  1. <b>Caches that contains duplicate records will only return unique records on subsequent calls</b>. This is because of the way <tt>find</tt> works when selecting multiple ids. For example, if your query returns <tt>[#<User id: 1>, #<User id: 1>, #<User id: 1>]</tt>, the IDs are cached as <tt>[1,1,1]</tt>. On the next call to the cache we load the IDs using <tt>User.find_all_by_id([1,1,1])</tt> which returns <tt>[#<User id: 1>]</tt>, not <tt>[#<User id: 1>, #<User id: 1>, #<User id: 1>]</tt> as you might have expected.
301
375
  2. <b>You can't cache polymorphic arrays</b> e.g. [#<User id: 1>, #<Pet id: 5>] because it expects all ActiveRecords to be of the same class. We could accept a <tt>:polymorphic => true</tt> option but I don't think this is a great idea because instantiating all the records would result in a lot of queries to the individual tables.
302
376
 
303
- == Wish List / Coming Soon
304
-
305
- * Some kind of memoize option.
306
- * Priming caches. Warm up the cache for all models based on the information in the instance and class cache configurations. Would need to know when you want to prime counts as well.
307
- * The ability to <tt>include AridCache</tt> on any class or module.
308
- * An option to bypass ID caching when you want to store records in the cache.
309
- * Option to turn off the <tt>respond_to?</tt> requirement for caches without blocks in cases where methods are implemented using method_missing and respond_to? hasn't been properly updated.
310
-
311
377
  == Contributors
312
378
 
313
379
  Contributions are welcome! Please,
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.4
1
+ 1.1.0
data/arid_cache.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{arid_cache}
8
- s.version = "1.0.4"
8
+ s.version = "1.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Karl Varga"]
12
- s.date = %q{2010-10-29}
12
+ s.date = %q{2010-12-17}
13
13
  s.description = %q{AridCache makes caching easy and effective. AridCache supports caching on all your model named scopes, class methods and instance methods right out of the box. AridCache prevents caching logic from cluttering your models and clarifies your logic by making explicit calls to cached result sets.
14
14
  AridCache is designed for handling large, expensive ActiveRecord collections but is equally useful for caching anything else as well.
15
15
  }
@@ -21,7 +21,6 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
21
21
  s.files = [
22
22
  ".gitignore",
23
23
  "Gemfile",
24
- "Gemfile.lock",
25
24
  "LICENSE",
26
25
  "README.rdoc",
27
26
  "Rakefile",
@@ -35,13 +34,13 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
35
34
  "lib/arid_cache/store.rb",
36
35
  "rails/init.rb",
37
36
  "spec/arid_cache/arid_cache_spec.rb",
37
+ "spec/arid_cache/cache_proxy_result_spec.rb",
38
38
  "spec/arid_cache/cache_proxy_spec.rb",
39
39
  "spec/spec.opts",
40
40
  "spec/spec_helper.rb",
41
41
  "spec/support/ar_query.rb",
42
42
  "spec/support/custom_methods.rb",
43
43
  "spec/support/matchers.rb",
44
- "tasks/arid_cache_tasks.rake",
45
44
  "test/arid_cache_test.rb",
46
45
  "test/console",
47
46
  "test/lib/add_query_counting_to_active_record.rb",
@@ -62,6 +61,7 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
62
61
  s.summary = %q{Automates efficient caching of your ActiveRecord collections, gives you counts for free and supports pagination.}
63
62
  s.test_files = [
64
63
  "spec/arid_cache/arid_cache_spec.rb",
64
+ "spec/arid_cache/cache_proxy_result_spec.rb",
65
65
  "spec/arid_cache/cache_proxy_spec.rb",
66
66
  "spec/spec_helper.rb",
67
67
  "spec/support/ar_query.rb",
@@ -43,38 +43,25 @@ module AridCache
43
43
  Rails.cache.delete_matched(%r[arid-cache-#{key}.*])
44
44
  end
45
45
 
46
- #
47
- # Fetching results
48
- #
49
-
50
- def self.fetch_count(object, key, opts={}, &block)
51
- CacheProxy.new(object, key, opts, &block).fetch_count
52
- end
53
-
54
- def self.fetch(object, key, opts={}, &block)
55
- CacheProxy.new(object, key, opts, &block).fetch
56
- end
57
-
58
- def initialize(object, key, opts, &block)
46
+ def initialize(object, key, opts={}, &block)
59
47
  self.object = object
60
48
  self.key = key
61
- self.opts = opts.symbolize_keys || {}
49
+ self.opts = opts.symbolize_keys
62
50
  self.blueprint = AridCache.store.find(object, key)
63
51
  self.block = block
64
52
  self.records = nil
65
53
 
66
54
  # The options from the blueprint merged with the options for this call
67
55
  self.combined_options = self.blueprint.nil? ? self.opts : self.blueprint.opts.merge(self.opts)
68
-
69
56
  self.cache_key = object.arid_cache_key(key, opts_for_cache_key)
70
- self.cached = Rails.cache.read(cache_key, opts_for_cache)
71
- self.klass = if self.cached && self.cached.is_a?(AridCache::CacheProxy::Result) # infer class of the results to return
72
- self.cached.klass
73
- else
74
- object_base_class
75
- end
76
57
  end
77
58
 
59
+ #
60
+ # Fetching results
61
+ #
62
+
63
+ # Return a count of ids in the cache, or return whatever is in the cache if it is
64
+ # not a CacheProxy::Result
78
65
  def fetch_count
79
66
  if refresh_cache?
80
67
  execute_count
@@ -89,20 +76,52 @@ module AridCache
89
76
  end
90
77
  end
91
78
 
79
+ # Return a list of records using the options provided. If the item in the cache
80
+ # is not a CacheProxy::Result it is returned as-is. If there is nothing in the cache
81
+ # the block defining the cache is exectued. If the :raw option is true, returns the
82
+ # CacheProxy::Result unmodified, ignoring other options, except where those options
83
+ # are used to initialize the cache.
92
84
  def fetch
93
- if refresh_cache?
94
- execute_find
85
+ @raw_result = opts_for_cache_proxy[:raw] == true
86
+
87
+ result = if refresh_cache?
88
+ execute_find(@raw_result)
95
89
  elsif cached.is_a?(AridCache::CacheProxy::Result)
96
- if cached.has_ids?
97
- fetch_from_cache
98
- else
99
- execute_find
90
+ if cached.has_ids? && @raw_result
91
+ self.cached # return it unmodified
92
+ elsif cached.has_ids?
93
+ fetch_from_cache # return a list of active records after applying options
94
+ else # true if we have only calculated the count thus far
95
+ execute_find(@raw_result)
100
96
  end
101
97
  else
102
- cached # some base type, return it
98
+ cached # some base type, return it unmodified
103
99
  end
104
100
  end
105
101
 
102
+ # Clear the cached result for this cache only
103
+ def clear_cached
104
+ Rails.cache.delete(self.cache_key, opts_for_cache)
105
+ end
106
+
107
+ # Return the cached result for this object's key
108
+ def cached
109
+ @cached ||= Rails.cache.read(self.cache_key, opts_for_cache)
110
+ end
111
+
112
+ # Return the class of the cached results i.e. if the cached result is a
113
+ # list of Album records, then klass returns Album. If there is nothing
114
+ # in the cache, then the class is inferred to be the class of the object
115
+ # that the cached method is being called on.
116
+ def klass
117
+ @klass ||= if self.cached && self.cached.is_a?(AridCache::CacheProxy::Result)
118
+ self.cached.klass
119
+ else
120
+ object_base_class
121
+ end
122
+ end
123
+
124
+
106
125
  private
107
126
 
108
127
  # Return a list of records from the database using the ids from
@@ -175,7 +194,14 @@ module AridCache
175
194
  self.records = block.nil? ? object.instance_eval(key) : object.instance_eval(&block)
176
195
  end
177
196
 
178
- def execute_find
197
+ # Seed the cache by executing the stored block (or by calling a method on the object).
198
+ # Then apply any options like pagination or ordering before returning the result, which
199
+ # is either some base type, or usually, a list of active records.
200
+ #
201
+ # Options:
202
+ # raw - if true, return the CacheProxy::Result after seeding the cache, ignoring
203
+ # other options. Default is false.
204
+ def execute_find(raw = false)
179
205
  get_records
180
206
  cached = AridCache::CacheProxy::Result.new
181
207
 
@@ -193,8 +219,11 @@ module AridCache
193
219
  end
194
220
  end
195
221
  Rails.cache.write(cache_key, cached, opts_for_cache)
196
-
197
222
  self.cached = cached
223
+
224
+ # Return the raw result?
225
+ return self.cached if raw
226
+
198
227
  # An order has been specified. We have to go to the database
199
228
  # to order because we can't be sure that the current order is the same as the cache.
200
229
  if cached.is_a?(AridCache::CacheProxy::Result) && combined_options.include?(:order)
@@ -300,6 +329,13 @@ module AridCache
300
329
  combined_options.reject { |k,v| !OPTIONS_FOR_CACHE_KEY.include?(k) }
301
330
  end
302
331
 
332
+ OPTIONS_FOR_CACHE_PROXY = [:raw, :clear]
333
+
334
+ # Returns options that affect the cache proxy result
335
+ def opts_for_cache_proxy
336
+ combined_options.reject { |k,v| !OPTIONS_FOR_CACHE_PROXY.include?(k) }
337
+ end
338
+
303
339
  def object_base_class #:nodoc:
304
340
  object.is_a?(Class) ? object : object.class
305
341
  end
@@ -63,12 +63,19 @@ module AridCache
63
63
 
64
64
  private
65
65
 
66
+ # Dynamically define a method on the object's class to return cached results
66
67
  def method_for_cached(object, key, fetch_method=:fetch, method_name=nil)
67
68
  method_name = ("cached_" + (method_name || key)).gsub(/[^\w\!\?]/, '_')
68
69
  method_body = <<-END
69
70
  def #{method_name}(*args, &block)
70
71
  opts = args.empty? ? {} : args.first
71
- AridCache.cache.send(#{fetch_method.inspect}, self, #{key.inspect}, opts, &block)
72
+
73
+ proxy = AridCache::CacheProxy.new(self, #{key.inspect}, opts, &block)
74
+ if opts[:clear] == true
75
+ proxy.clear_cached
76
+ else
77
+ proxy.#{fetch_method.to_s}
78
+ end
72
79
  end
73
80
  END
74
81
  # Get the correct object
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe AridCache::CacheProxy::Result do
4
+ before :each do
5
+ class X; end
6
+ @result = AridCache::CacheProxy::Result.new
7
+ end
8
+
9
+ it "should set the klass from a class" do
10
+ @result.klass = X
11
+ @result.klass.should be(X)
12
+ end
13
+
14
+ it "should set the klass from an object" do
15
+ @result.klass = X.new
16
+ @result.klass.should be(X)
17
+ end
18
+
19
+ it "should store the klass as a string" do
20
+ @result.klass = X
21
+ @result[:klass].should == X.name
22
+ end
23
+
24
+ it "should not have ids if it's nil" do
25
+ @result.ids = nil
26
+ @result.has_ids?.should be_false
27
+ end
28
+
29
+ it "should have ids" do
30
+ @result.ids = [1,2,3]
31
+ @result.has_ids?.should be_true
32
+ end
33
+
34
+ it "should have ids even if the array is empty" do
35
+ @result.ids = []
36
+ @result.has_ids?.should be_true
37
+ end
38
+
39
+ it "should not have a count if it's nil" do
40
+ @result.count = nil
41
+ @result.has_count?.should be_false
42
+ end
43
+
44
+ it "should have a count" do
45
+ @result.count = 3
46
+ @result.has_count?.should be_true
47
+ end
48
+
49
+ it "should have a count even if it is zero" do
50
+ @result.count = 0
51
+ @result.has_count?.should be_true
52
+ end
53
+ end
@@ -1,19 +1,95 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe AridCache::CacheProxy do
4
- describe 'id in order clause' do
4
+ describe 'preserve_order' do
5
5
  before :each do
6
6
  @proxy = AridCache::CacheProxy.new(Company, 'dummy-key', {})
7
7
  end
8
-
9
- it "should be prefixed by the table name" do
8
+
9
+ it "id column should be prefixed by the table name" do
10
10
  ::ActiveRecord::Base.stubs(:is_mysql_adapter?).returns(true)
11
11
  @proxy.send(:preserve_order, [1,2,3]).should =~ %r[#{Company.table_name}]
12
12
  end
13
-
14
- it "should be prefixed by the table name" do
13
+
14
+ it "id column should be prefixed by the table name" do
15
15
  ::ActiveRecord::Base.stubs(:is_mysql_adapter?).returns(false)
16
16
  @proxy.send(:preserve_order, [1,2,3]).should =~ %r[#{Company.table_name}]
17
17
  end
18
18
  end
19
+
20
+ describe "with raw => true" do
21
+ before :each do
22
+ @user = User.make
23
+ @user.companies << Company.make
24
+ @user.companies << Company.make
25
+ @user.clear_instance_caches
26
+ end
27
+
28
+ it "should return a CacheProxy::Result" do
29
+ @user.cached_companies(:raw => true).should be_a(AridCache::CacheProxy::Result)
30
+ end
31
+
32
+ it "result should have the same ids as the normal result" do
33
+ @user.cached_companies(:raw => true).ids.should == @user.cached_companies.collect(&:id)
34
+ end
35
+
36
+ it "should ignore :raw => false" do
37
+ @user.cached_companies(:raw => false).should == @user.cached_companies
38
+ end
39
+
40
+ it "should only query once to seed the cache, ignoring all other options" do
41
+ lambda { @user.cached_companies(:raw => true, :limit => 0, :order => 'nonexistent_column desc') }.should query(1)
42
+ end
43
+
44
+ it "should ignore all other options if the cache has already been seeded" do
45
+ lambda {
46
+ companies = @user.cached_companies
47
+ @user.cached_companies(:raw => true, :limit => 0, :order => 'nonexistent_column').ids.should == companies.collect(&:id)
48
+ }.should query(1)
49
+ end
50
+
51
+ it "should not use the raw option when reading from the cache" do
52
+ Rails.cache.expects(:read).with(@user.arid_cache_key(:companies), {})
53
+ @user.cached_companies(:raw => true)
54
+ end
55
+
56
+ it "should work for calls to a method that ends with _count" do
57
+ @user.cached_bogus_count do
58
+ 10
59
+ end
60
+ @user.cached_bogus_count(:raw => true).should == 10
61
+ end
62
+
63
+ it "should work for calls to a method that ends with _count" do
64
+ @user.cached_companies_count(:raw => true).should == @user.cached_companies_count
65
+ end
66
+ end
67
+
68
+ describe "with clear => true" do
69
+ before :each do
70
+ @user = User.make
71
+ @user.companies << Company.make
72
+ @user.companies << Company.make
73
+ @user.clear_instance_caches rescue Rails.cache.clear
74
+ end
75
+
76
+ it "should not fail if there is no cached value" do
77
+ lambda { @user.cached_companies(:clear => true) }.should_not raise_exception
78
+ end
79
+
80
+ it "should clear the cached entry" do
81
+ key = @user.arid_cache_key(:companies)
82
+ @user.cached_companies
83
+ Rails.cache.read(key).should_not be_nil
84
+ @user.cached_companies(:clear => true)
85
+ Rails.cache.read(key).should be_nil
86
+ end
87
+
88
+ it "should not read from the cache or database" do
89
+ Rails.cache.expects(:read).never
90
+ lambda {
91
+ @user.cached_companies(:clear => true)
92
+ }.should query(0)
93
+ end
94
+ end
19
95
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  root_path = File.expand_path(File.join(File.dirname(__FILE__), '..'))
2
- $LOAD_PATH.unshift(File.join(root_path, '/test/lib')) # make requiring from test/lib easy
2
+ $LOAD_PATH.unshift(File.join(root_path, '/test/lib')) # add test/lib to the load path
3
3
 
4
4
  require 'bundler/setup'
5
5
  Bundler.require
@@ -331,11 +331,11 @@ class AridCacheTest < ActiveSupport::TestCase
331
331
 
332
332
  test "should support calling store methods directly with a block" do
333
333
  assert_nothing_raised do
334
- AridCache.cache.fetch(@user, 'my-fancy-key') do
334
+ AridCache.cache.new(@user, 'my-fancy-key') do
335
335
  companies
336
- end
336
+ end.fetch
337
337
  end
338
- assert_equal @user.companies.all(:order => 'name DESC'), AridCache.cache.fetch(@user, 'my-fancy-key', :order => 'name DESC') { companies }
338
+ assert_equal @user.companies.all(:order => 'name DESC'), AridCache.cache.new(@user, 'my-fancy-key', :order => 'name DESC') { companies }.fetch
339
339
  end
340
340
 
341
341
  test "empty user relation should be empty" do
@@ -371,14 +371,14 @@ class AridCacheTest < ActiveSupport::TestCase
371
371
  ::ActiveRecord::Base.connection.instance_eval { def adapter_name; "Mysql2"; end }
372
372
  assert_equal "Mysql2", ::ActiveRecord::Base.connection.adapter_name
373
373
  assert_equal true, ::ActiveRecord::Base.is_mysql_adapter?
374
-
374
+
375
375
  # Restore it otherwise the other tests break
376
376
  ::ActiveRecord::Base.connection.instance_eval { def adapter_name; "SQLite3"; end }
377
377
  assert_equal "SQLite3", ::ActiveRecord::Base.connection.adapter_name
378
378
  ::ActiveRecord::Base.is_mysql_adapter = nil
379
379
  assert_equal false, ::ActiveRecord::Base.is_mysql_adapter?
380
380
  end
381
-
381
+
382
382
  #
383
383
  # Tests requiring manual verification by looking at the SQL logs.
384
384
  # TODO move these to a separate class.
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arid_cache
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
+ - 1
8
9
  - 0
9
- - 4
10
- version: 1.0.4
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Karl Varga
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-29 00:00:00 -07:00
18
+ date: 2010-12-17 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -89,7 +89,6 @@ extra_rdoc_files:
89
89
  files:
90
90
  - .gitignore
91
91
  - Gemfile
92
- - Gemfile.lock
93
92
  - LICENSE
94
93
  - README.rdoc
95
94
  - Rakefile
@@ -103,13 +102,13 @@ files:
103
102
  - lib/arid_cache/store.rb
104
103
  - rails/init.rb
105
104
  - spec/arid_cache/arid_cache_spec.rb
105
+ - spec/arid_cache/cache_proxy_result_spec.rb
106
106
  - spec/arid_cache/cache_proxy_spec.rb
107
107
  - spec/spec.opts
108
108
  - spec/spec_helper.rb
109
109
  - spec/support/ar_query.rb
110
110
  - spec/support/custom_methods.rb
111
111
  - spec/support/matchers.rb
112
- - tasks/arid_cache_tasks.rake
113
112
  - test/arid_cache_test.rb
114
113
  - test/console
115
114
  - test/lib/add_query_counting_to_active_record.rb
@@ -158,6 +157,7 @@ specification_version: 3
158
157
  summary: Automates efficient caching of your ActiveRecord collections, gives you counts for free and supports pagination.
159
158
  test_files:
160
159
  - spec/arid_cache/arid_cache_spec.rb
160
+ - spec/arid_cache/cache_proxy_result_spec.rb
161
161
  - spec/arid_cache/cache_proxy_spec.rb
162
162
  - spec/spec_helper.rb
163
163
  - spec/support/ar_query.rb
data/Gemfile.lock DELETED
@@ -1,59 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- arid_cache (1.0.3)
5
- will_paginate
6
-
7
- GEM
8
- remote: http://rubygems.org/
9
- specs:
10
- activerecord (2.3.8)
11
- activesupport (= 2.3.8)
12
- activesupport (2.3.8)
13
- columnize (0.3.1)
14
- faker (0.3.1)
15
- gemcutter (0.6.1)
16
- git (1.2.5)
17
- hoe (2.6.2)
18
- rake (>= 0.8.7)
19
- rubyforge (>= 2.0.4)
20
- jeweler (1.4.0)
21
- gemcutter (>= 0.1.0)
22
- git (>= 1.2.5)
23
- rubyforge (>= 2.0.0)
24
- json_pure (1.4.6)
25
- linecache (0.43)
26
- machinist (1.0.6)
27
- mocha (0.9.8)
28
- rake
29
- rake (0.8.7)
30
- rspec (1.3.0)
31
- ruby-debug (0.10.3)
32
- columnize (>= 0.1)
33
- ruby-debug-base (~> 0.10.3.0)
34
- ruby-debug-base (0.10.3)
35
- linecache (>= 0.3)
36
- rubyforge (2.0.4)
37
- json_pure (>= 1.1.7)
38
- sqlite3-ruby (1.3.1)
39
- test-unit (1.2.3)
40
- hoe (>= 1.5.1)
41
- will_paginate (2.3.15)
42
-
43
- PLATFORMS
44
- ruby
45
-
46
- DEPENDENCIES
47
- activerecord (= 2.3.8)
48
- activesupport (= 2.3.8)
49
- arid_cache!
50
- faker (= 0.3.1)
51
- jeweler (= 1.4.0)
52
- machinist (= 1.0.6)
53
- mocha (= 0.9.8)
54
- rspec (= 1.3.0)
55
- ruby-debug (= 0.10.3)
56
- ruby-debug-base (= 0.10.3)
57
- sqlite3-ruby (= 1.3.1)
58
- test-unit (= 1.2.3)
59
- will_paginate (= 2.3.15)
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :arid_cache do
3
- # # Task goes here
4
- # end