arid_cache 1.3.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,8 +1,8 @@
1
1
  # A sample Gemfile
2
2
  source "http://rubygems.org"
3
3
 
4
- gem 'activerecord', '=2.3.8'
5
- gem 'activesupport', '=2.3.8'
4
+ gem 'activerecord', '=2.3.11'
5
+ gem 'activesupport', '=2.3.11'
6
6
  gem 'sqlite3-ruby', '=1.3.1'
7
7
 
8
8
  gem 'will_paginate', '=2.3.15'
data/README.rdoc CHANGED
@@ -8,10 +8,11 @@ AridCache simplifies caching by supporting auto-expiring cache keys - as well as
8
8
 
9
9
  == Changes
10
10
 
11
- v1.3.1: Support proxies which allow you to control how your objects get serialized and unserialized
12
- v1.3.0: Support limits, ordering and pagination on cached Enumerables
13
- v1.2.0: Fix Rails 3 ActiveRecord hooks & remove some Rails dependencies
14
- v1.0.5: Support <tt>:raw</tt> and <tt>:clear</tt> options.
11
+ * v1.3.2: <tt>AridCache.raw_with_options</tt> configuration for better <tt>:raw</tt> handling on cached ActiveRecord collections
12
+ * v1.3.1: Proxy support which allow you to control how your objects get serialized and unserialized
13
+ * v1.3.0: Support limits, ordering and pagination on cached Enumerables
14
+ * v1.2.0: Fix Rails 3 ActiveRecord hooks & remove some Rails dependencies...almost Rails agnostic!
15
+ * v1.0.5: Support <tt>:raw</tt> and <tt>:clear</tt> options.
15
16
 
16
17
  == Install
17
18
 
@@ -38,17 +39,17 @@ Then
38
39
  == Features
39
40
 
40
41
  * Include the AridCache module in any Class
41
- * Rails 2 & 3 compatible with automatic ActiveRecord::Base integration
42
- * Supports auto-expiring cache keys
43
- * Supports limits, ordering & pagination of cached Enumerables and ActiveRecord collections
42
+ * <b>Rails 2 & 3 compatible</b> with automatic ActiveRecord::Base integration
43
+ * Supports <b>auto-expiring cache keys</b>
44
+ * Supports *limits*, *ordering* & *pagination* of cached Enumerables and ActiveRecord collections
44
45
  * Define caches and their options on your class using +instance_caches+ and +class_caches+
45
- * Counts for free - if you have already cached the result, you get the count for free
46
- * Smart counts - if you only ask for the count it will only calculate the count; useful when the result is an Association Reflection or a named scope.
47
- * Supports eager-loading and other options to <tt>ActiveRecord::Base#find</tt> like <tt>:conditions</tt>, <tt>:include</tt>, <tt>:joins</tt>, <tt>:select</tt>, <tt>:readonly</tt>, <tt>:group</tt>, <tt>:having</tt>, <tt>:from</tt>
48
- * Provides methods to clear caches individually, at the instance-level, class-level and globally
49
- * Preserves the order of your cached ActiveRecord collections
50
- * Optimized to make as few cache and database accesses as absolutely neccessary
51
- * Define your own cache proxy to serialize your objects as they go to and from the cache
46
+ * <b>Counts for free</b> - if you have already cached the result, you get the count for free
47
+ * <b>Smart counts</b> - if you only ask for the count it will only calculate the count; useful when the result is an Association Reflection or a named scope.
48
+ * Supports <b>eager-loading</b> and other options to <tt>ActiveRecord::Base#find</tt> like <tt>:conditions</tt>, <tt>:include</tt>, <tt>:joins</tt>, <tt>:select</tt>, <tt>:readonly</tt>, <tt>:group</tt>, <tt>:having</tt>, <tt>:from</tt>
49
+ * Provides methods to <b>clear caches</b> individually, at the instance-level, class-level and globally
50
+ * <b>Preserves ordering</b> of your cached ActiveRecord collections
51
+ * <b>Optimized</b> to make as few cache and database accesses as absolutely neccessary
52
+ * Define your own <b>cache proxy</b> to serialize your objects as they go to and from the cache
52
53
 
