arid_cache 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -20,3 +20,4 @@ pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
22
  test/**/*.log
23
+ test/tmp/*
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Karl Varga
1
+ Copyright (c) 2009 Karl Varga <kjvarga@gmail.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -1,120 +1,279 @@
1
- == ARID Cache
1
+ = AridCache
2
2
 
3
- ARID Cache makes caching easy and effective. ARID cache supports caching on all your model named scopes, class methods and instance methods right out of the box. ARID cache prevents caching logic from cluttering your models and clarifies your logic by making explicit calls to cached result sets.
3
+ AridCache makes caching easy and effective. AridCache supports caching on all of your ActiveRecord model named scopes, class and instance methods right out of the box. AridCache keeps caching logic out of your model methods and clarifies your view code by making calls to cached result sets explicit.
4
4
 
5
- ARID Cache is designed for handling large, expensive ActiveRecord collections but is equally useful for caching anything else as well.
5
+ AridCache supports caching large, expensive ActiveRecord collections by caching only the model IDs, provides efficient in-memory pagination of your cached collections, and gives you collection counts for free. Non-ActiveRecord collection data is cached unchanged allowing you to cache the results of any expensive operation simply by prepending your method call with <tt>cached_</tt>.
6
6
 
7
- === Counts for free
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
- ARID Cache gives you counts for free. When a large collection is stored in the cache
10
- ARID Cache stores the count as well so the next time you want request the count it
11
- just takes a single read from the cache. This is also supported for your non-ActiveRecord
12
- collections if the collection <tt>responds_to?(:count)</tt>.
9
+ == Install
13
10
 
14
- Given that we have a cache like <tt>album.cached_tracks</tt> we can get the count by calling <tt>album.cached_tracks_count</tt>.
11
+ Add this to your <tt>config/environment.rb</tt> file:
15
12
 
16
- === Auto-expiring cache keys
13
+ config.gem 'arid_cache'
17
14
 
18
- Caches on model instances automatically incorporate the ActiveRecord <tt>cache_key</tt> which includes the <tt>updated_at</tt> timestamp of that instance, making them auto-expire when the instance is updated.
15
+ Then
19
16
 
20
- Caches on your model classes (like on the results of named scopes) will not expire however.
17
+ rake gems:install
21
18
 
22
- ARID cache provides methods to help you expire your caches.
23
-
24
- AridCache.clear_all_caches => expires all ARID Cache caches
25
- Model.clear_all_caches => expires class and instance-level caches for this model
26
- Model.clear_instance_caches => expires instance-level caches for this model
27
- Model.clear_class_caches => expires class-level caches for this model
19
+ (Requires having GemCutter in your gem sources.)
20
+
21
+ == Introduction
22
+
23
+ The name AridCache comes from <b>A</b>ctive<b>R</b>ecord *ID* Cache. It's also very DRY...get it? :)
24
+
25
+ Out of the box AridCache supports caching on all your ActiveRecord class and instance methods and named scopes...basically if a class or class instance <tt>respond_to?</tt> something, you can cache it.
26
+
27
+ The way you interact with the cache via your model methods is to prepend the method call with <tt>cached_</tt>. The part of the method call after <tt>cached_</tt> serves as the basis for the cache key. For example,
28
+
29
+ User.cached_count # cache key is arid-cache-user-count
30
+ genre.cached_top_ten_tracks # cache key is arid-cache-genres/<id>-top_ten_tracks
28
31
 
29
- These methods are also available on model instances.
32
+ You can also define caches that use compositions of methods or named scopes, or other complex queries, without having to add a new method to your class. This way you can also create different caches that all use the same method. For example,
30
33
 
31
- ARID Cache keys are based on the method you call to create the cache. For example:
32
- Album.cached_featured_albums => cache key is arid-cache-album-featured_albums
33
- album.cached_top_tracks => cache key like arid-cache-albums/2-20090930200-top_tracks
34
+ # cache key is arid-cache-user-most_active_users
35
+ User.cached_most_active_users do
36
+ active.find(:order => 'activity DESC', :limit => 5)
37
+ end
38
+
39
+ === ActiveRecord Collections
34
40
 
35
- === Support for pagination and options to <tt>find</tt>
41
+ If the result of your <tt>cached_</tt> call is an array of ActiveRecords, AridCache only stores the IDs in the cache (because it's a bad idea to store records in the cache).
36
42
 
37
- ARID cache performs pagination and applies <tt>:limit</tt> and <tt>:offset</tt> to the IDs in memory and only selects the page/sub-set from the database, directly from the target table.
43
+ On subsequent calls we call <tt>find</tt> on the target class passing in the ActiveRecord IDs that were stored in the cache. AridCache will preserve the original ordering of your collection (you can change this using the <tt>:order</tt>).
38
44
 
39
- You can pass options like <tt>:include</tt> (or any other valid <tt>find</tt> options) to augment the results of your cached query.
45
+ The idea here is to cache collections that are expensive to query. Once the cache is loaded, retrieving the cached records from the database simply involves a <tt>SELECT * FROM table WHERE id IN (ids, ...)</tt>.
40
46
 
41
- === Efficiency
47
+ Consider how long it would take to get the top 10 favorited tracks of all time from a database with a million tracks and 100,000 users. Now compare that to selecting 10 tracks by ID from the track table. The performance gain is huge.
42
48
 
43
- ARID Cache intercepts calls to <tt>cached_</tt> methods using <tt>method_missing</tt> then defines those methods on your models as they are called, so they bypass method missing on subsequent calls.
49
+ === Base Types and Other Collections
44
50
 
45
- == Examples
51
+ Arrays of non-ActiveRecords are stored as-is so you can cache arrays of strings and other types without problems.
46
52
 
47
- ==== Given the following model:
53
+ Any other objects (including single ActiveRecord objects) are cached and returned as-is.
54
+
55
+ === Example
56
+
57
+ An example of caching using existing methods on your class:
48
58
 
49
59
  class User < ActiveRecord::Base
50
- include AridCache
51
60
  has_many :pets
52
61
  has_one :preferences
53
62
  named_scope :active, :conditions => [ 'updated_at <= ', 5.minutes.ago ]
54
63
  end
55
64
 
56
- ==== ARID Cache provides these methods:
57
-
58
- User.cached_active # caches the user IDs and the count
59
- User.cached_active_count # gets the count for free
65
+ User.cached_count # uses the built-in count method
66
+ User.cached_active # only stores the IDs of the active users in the cache
67
+ User.cached_active_count # returns the count of active users directly from the cache
60
68
 
61
- user.cached_pets # caches the pets IDs and the count
62
- user.cached_pets_count # gets the count for free
63
-
64
- When we call these methods again, instead of doing a full select - usually including
65
- complex joins or over very large tables which makes this expensive - it just
66
- selects where the IDs are the cached IDs.
69
+ user.cached_pets_count # only selects the count until the collection is requested
70
+ user.cached_pets # loads the collection and stores the pets IDs in the cache
67
71
 
68
- It also gives you paging using WillPaginate. The IDs from the cache are paginated and
69
- only that page is selected from the database - again directly from the table, without
70
- any complex joins.
72
+ == Defining Your Caches
71
73
 
72
- ==== Some examples of pagination:
74
+ === Dynamically
73
75
 
74
- User.cached_active.paginate(:page => 1, :per_page => 30)
75
- User.cached_active.paginate(:page => 1)
76
- User.cached_active.paginate(:page => 3)
76
+ To dynamically define caches just pass a block to your <tt>cached_</tt> calls. Caches can be defined on your classes or class instances. For example,
77
77
 
78
- You can also include options for find, such as <tt>:join</tt>, <tt>:include</tt> and <tt>order</tt>...basically any options that find supports.
78
+ User.cached_most_active_users do
79
+ active.find(:order => 'activity DESC', :limit => 5)
80
+ end
81
+
82
+ => [#<User id: 23>, #<User id: 30>, #<User id: 5>, #<User id: 2>, #<User id: 101>]
83
+
84
+ user.cached_favorite_pets do
85
+ pets.find(:all, :conditions => { 'favorite' => true })
86
+ end
87
+
88
+ => [#<Pet id: 11>, #<Pet id: 21>, #<Pet id: 3>]
79
89
 
80
- User.cached_active.paginate(:page => 1, :include => :preferences)
81
- User.cached_active.paginate(:page => 1, :order => 'created_at DESC') # don't change the order, just enforce it
90
+ === Configuring Caches on your Models
82
91
 
83
- You can limit the results returned using <tt>:limit</tt> and <tt>:offset</tt>:
92
+ We can clean up our views significantly by configuring caches on our model rather than defining them dynamically and passing options in each time. You configure caches by calling <tt>instance_caches(options={})</tt> or <tt>class_caches(options={})</tt> with a block and defining your caches inside the block (you don't need to prepend <tt>cached_</tt> when defining these caches because we are not returning results, just storing options).
84
93
 
85
- user.cached_pets(:limit => 2, :include => :toys)
86
- user.cached_pets(:limit => 2, :offset => 3, :include => :toys)
87
-
88
- ==== You can dynamically create caches
94
+ You can pass a hash of options to <tt>instance_caches</tt> and <tt>class_caches</tt> to have those options applied to all caches in the block. The following is a more complex example that also demonstrates nested cached calls.
89
95
 
90
- User.cached_most_active_users do
91
- self.active.find(:order => 'activity DESC', :limit => 10)
96
+ # app/models/genre.rb
97
+ class Genre
98
+ class_caches do
99
+ most_popular do
100
+ popular(:limit => 10, :order => 'popularity DESC')
101
+ end
102
+ end
103
+
104
+ instance_caches(:order => 'release_date DESC') do
105
+ highlight_tracks(:include => [:album, :artist]) do
106
+ cached_tracks(:limit => 10, :include => [:album, :artist])
107
+ end
108
+ highlight_artists do
109
+ cached_artists(:limit => 10)
110
+ end
111
+ highlight_albums(:include => :artist) do
112
+ cached_albums(:limit => 3, :include => :artist)
113
+ end
114
+ end
92
115
  end
116
+
117
+ # app/controllers/genre_controller.rb
118
+ @most_popular = Genre.cached_most_popular
119
+ @tracks = @genre.cached_highlight_tracks
120
+ @artists = @genre.cached_highlight_artists
121
+ @albums = @genre.cached_highlight_albums
93
122
 
94
- Dynamic caches that make use of other cached collections:
123
+ You can configure your caches in this manner wherever you want, but I think the model is a good place. If you wanted to move all your cache configurations to a file in <tt>lib</tt> or elsewhere, your calls would look like,
95
124
 
96
- @tracks = @genre.cached_highlight_tracks(:order => 'release_date DESC', :include => [:album, :artist]) do
97
- cached_tracks(:order => 'release_date DESC', :limit => 10, :include => [:album, :artist])
125
+ Genre.class_caches do
126
+ ...
98
127
  end
99
- @artists = @genre.cached_highlight_artists do
100
- cached_artists(:limit => 10)
101
- end
102
- @albums = @genre.cached_highlight_albums(:order => 'release_date DESC', :include => :artist) do
103
- cached_albums(:order => 'release_date DESC', :limit => 3, :include => :artist)
128
+ Genre.instance_caches do
129
+ ...
104
130
  end
131
+
132
+ == Cache Keys & Managing your Caches
133
+
134
+ === Cache Keys
135
+
136
+ AridCache cache keys are defined based on the methods you call to interact with the cache. For example:
137
+
138
+ Album.cached_featured_albums => cache key is arid-cache-album-featured_albums
139
+ album.cached_top_tracks => cache key is arid-cache-albums/<id>-top_tracks
105
140
 
106
- More docs to come...
141
+ Caches on model instances can be set to automatically incorporate the ActiveRecord <tt>cache_key</tt> which includes the <tt>updated_at</tt> timestamp of that instance, making them auto-expire when the instance is updated. To turn it on pass <tt>:auto_expire => true</tt> to your cache.
142
+
143
+ album.cached_top_tracks(:auto_expire => true) => cache key like arid-cache-albums/2-20091211120100-top_tracks
144
+
145
+ Or via the cache configuration,
146
+
147
+ Album.instance_caches do
148
+ top_tracks(:auto_expire => true)
149
+ end
150
+
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,
152
+
153
+ Album.arid_cache_key('featured_albums') => arid-cache-album-featured_albums
154
+ album.arid_cache_key('top_tracks') => arid-cache-albums/2-top_tracks
155
+ album.arid_cache_key('top_tracks', :auto_expire => true) => arid-cache-albums/2-20091211120100-top_tracks
107
156
 
108
- == Note on Patches/Pull Requests
157
+ == Managing your Caches
158
+
159
+ === Deleting & Expiring Caches
160
+
161
+ AridCache provides methods to help you clear your caches:
162
+
163
+ AridCache.clear_caches => expires all AridCache caches
164
+ Model.clear_caches => expires class and instance-level caches for this model
165
+ Model.clear_instance_caches => expires instance-level caches for this model
166
+ Model.clear_class_caches => expires class-level caches for this model
167
+
168
+ (The <tt>Model.clear_caches</tt> methods are also available on all model instances. Your cache store will need to support the <tt>delete_matched</tt> method for these to work.)
169
+
170
+ Alternatively you can pass a <tt>:force => true</tt> option in your <tt>cached_</tt> calls to force a refresh of a particular cache, while still returning the refreshed results. For example:
171
+
172
+ Album.cached_featured_albums(:force => true) => returns featured albums
173
+ album.cached_top_tracks(:force => true) => returns top tracks
174
+
175
+ You can pass an <tt>:expires_in</tt> option to your caches to manage your cache expiry (if your cache store supports this option).
176
+
177
+ Album.cached_featured_albums(:expires_in => 1.day)
178
+ album.cached_top_tracks(:expires_in => 1.day)
179
+
180
+ Or via the cache configuration,
181
+
182
+ Album.instance_caches(:expires_in => 1.day) do
183
+ top_tracks
184
+ featured_albums
185
+ end
186
+
187
+ 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
188
+
189
+ AridCache::CacheProxy::OPTIONS_FOR_CACHE.push(:raw, :unless_exist)
190
+
191
+ == Extras
192
+
193
+ === Cached Counts
194
+
195
+ AridCache gives you counts for free. When a collection is stored in the cache
196
+ AridCache stores the count as well so the next time you request the count it
197
+ just takes a single read from the cache.
198
+
199
+ To get the count just append <tt>_count</tt> to your <tt>cached_</tt> call. For example, if we have a cache like <tt>album.cached_tracks</tt> we can get the count by calling,
200
+
201
+ album.cached_tracks => returns an array of tracks
202
+ album.cached_tracks_count => returns the count with a single read from the cache
203
+
204
+ This is also supported for your non-ActiveRecord collections if the collection <tt>responds_to?(:count)</tt>. For example,
205
+
206
+ album.cached_similar_genres => returns ['Pop', 'Rock', 'Rockabilly']
207
+ album.cached_similar_genres_count => returns 3
208
+
209
+ Sometimes you may want the collection count without loading and caching the collection itself. AridCache is smart enough that if you only ask for a count it will only query for the count. This is only possible if the return value of your method is a named scope or association proxy (since these are lazy-loaded unlike a call to <tt>find()</tt>).
210
+
211
+ In the example above if we only ever call <tt>album.cached_tracks_count</tt>, only the count will be cached. If we subsequently call <tt>album.cached_tracks</tt> the collection will be loaded and the IDs cached as per normal.
212
+
213
+ Other methods for caching counts are provided for us by virtue of ActiveRecord's built-in methods and named scopes, for example,
214
+
215
+ Artist.cached_count # takes advantage of the built-in method Artist.count
216
+
217
+ === Pagination
218
+
219
+ AridCache supports pagination using WillPaginate. If you are not changing the order of the cached collection the IDs are paginated in memory and only that page is selected from the database - directly from the target table, which is extremely fast.
220
+
221
+ An advantage of using AridCache is that since we already have the size of the collection in the cache no query is required to set the <tt>:total_entries</tt> on the <tt>WillPaginate::Collection</tt>.
222
+
223
+ 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>.
224
+
225
+ Some examples of pagination:
226
+
227
+ User.cached_active(:page => 1, :per_page => 30)
228
+ User.cached_active(:page => 2) # uses User.per_page
229
+ user.cached_pets(:page => 1) # uses Pet.per_page
230
+
231
+ If you want to paginate using a different ordering, pass an <tt>:order</tt> option. Because the order is being changed AridCache cannot paginate in memory. Instead, the cached IDs are passed to your <tt>Model.paginate</tt> method along with any other options and the database will order the collection and apply limits etc.
232
+
233
+ For example, the following queries will work:
234
+
235
+ user.cached_companies(:page => 1, :per_page => 3, :order => 'name DESC')
236
+ user.cached_companies(:page => 1, :per_page => 3, :order => 'name ASC')
237
+
238
+ === Limit & Offset
239
+
240
+ You apply <tt>:limit</tt> and <tt>:offset</tt> options in a similar manner to the <tt>:page</tt> and <tt>:per_page</tt> options. The limit and offset will be applied in memory and only the resulting subset selected from the target table - unless you specify a new order.
241
+
242
+ user.cached_pets(:limit => 2, :include => :toys)
243
+ user.cached_pets(:limit => 2, :offset => 3, :include => :toys)
244
+ genre.cached_top_ten_tracks { cached_tracks(:limit => 10, :order => 'popularity DESC') }
245
+
246
+ === Other Options to <tt>find</tt>
247
+
248
+ 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.
249
+
250
+ For example, assume we have a <tt>named_scope :active</tt> on <tt>User</tt> which gives the active users. We can call:
251
+
252
+ User.cached_active.paginate(:page => 2, :per_page => 10, :include => :preferences)
253
+ User.cached_active(:limit => 10, :offset => 10, :include => :preferences)
254
+
255
+ (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>.)
256
+
257
+ == Efficiency
258
+
259
+ * AridCache intercepts calls to <tt>cached_</tt> methods using <tt>method_missing</tt> then defines those methods on your models as they are called, so they bypass method missing on subsequent calls.
260
+ * In-memory pagination of cached collections speeds up your queries. See _Pagination_.
261
+ * If you only request a count AridCache will only select the count. See <i>Cached Counts</i>.
262
+ * If a collection has already been loaded, you get the count for free. See <i>Cached Counts</i>.
263
+
264
+ == Known Issues
265
+
266
+ 1. Caches that contains duplicate records (for example from the result of a join), will only return unique records on subsequent calls. 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([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.
267
+
268
+ == Contributions
269
+
270
+ Contributions are welcome! Please,
109
271
 
110
272
  * Fork the project.
111
273
  * Make your feature addition or bug fix.
112
- * Add tests for it. This is important so I don't break it in a
113
- future version unintentionally.
114
- * Commit, do not mess with rakefile, version, or history.
115
- (if you want to have your own version, that is fine but
116
- bump version in a commit by itself I can ignore when I pull)
117
- * Send me a pull request. Bonus points for topic branches.
274
+ * Add tests for it (this is important so I don't break it in a future release).
275
+ * Commit (don't mess with the Rakefile, version, or history).
276
+ * Send me a pull request.
118
277
 
119
278
  == Copyright
120
279
 
data/Rakefile CHANGED
@@ -7,16 +7,17 @@ begin
7
7
  gem.name = "arid_cache"
8
8
  gem.summary = %Q{Automates efficient caching of your ActiveRecord collections, gives you counts for free and supports pagination.}
9
9
  gem.description = <<-END.gsub(/^\s+/, '')
10
- ARID Cache makes caching easy and effective. ARID cache supports caching on all your model named scopes, class methods and instance methods right out of the box. ARID cache prevents caching logic from cluttering your models and clarifies your logic by making explicit calls to cached result sets.
10
+ 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.
11
11
 
12
- ARID Cache is designed for handling large, expensive ActiveRecord collections but is equally useful for caching anything else as well.
12
+ AridCache is designed for handling large, expensive ActiveRecord collections but is equally useful for caching anything else as well.
13
13
  END
14
14
  gem.email = "kjvarga@gmail.com"
15
15
  gem.homepage = "http://github.com/kjvarga/arid_cache"
16
16
  gem.authors = ["Karl Varga"]
17
17
  gem.add_dependency "will_paginate"
18
- gem.add_development_dependency "rspec", ">= 1.2.9"
19
18
  gem.add_development_dependency "will_paginate"
19
+ gem.add_development_dependency "faker"
20
+ gem.add_development_dependency "machinist"
20
21
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
21
22
  end
22
23
  Jeweler::GemcutterTasks.new
@@ -24,21 +25,29 @@ rescue LoadError
24
25
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
25
26
  end
26
27
 
27
- require 'spec/rake/spectask'
28
- Spec::Rake::SpecTask.new(:spec) do |spec|
29
- spec.libs << 'lib' << 'spec'
30
- spec.spec_files = FileList['spec/**/*_spec.rb']
31
- end
28
+ # require 'spec/rake/spectask'
29
+ # Spec::Rake::SpecTask.new(:spec) do |spec|
30
+ # spec.libs << 'lib' << 'spec'
31
+ # spec.spec_files = FileList['spec/**/*_spec.rb']
32
+ # end
33
+ #
34
+ # Spec::Rake::SpecTask.new(:rcov) do |spec|
35
+ # spec.libs << 'lib' << 'spec'
36
+ # spec.pattern = 'spec/**/*_spec.rb'
37
+ # spec.rcov = true
38
+ # end
39
+ #
40
+ # task :spec => :check_dependencies
32
41
 
33
- Spec::Rake::SpecTask.new(:rcov) do |spec|
34
- spec.libs << 'lib' << 'spec'
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
42
+ require 'rake/testtask'
43
+ Rake::TestTask.new(:test) do |test|
44
+ test.libs << 'lib' << 'test'
45
+ test.pattern = 'test/**/*_test.rb'
46
+ test.verbose = true
37
47
  end
38
48
 
39
- task :spec => :check_dependencies
40
-
41
- task :default => :spec
49
+ task :test => :check_dependencies
50
+ task :default => :test
42
51
 
43
52
  require 'rake/rdoctask'
44
53
  Rake::RDocTask.new do |rdoc|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.1
data/arid_cache.gemspec CHANGED
@@ -5,13 +5,13 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{arid_cache}
8
- s.version = "0.1.1"
8
+ s.version = "0.2.1"
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{2009-12-25}
13
- s.description = %q{ARID Cache makes caching easy and effective. ARID cache supports caching on all your model named scopes, class methods and instance methods right out of the box. ARID cache prevents caching logic from cluttering your models and clarifies your logic by making explicit calls to cached result sets.
14
- ARID Cache is designed for handling large, expensive ActiveRecord collections but is equally useful for caching anything else as well.
12
+ s.date = %q{2010-01-15}
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
+ AridCache is designed for handling large, expensive ActiveRecord collections but is equally useful for caching anything else as well.
15
15
  }
16
16
  s.email = %q{kjvarga@gmail.com}
17
17
  s.extra_rdoc_files = [
@@ -40,8 +40,8 @@ ARID Cache is designed for handling large, expensive ActiveRecord collections bu
40
40
  "test/console",
41
41
  "test/db/prepare.rb",
42
42
  "test/db/schema.rb",
43
- "test/fixtures/companies.yml",
44
- "test/fixtures/users.yml",
43
+ "test/lib/active_support/cache/file_store_extras.rb",
44
+ "test/lib/blueprint.rb",
45
45
  "test/log/.gitignore",
46
46
  "test/models/company.rb",
47
47
  "test/models/user.rb",
@@ -58,6 +58,8 @@ ARID Cache is designed for handling large, expensive ActiveRecord collections bu
58
58
  "test/arid_cache_test.rb",
59
59
  "test/db/prepare.rb",
60
60
  "test/db/schema.rb",
61
+ "test/lib/active_support/cache/file_store_extras.rb",
62
+ "test/lib/blueprint.rb",
61
63
  "test/models/company.rb",
62
64
  "test/models/user.rb",
63
65
  "test/test_helper.rb"
@@ -69,17 +71,20 @@ ARID Cache is designed for handling large, expensive ActiveRecord collections bu
69
71
 
70
72
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
71
73
  s.add_runtime_dependency(%q<will_paginate>, [">= 0"])
72
- s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
73
74
  s.add_development_dependency(%q<will_paginate>, [">= 0"])
75
+ s.add_development_dependency(%q<faker>, [">= 0"])
76
+ s.add_development_dependency(%q<machinist>, [">= 0"])
74
77
  else
75
78
  s.add_dependency(%q<will_paginate>, [">= 0"])
76
- s.add_dependency(%q<rspec>, [">= 1.2.9"])
77
79
  s.add_dependency(%q<will_paginate>, [">= 0"])
80
+ s.add_dependency(%q<faker>, [">= 0"])
81
+ s.add_dependency(%q<machinist>, [">= 0"])
78
82
  end
79
83
  else
80
84
  s.add_dependency(%q<will_paginate>, [">= 0"])
81
- s.add_dependency(%q<rspec>, [">= 1.2.9"])
82
85
  s.add_dependency(%q<will_paginate>, [">= 0"])
86
+ s.add_dependency(%q<faker>, [">= 0"])
87
+ s.add_dependency(%q<machinist>, [">= 0"])
83
88
  end
84
89
  end
85
90
 
@@ -1,18 +1,21 @@
1
1
  module AridCache
2
2
  module ActiveRecord
3
3
  def self.included(base)
4
+ base.extend ClassMethods
4
5
  base.extend MirrorMethods
5
6
  base.send :include, MirrorMethods
6
7
  base.class_eval do
7
8
  alias_method_chain :method_missing, :arid_cache
9
+ alias_method_chain :respond_to?, :arid_cache
8
10
  end
9
11
  class << base
10
12
  alias_method_chain :method_missing, :arid_cache
13
+ alias_method_chain :respond_to?, :arid_cache
11
14
  end
12
15
  end
13
16
 
14
17
  module MirrorMethods
15
- def clear_all_caches
18
+ def clear_caches
16
19
  AridCache.cache.clear_class_caches(self)
17
20
  AridCache.cache.clear_instance_caches(self)
18
21
  end
@@ -24,44 +27,69 @@ module AridCache
24
27
  def clear_instance_caches
25
28
  AridCache.cache.clear_instance_caches(self)
26
29
  end
27
-
28
- def get_singleton
29
- class << self; self; end
30
- end
31
30
 
32
- # Return a cache key for the given key e.g.
31
+ # Return an AridCache key for the given key fragment for this object.
32
+ #
33
+ # Supported options:
34
+ # :auto_expire => true/false # (default false) whether or not to use the <tt>cache_key</tt> method on instance caches
35
+ #
36
+ # Examples:
33
37
  # User.arid_cache_key('companies') => 'user-companies'
34
- # User.first.arid_cache_key('companies') => 'users/20090120091123-companies'
35
- def arid_cache_key(key)
36
- key = (self.is_a?(Class) ? self.name.downcase : self.cache_key) + '-' + key.to_s
37
- 'arid-cache-' + key
38
+ # User.first.arid_cache_key('companies') => 'users/1-companies'
39
+ # User.first.arid_cache_key('companies', :auto_expire => true) => 'users/20090120091123-companies'
40
+ #
41
+ def arid_cache_key(key, options={})
42
+ object_key = if self.is_a?(Class)
43
+ self.name.downcase
44
+ elsif options[:auto_expire]
45
+ self.cache_key
46
+ else
47
+ "#{self.class.name.downcase.pluralize}/#{self.id}"
48
+ end
49
+ 'arid-cache-' + object_key + '-' + key.to_s
38
50
  end
39
51
 
40
- def respond_to?(method, include_private = false) #:nodoc:
41
- if (method.to_s =~ /^class_cache_.*|cache_.*|cached_.*(_count)?$/).nil?
42
- super(method, include_private)
52
+ def respond_to_with_arid_cache?(method, include_private = false) #:nodoc:
53
+ if (method.to_s =~ /^cached_.*(_count)?$/).nil?
54
+ respond_to_without_arid_cache?(method, include_private)
43
55
  elsif method.to_s =~ /^cached_(.*)_count$/
44
56
  AridCache.store.has?(self, "#{$1}_count") || AridCache.store.has?(self, $1) || super("#{$1}_count", include_private) || super($1, include_private)
45
57
  elsif method.to_s =~ /^cached_(.*)$/
46
58
  AridCache.store.has?(self, $1) || super($1, include_private)
47
59
  else
48
- super(method, include_private)
60
+ respond_to_without_arid_cache?(method, include_private)
49
61
  end
50
62
  end
51
63
 
52
64
  protected
53
65
 
54
- # Intercept methods beginning with <tt>cached_</tt>
55
66
  def method_missing_with_arid_cache(method, *args, &block) #:nodoc:
56
- opts = args.empty? ? {} : args.first
57
- if method.to_s =~ /^cache_(.*)$/
58
- AridCache.define(self, $1, opts, &block)
59
- elsif method.to_s =~ /^cached_(.*)$/
67
+ if method.to_s =~ /^cached_(.*)$/
68
+ opts = args.empty? ? {} : args.first
60
69
  AridCache.lookup(self, $1, opts, &block)
61
70
  else
62
71
  method_missing_without_arid_cache(method, *args)
63
72
  end
64
73
  end
65
74
  end
75
+
76
+ module ClassMethods
77
+
78
+ def instance_caches(opts={}, &block)
79
+ AridCache::Store::InstanceCacheConfiguration.new(self, opts).instance_eval(&block) && nil
80
+ end
81
+
82
+ def class_caches(opts={}, &block)
83
+ AridCache::Store::ClassCacheConfiguration.new(self, opts).instance_eval(&block) && nil
84
+ end
85
+
86
+ def is_mysql_adapter?
87
+ @is_mysql_adapter ||= ::ActiveRecord::Base.connection.adapter_name =~ /MySQL/
88
+ end
89
+
90
+ def is_mysql_adapter=(value)
91
+ @is_mysql_adapter = value
92
+ end
93
+ end
66
94
  end
67
95
  end