53
54
  == Introduction
54
55
 
@@ -241,10 +242,6 @@ Or via the cache configuration,
241
242
  featured_albums
242
243
  end
243
244
 
244
- 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
245
-
246
- AridCache::CacheProxy::OPTIONS_FOR_CACHE.push(:raw, :unless_exist)
247
-
248
245
  == Extras
249
246
 
250
247
  === Cached Counts
@@ -333,15 +330,23 @@ For example, we could call:
333
330
 
334
331
  To return page two of the active users, with the <tt>preferences</tt> association eager-loaded for all the users.
335
332
 
336
- === Accessing the cached IDs directly
333
+ === The <tt>:raw</tt> option & Accessing the cached IDs directly
334
+
335
+ When you cache a collection of ActiveRecords, the records IDs are stored in the cache in a <tt>AridCache::CacheProxy::CachedResult</tt> which is a <tt>Struct</tt> with methods to return the <tt>ids</tt>, <tt>count</tt> and <tt>klass</tt> of the cached records.
336
+
337
+ Sometimes you may want to access what is in the cache without instantiating any records. <tt>:raw => true</tt> is what you use for that.
338
+
339
+ The current (and deprecated) behaviour of <tt>:raw => true</tt> is to return the +CachedResult+ object and ignore all other options - if the cached result is an ActiveRecord collection. Passing <tt>:raw => true</tt> when you have cached an Enumerable or some other base type does not share this behaviour. For these types <tt>:raw => true</tt> returns whatever is in the cache, after applying all options. This means that we can apply limits, ordering and pagination to our cached results and return the same type of data as is stored in the cache.
337
340
 
338
- 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.
341
+ v1.3.2 introduces a new configuration option which makes the behaviour on cached ActiveRecord collections the same as for other cached types. Set
339
342
 
340
- 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.
343
+ AridCache.raw_with_options = true
341
344
 
342
- 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.
345
+ to enable the new behaviour. With this option set, rather than return a <tt>CachedResult</tt> <tt>:raw => true<tt> will return the list of IDs itself, <b>after applying all options</b>. So you can limit, order (with a Proc) and paginate the IDs before they are returned. Keep in mind the <tt>:order</tt> Proc is applied <b>to the cached/raw data</tt>.
343
346
 
344
- Usage example:
347
+ 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 can do a cache read manually.
348
+
349
+ The current (deprecated) behaviour:
345
350
 
346
351
  user = User.first
347
352
  user.cached_favorite_tracks => returns [#<Track:1>, #<Track:2>]
@@ -353,7 +358,7 @@ Usage example:
353
358
  }
354
359
  user.cached_favorite_tracks(:raw => true).ids => returns [1, 2]
355
360
 
356
- 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>.
361
+ The cache will be primed if it is empty, so you can be sure that it will always return a <tt>AridCache::CacheProxy::CachedResult</tt>.
357
362
 
358
363
  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:
359
364
 
@@ -375,6 +380,18 @@ In some circumstances - like when you are querying on a named scope - if you hav
375
380
  :ids => [2, 235, 236, 237] # the ids array is seeded before returning
376
381
  }
377
382
 
383
+ With <tt>AridCache.raw_with_options = true</tt>:
384
+
385
+ user = User.first
386
+ user.cached_favorite_tracks
387
+ >> [#<Track:1>, #<Track:2>]
388
+ user.cached_favorite_tracks(:raw => true)
389
+ >> [1, 2]
390
+ user.cached_favorite_tracks(:order => Proc.new { |a, b| b <=> a }, :raw => true)
391
+ >> [2, 1]
392
+ user.cached_favorite_tracks(:offset => 1, :raw => true)
393
+ >> [2]
394
+
378
395
  == Proxies
379
396
 
380
397
  Proxies allow you to do anything you want to your objects as they go into - and come out of - the cache. They are most useful for serializing your objects, for example if you want to store JSON, or hashes.
@@ -444,22 +461,22 @@ In practice you probably want to define your proxy method on <tt>ActiveRecord::B
444
461
  == Compatibility
445
462
 
446
463
  Tested on Ruby 1.8.6, 1.8.7, REE 1.8.7 and 1.9.1.
447
- Tested in Rails 2.3.11, Rails 3.0.0, Rails 3.0.5
464
+ Tested in Rails 2.3.8, 2.3.11, Rails 3.0.0, Rails 3.0.5
448
465
 
449
466
  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:
450
467
 
451
468
  Array.class_eval { alias count size }
452
469
 
453
- == Resources & Metrics
470
+ == Resources
454
471
 
455
-
456
- * {RDoc}[http://rdoc.info/projects/kjvarga/arid_cache]
457
- * {GetCaliper Metrics}[http://getcaliper.com/caliper/project?repo=git%3A%2F%2Fgithub.com%2Fkjvarga%2Farid_cache.git]
472
+ * {RDoc}[http://rdoc.info/github/kjvarga/arid_cache/master/frames]
458
473
 
459
474
  == Known Issues
460
475
 
461
476
  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.
462
- 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. If you need polymorphism consider using a proxy.
477
+ 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. If you need polymorphism consider using a proxy and do the record-loading yourself.
478
+ 3. Rails ActiveRecord (or SQL) has a quirk where if you pass an +offset+ without a +limit+ the +offset+ is ignored. AridCache fixes this unexpected behaviour by including a +limit+ on its queries.
479
+ 4. When you use the <tt>:order</tt> option with a cached ActiveRecord collection we have to go to the database to order. So we also do pagination and limiting there because we want to retrieve as few records as possible. So we use WillPaginate's ActiveRecord::Base#paginate method. Unfortunately if you pass <tt>:limit</tt> or <tt>:offset</tt> in addition to your pagination options, the limit and offset are ignored. This is not the behaviour when interacting with other cached types. In that case we apply the order, then the limits and finally the pagination, which is what you would expect.
463
480
 
464
481
  == Contributors
465
482
 
@@ -471,7 +488,6 @@ Contributions are welcome! Please,
471
488
  * Commit (don't mess with the Rakefile, version, or history).
472
489
  * Send me a pull request.
473
490
 
474
-
475
491
  ==== Thank-you to these contributors to AridCache:
476
492
 
477
493
  * {Sutto}[http://github.com/Sutto]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.1
1
+ 1.3.2
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.3.1"
8
+ s.version = "1.3.2"
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{2011-04-06}
12
+ s.date = %q{2011-04-07}
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
  }
@@ -6,14 +6,14 @@ module AridCache
6
6
  def initialize(opts={})
7
7
  self.merge!(opts)
8
8
  end
9
-
9
+
10
10
  # Filter options for paginate. Get the :per_page value from the receiver if it's not set.
11
11
  # Set total_entries to +records.size+ if +records+ is supplied
12
12
  def opts_for_paginate(records=nil)
13
13
  paginate_opts = reject { |k,v| !OPTIONS_FOR_PAGINATE.include?(k) }
14
14
  paginate_opts[:finder] = :find_all_by_id unless paginate_opts.include?(:finder)
15
15
  if self[:result_klass].respond_to?(:per_page) && !paginate_opts.include?(:per_page)
16
- paginate_opts[:per_page] = self[:result_klass].per_page
16
+ paginate_opts[:per_page] = self[:result_klass].per_page
17
17
  end
18
18
  paginate_opts[:total_entries] = records.size unless records.nil?
19
19
  paginate_opts
@@ -43,35 +43,39 @@ module AridCache
43
43
  # Returns options that affect the cache proxy result
44
44
  def opts_for_cache_proxy
45
45
  reject { |k,v| !OPTIONS_FOR_CACHE_PROXY.include?(k) }
46
- end
47
-
46
+ end
47
+
48
48
  def force?
49
49
  !!self[:force]
50
- end
51
-
50
+ end
51
+
52
52
  def paginate?
53
53
  include?(:page)
54
54
  end
55
-
55
+
56
56
  def raw?
57
57
  !!self[:raw]
58
58
  end
59
-
59
+
60
60
  def count_only?
61
61
  !!self[:count_only]
62
62
  end
63
-
63
+
64
64
  def order_by_proc?
65
65
  include?(:order) && self[:order].is_a?(Proc)
66
66
  end
67
-
67
+
68
68
  def order_by_key?
69
69
  include?(:order) && (self[:order].is_a?(Symbol) || self[:order].is_a?(String))
70
- end
71
-
70
+ end
71
+
72
72
  def proxy?
73
73
  include?(:proxy)
74
74
  end
75
+
76
+ def deprecated_raw?
77
+ !!(raw? && !AridCache.raw_with_options)
78
+ end
75
79
  end
76
80
  end
77
- end
81
+ end
@@ -36,7 +36,7 @@ module AridCache
36
36
  # Order in the database if an order clause has been specified and we
37
37
  # have a list of ActiveRecords or a CachedResult.
38
38
  def order_in_database?
39
- is_cached_result? || (@options.order_by_key? && is_activerecord?)
39
+ (is_cached_result? && !@options.raw?) || (@options.order_by_key? && is_activerecord?)
40
40
  end
41
41
 
42
42
  # Return true if the result is an enumerable and the first item is
@@ -89,10 +89,9 @@ module AridCache
89
89
  def to_result
90
90
  if @options.count_only?
91
91
  get_count
92
- elsif is_cached_result? && @options.raw?
93
- @result
92
+
94
93
  elsif @options.proxy?
95
- results =
94
+ results =
96
95
  if @cached.nil? || !@options.raw?
97
96
  @result
98
97
  else
@@ -109,6 +108,21 @@ module AridCache
109
108
  else
110
109
  filtered
111
110
  end
111
+
112
+ elsif @options.raw?
113
+
114
+ result =
115
+ if @cached.is_a?(AridCache::CacheProxy::CachedResult)
116
+ @cached
117
+ else
118
+ @result
119
+ end
120
+ if @options.deprecated_raw?
121
+ result
122
+ else
123
+ filter_results(result.is_a?(AridCache::CacheProxy::CachedResult) ? result.ids : result)
124
+ end
125
+
112
126
  elsif is_cached_result?
113
127
  fetch_activerecords(filter_results(@result.ids))
114
128
  elsif order_in_database?
data/lib/arid_cache.rb CHANGED
@@ -12,33 +12,43 @@ module AridCache
12
12
  extend AridCache::Helpers
13
13
  Error = Class.new(StandardError) #:nodoc:
14
14
 
15
+ # Set to true to make the :raw option return ids after applying options to them.
16
+ # The deprecated behaviour is to return a CachedResult and ignore all options.
17
+ def self.raw_with_options=(value)
18
+ @raw_with_options = value
19
+ end
20
+
21
+ def self.raw_with_options
22
+ !!@raw_with_options
23
+ end
24
+
15
25
  def self.cache
16
26
  AridCache::CacheProxy
17
27
  end
18
28
 
19
29
  def self.clear_caches
20
30
  AridCache::CacheProxy.clear_caches
21
- end
22
-
31
+ end
32
+
23
33
  def self.clear_class_caches(object)
24
34
  AridCache::CacheProxy.clear_class_caches(object)
25
- end
26
-
35
+ end
36
+
27
37
  def self.clear_instance_caches(object)
28
38
  AridCache::CacheProxy.clear_instance_caches(object)
29
- end
30
-
39
+ end
40
+
31
41
  def self.store
32
42
  AridCache::Store.instance
33
43
  end
34
-
44
+
35
45
  # The old method of including this module, if you don't want to
36
46
  # extend active record. Just add 'include AridCache' to your
37
47
  # model class.
38
48
  def self.included(base)
39
49
  base.send(:include, AridCache::ActiveRecord)
40
50
  end
41
-
51
+
42
52
  # Initializes AridCache for Rails.
43
53
  #
44
54
  # This method is called by `init.rb`,
@@ -20,7 +20,7 @@ describe AridCache do
20
20
  results[1].name.should == @company2.name
21
21
  end
22
22
  end
23
-
23
+
24
24
  it "order should match the order option" do
25
25
  3.times do |t|
26
26
  results = Company.cached_ordered_by_name(:order => 'name DESC')
@@ -29,11 +29,17 @@ describe AridCache do
29
29
  results[1].name.should == @company1.name
30
30
  end
31
31
  end
32
-
32
+
33
33
  it "with order option should go to the database to order" do
34
34
  lambda {
35
35
  Company.cached_ordered_by_name(:order => 'name DESC')
36
36
  }.should query(2)
37
37
  end
38
38
  end
39
+
40
+ it "should set the special raw flag" do
41
+ AridCache.raw_with_options.should be_false
42
+ AridCache.raw_with_options = true
43
+ AridCache.raw_with_options.should be_true
44
+ end
39
45
  end
@@ -76,11 +76,23 @@ describe AridCache::CacheProxy::Options do
76
76
  it "should use find_all_by_id as the finder" do
77
77
  new_options.opts_for_paginate[:finder].should == :find_all_by_id
78
78
  end
79
- end
80
-
79
+ end
80
+
81
81
  describe "proxies" do
82
82
  it "should use proxy" do
83
83
  new_options(:proxy => :serializing_proxy).proxy?.should be_true
84
84
  end
85
85
  end
86
+
87
+ describe "deprecated raw" do
88
+ it "should be deprecated" do
89
+ AridCache.expects(:raw_with_options).returns(false)
90
+ new_options(:raw => true).deprecated_raw?.should be_true
91
+ end
92
+
93
+ it "should not be deprecated" do
94
+ AridCache.expects(:raw_with_options).returns(true)
95
+ new_options(:raw => true).deprecated_raw?.should be_false
96
+ end
97
+ end
86
98
  end
@@ -355,4 +355,47 @@ describe AridCache::CacheProxy::ResultProcessor do
355
355
  end
356
356
  end
357
357
  end
358
+
359
+ describe "raw with options handling" do
360
+ before :each do
361
+ AridCache.raw_with_options = true
362
+ @user = User.make
363
+ @company1 = Company.make
364
+ @company2 = Company.make
365
+ @user.companies << @company1
366
+ @user.companies << @company2
367
+ class User
368
+ instance_caches do
369
+ raw_companies(:raw => true) { companies }
370
+ end
371
+ end
372
+ @user.cached_raw_companies(:clear => true)
373
+ end
374
+
375
+ it "should return ids" do
376
+ @user.cached_raw_companies.should == [@company1.id, @company2.id]
377
+ end
378
+
379
+ it "should apply order" do
380
+ @user.cached_raw_companies(:order => Proc.new { |a,b| b <=> a }).should == [@company2.id, @company1.id]
381
+ end
382
+
383
+ it "should not order in database" do
384
+ new_result(AridCache::CacheProxy::CachedResult.new).order_in_database?.should be_true
385
+ new_result(AridCache::CacheProxy::CachedResult.new, { :raw => true }).order_in_database?.should be_false
386
+ end
387
+
388
+ it "should apply offset and limit" do
389
+ @user.cached_raw_companies(:limit => 1).should == [@company1.id]
390
+ @user.cached_raw_companies(:offset => 1).should == [@company2.id]
391
+ end
392
+
393
+ it "should apply pagination" do
394
+ value = @user.cached_raw_companies(:page => 2, :per_page => 1)
395
+ value.should be_a(WillPaginate::Collection)
396
+ value.total_entries.should == 2
397
+ value.current_page.should == 2
398
+ value.should == [@company2.id]
399
+ end
400
+ end
358
401
  end
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: 25
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 3
9
- - 1
10
- version: 1.3.1
9
+ - 2
10
+ version: 1.3.2
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: 2011-04-06 00:00:00 -07:00
18
+ date: 2011-04-07 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency