arid_cache 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +18 -0
- data/Gemfile.lock +59 -0
- data/README.rdoc +44 -28
- data/Rakefile +15 -14
- data/VERSION +1 -1
- data/arid_cache.gemspec +24 -14
- data/lib/arid_cache/cache_proxy.rb +14 -13
- data/spec/arid_cache_spec.rb +34 -2
- data/spec/spec_helper.rb +25 -8
- data/spec/support/ar_query.rb +128 -0
- data/spec/support/custom_methods.rb +7 -0
- data/spec/support/matchers.rb +33 -0
- data/test/arid_cache_test.rb +1 -1
- data/test/console +13 -6
- data/test/lib/add_query_counting_to_active_record.rb +11 -0
- data/test/lib/db_prepare.rb +34 -0
- data/test/lib/{active_support/cache/file_store_extras.rb → fix_active_support_file_store_expires_in.rb} +1 -0
- data/test/lib/mock_rails.rb +29 -0
- data/test/{models → lib/models}/company.rb +0 -0
- data/test/{models → lib/models}/empty_user_relation.rb +0 -0
- data/test/{models → lib/models}/user.rb +0 -0
- data/test/test_helper.rb +9 -47
- metadata +42 -32
- data/test/db/prepare.rb +0 -33
- data/test/db/schema.rb +0 -19
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# A sample Gemfile
|
2
|
+
source "http://rubygems.org"
|
3
|
+
|
4
|
+
gem 'activerecord', '=2.3.8'
|
5
|
+
gem 'activesupport', '=2.3.8'
|
6
|
+
gem 'sqlite3-ruby', '=1.3.1'
|
7
|
+
|
8
|
+
gem 'will_paginate', '=2.3.15'
|
9
|
+
gem 'jeweler', '=1.4.0'
|
10
|
+
|
11
|
+
gem 'ruby-debug', '=0.10.3'
|
12
|
+
gem 'ruby-debug-base', '=0.10.3'
|
13
|
+
gem 'machinist', '=1.0.6'
|
14
|
+
gem 'faker', '=0.3.1'
|
15
|
+
gem 'rspec', '=1.3.0', :require => 'spec'
|
16
|
+
gem 'test-unit', '=1.2.3'
|
17
|
+
gem 'mocha', '=0.9.8'
|
18
|
+
gem 'arid_cache', :path => "./"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
arid_cache (1.0.2)
|
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)
|
data/README.rdoc
CHANGED
@@ -9,9 +9,9 @@ AridCache simplifies caching by supporting auto-expiring cache keys - as well as
|
|
9
9
|
== Install
|
10
10
|
|
11
11
|
Add this to your <tt>config/environment.rb</tt> file:
|
12
|
-
|
12
|
+
|
13
13
|
config.gem 'arid_cache'
|
14
|
-
|
14
|
+
|
15
15
|
Then
|
16
16
|
|
17
17
|
rake gems:install
|
@@ -28,7 +28,7 @@ The way you interact with the cache via your model methods is to prepend the met
|
|
28
28
|
|
29
29
|
User.cached_count # cache key is arid-cache-user-count
|
30
30
|
genre.cached_top_ten_tracks # cache key is arid-cache-genres/<id>-top_ten_tracks
|
31
|
-
|
31
|
+
|
32
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,
|
33
33
|
|
34
34
|
# cache key is arid-cache-user-most_active_users
|
@@ -78,13 +78,13 @@ To dynamically define caches just pass a block to your <tt>cached_</tt> calls.
|
|
78
78
|
User.cached_most_active_users do
|
79
79
|
active.find(:order => 'activity DESC', :limit => 5)
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
=> [#<User id: 23>, #<User id: 30>, #<User id: 5>, #<User id: 2>, #<User id: 101>]
|
83
|
-
|
83
|
+
|
84
84
|
user.cached_favorite_pets do
|
85
85
|
pets.find(:all, :conditions => { 'favorite' => true })
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
=> [#<Pet id: 11>, #<Pet id: 21>, #<Pet id: 3>]
|
89
89
|
|
90
90
|
=== Configuring Caches on your Models
|
@@ -100,12 +100,12 @@ You can pass a hash of options to <tt>instance_caches</tt> and <tt>class_caches<
|
|
100
100
|
popular(:limit => 10, :order => 'popularity DESC')
|
101
101
|
end
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
instance_caches(:order => 'release_date DESC') do
|
105
105
|
highlight_tracks(:include => [:album, :artist]) do
|
106
106
|
cached_tracks(:limit => 10, :include => [:album, :artist])
|
107
107
|
end
|
108
|
-
highlight_artists(:order => nil) do # override the global :order option
|
108
|
+
highlight_artists(:order => nil) do # override the global :order option
|
109
109
|
cached_artists(:limit => 10)
|
110
110
|
end
|
111
111
|
highlight_albums(:include => :artist) do
|
@@ -113,7 +113,7 @@ You can pass a hash of options to <tt>instance_caches</tt> and <tt>class_caches<
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
# app/controllers/genre_controller.rb
|
118
118
|
@most_popular = Genre.cached_most_popular
|
119
119
|
@tracks = @genre.cached_highlight_tracks
|
@@ -135,8 +135,8 @@ AridCache cache keys are defined based on the methods you call to interact with
|
|
135
135
|
|
136
136
|
Album.cached_featured_albums => cache key is arid-cache-album-featured_albums
|
137
137
|
album.cached_top_tracks => cache key is arid-cache-albums/<id>-top_tracks
|
138
|
-
|
139
|
-
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.
|
138
|
+
|
139
|
+
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.
|
140
140
|
|
141
141
|
To incorporate the the <tt>cache_key</tt> pass <b><tt>:auto_expire => true</tt></b> to your cache method:
|
142
142
|
|
@@ -153,22 +153,22 @@ If you need to examine values in the cache yourself you can build the AridCache
|
|
153
153
|
Album.arid_cache_key('featured_albums') => arid-cache-album-featured_albums
|
154
154
|
album.arid_cache_key('top_tracks') => arid-cache-albums/2-top_tracks
|
155
155
|
album.arid_cache_key('top_tracks', :auto_expire => true) => arid-cache-albums/2-20091211120100-top_tracks
|
156
|
-
|
156
|
+
|
157
157
|
== Managing your Caches
|
158
158
|
|
159
159
|
=== Deleting & Expiring Caches
|
160
160
|
|
161
161
|
AridCache provides methods to help you clear your caches:
|
162
|
-
|
162
|
+
|
163
163
|
AridCache.clear_caches => expires all AridCache caches
|
164
164
|
Model.clear_caches => expires class and instance-level caches for this model
|
165
165
|
Model.clear_instance_caches => expires instance-level caches for this model
|
166
166
|
Model.clear_class_caches => expires class-level caches for this model
|
167
|
-
|
167
|
+
|
168
168
|
The <tt>Model.clear_caches</tt> methods are also available on all model instances.
|
169
169
|
|
170
170
|
<B>Your cache store needs to support the <tt>delete_matched</tt> method for the above to work. Currently MemCacheStore and MemoryStore do not.</b>
|
171
|
-
|
171
|
+
|
172
172
|
Alternatively you can pass a <b><tt>:force => true</tt></b> option in your <tt>cached_</tt> calls to force a refresh of a particular cache, while still returning the refreshed results. For example:
|
173
173
|
|
174
174
|
Album.cached_featured_albums(:force => true) => returns featured albums
|
@@ -190,7 +190,7 @@ If you would like to be able to pass more options to your cache store (like <tt>
|
|
190
190
|
|
191
191
|
AridCache::CacheProxy::OPTIONS_FOR_CACHE.push(:raw, :unless_exist)
|
192
192
|
|
193
|
-
== Extras
|
193
|
+
== Extras
|
194
194
|
|
195
195
|
=== Cached Counts
|
196
196
|
|
@@ -199,7 +199,7 @@ AridCache stores the count as well so the next time you request the count it
|
|
199
199
|
just takes a single read from the cache.
|
200
200
|
|
201
201
|
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,
|
202
|
-
|
202
|
+
|
203
203
|
album.cached_tracks => returns an array of tracks
|
204
204
|
album.cached_tracks_count => returns the count with a single read from the cache
|
205
205
|
|
@@ -207,7 +207,7 @@ This is also supported for your non-ActiveRecord collections if the collection <
|
|
207
207
|
|
208
208
|
album.cached_similar_genres => returns ['Pop', 'Rock', 'Rockabilly']
|
209
209
|
album.cached_similar_genres_count => returns 3
|
210
|
-
|
210
|
+
|
211
211
|
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>).
|
212
212
|
|
213
213
|
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.
|
@@ -222,21 +222,37 @@ AridCache supports pagination using WillPaginate. If you are not changing the o
|
|
222
222
|
|
223
223
|
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>.
|
224
224
|
|
225
|
-
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
|
225
|
+
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
226
|
|
227
227
|
Some examples of pagination:
|
228
228
|
|
229
229
|
User.cached_active(:page => 1, :per_page => 30)
|
230
|
-
User.cached_active(:page => 2) # uses User.per_page
|
230
|
+
User.cached_active(:page => 2) # uses User.per_page
|
231
231
|
user.cached_pets(:page => 1) # uses Pet.per_page
|
232
232
|
|
233
|
-
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
|
233
|
+
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, apply limits and offsets, etc. Because the number of records the database deals with is limited, this is still much, much faster than ordering over the whole table.
|
234
234
|
|
235
235
|
For example, the following queries will work:
|
236
236
|
|
237
237
|
user.cached_companies(:page => 1, :per_page => 3, :order => 'name DESC')
|
238
238
|
user.cached_companies(:page => 1, :per_page => 3, :order => 'name ASC')
|
239
|
-
|
239
|
+
|
240
|
+
By specifying an <tt>:order</tt> option in our cached call we can get different "views" of the cached collection. I think this a "good thing". However, you need to be aware that in order to guarantee that the ordering you requested is the same as the order of the initial results (when the cache was primed), we have to order in the database. This results in two queries being executed the first time you query the cache (one to prime it and the other to order and return the results). If no order option is specified, we can skip the second query and do everything in memory.
|
241
|
+
|
242
|
+
If you have an expensive cache and don't want that extra query, just define a new cache with your desired ordering and use that. Make sure that the order of the initial results matches your desired ordering. Building on the example above we could do:
|
243
|
+
|
244
|
+
User.instance_caches do
|
245
|
+
companies_asc do
|
246
|
+
companies(:order => 'name ASC')
|
247
|
+
end
|
248
|
+
companies_desc do
|
249
|
+
companies(:order => 'name DESC')
|
250
|
+
end
|
251
|
+
end
|
252
|
+
user.cached_companies_asc(:page => 1, :per_page => 3)
|
253
|
+
user.cached_companies_desc(:page => 1, :per_page => 3)
|
254
|
+
|
255
|
+
|
240
256
|
=== Limit & Offset
|
241
257
|
|
242
258
|
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.
|
@@ -244,18 +260,18 @@ You apply <tt>:limit</tt> and <tt>:offset</tt> options in a similar manner to th
|
|
244
260
|
user.cached_pets(:limit => 2, :include => :toys)
|
245
261
|
user.cached_pets(:limit => 2, :offset => 3, :include => :toys)
|
246
262
|
genre.cached_top_ten_tracks { cached_tracks(:limit => 10, :order => 'popularity DESC') }
|
247
|
-
|
263
|
+
|
248
264
|
=== Other Options to <tt>find</tt>
|
249
|
-
|
265
|
+
|
250
266
|
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.
|
251
267
|
|
252
268
|
For example, assume we have a <tt>named_scope :active</tt> on <tt>User</tt> which gives the active users. We can call:
|
253
|
-
|
269
|
+
|
254
270
|
User.cached_active.paginate(:page => 2, :per_page => 10, :include => :preferences)
|
255
271
|
User.cached_active(:limit => 10, :offset => 10, :include => :preferences)
|
256
272
|
|
257
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>.)
|
258
|
-
|
274
|
+
|
259
275
|
== Efficiency
|
260
276
|
|
261
277
|
* 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.
|
@@ -265,10 +281,10 @@ For example, assume we have a <tt>named_scope :active</tt> on <tt>User</tt> whic
|
|
265
281
|
|
266
282
|
== Compatibility
|
267
283
|
|
268
|
-
Tested on Ruby 1.8.6, 1.8.7 and 1.9.1.
|
284
|
+
Tested on Ruby 1.8.6, 1.8.7 and 1.9.1.
|
269
285
|
|
270
286
|
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:
|
271
|
-
|
287
|
+
|
272
288
|
Array.class_eval { alias count size }
|
273
289
|
|
274
290
|
The version of Rails shouldn't matter much, but it's working on 2.3.4.
|
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
2
|
+
require 'bundler/setup'
|
3
|
+
Bundler.require
|
3
4
|
|
4
5
|
begin
|
5
6
|
require 'jeweler'
|
@@ -25,19 +26,19 @@ rescue LoadError
|
|
25
26
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
29
|
+
require 'spec/rake/spectask'
|
30
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
31
|
+
spec.libs << 'lib' << 'spec'
|
32
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
33
|
+
end
|
34
|
+
|
35
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
36
|
+
spec.libs << 'lib' << 'spec'
|
37
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
38
|
+
spec.rcov = true
|
39
|
+
end
|
40
|
+
|
41
|
+
task :spec => :check_dependencies
|
41
42
|
|
42
43
|
require 'rake/testtask'
|
43
44
|
Rake::TestTask.new(:test) do |test|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.3
|
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.
|
8
|
+
s.version = "1.0.3"
|
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-
|
12
|
+
s.date = %q{2010-10-21}
|
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
|
}
|
@@ -20,6 +20,8 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
|
|
20
20
|
]
|
21
21
|
s.files = [
|
22
22
|
".gitignore",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
23
25
|
"LICENSE",
|
24
26
|
"README.rdoc",
|
25
27
|
"Rakefile",
|
@@ -35,17 +37,21 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
|
|
35
37
|
"spec/arid_cache_spec.rb",
|
36
38
|
"spec/spec.opts",
|
37
39
|
"spec/spec_helper.rb",
|
40
|
+
"spec/support/ar_query.rb",
|
41
|
+
"spec/support/custom_methods.rb",
|
42
|
+
"spec/support/matchers.rb",
|
38
43
|
"tasks/arid_cache_tasks.rake",
|
39
44
|
"test/arid_cache_test.rb",
|
40
45
|
"test/console",
|
41
|
-
"test/
|
42
|
-
"test/db/schema.rb",
|
43
|
-
"test/lib/active_support/cache/file_store_extras.rb",
|
46
|
+
"test/lib/add_query_counting_to_active_record.rb",
|
44
47
|
"test/lib/blueprint.rb",
|
48
|
+
"test/lib/db_prepare.rb",
|
49
|
+
"test/lib/fix_active_support_file_store_expires_in.rb",
|
50
|
+
"test/lib/mock_rails.rb",
|
51
|
+
"test/lib/models/company.rb",
|
52
|
+
"test/lib/models/empty_user_relation.rb",
|
53
|
+
"test/lib/models/user.rb",
|
45
54
|
"test/log/.gitignore",
|
46
|
-
"test/models/company.rb",
|
47
|
-
"test/models/empty_user_relation.rb",
|
48
|
-
"test/models/user.rb",
|
49
55
|
"test/test_helper.rb"
|
50
56
|
]
|
51
57
|
s.homepage = %q{http://github.com/kjvarga/arid_cache}
|
@@ -56,14 +62,18 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
|
|
56
62
|
s.test_files = [
|
57
63
|
"spec/arid_cache_spec.rb",
|
58
64
|
"spec/spec_helper.rb",
|
65
|
+
"spec/support/ar_query.rb",
|
66
|
+
"spec/support/custom_methods.rb",
|
67
|
+
"spec/support/matchers.rb",
|
59
68
|
"test/arid_cache_test.rb",
|
60
|
-
"test/
|
61
|
-
"test/db/schema.rb",
|
62
|
-
"test/lib/active_support/cache/file_store_extras.rb",
|
69
|
+
"test/lib/add_query_counting_to_active_record.rb",
|
63
70
|
"test/lib/blueprint.rb",
|
64
|
-
"test/
|
65
|
-
"test/
|
66
|
-
"test/
|
71
|
+
"test/lib/db_prepare.rb",
|
72
|
+
"test/lib/fix_active_support_file_store_expires_in.rb",
|
73
|
+
"test/lib/mock_rails.rb",
|
74
|
+
"test/lib/models/company.rb",
|
75
|
+
"test/lib/models/empty_user_relation.rb",
|
76
|
+
"test/lib/models/user.rb",
|
67
77
|
"test/test_helper.rb"
|
68
78
|
]
|
69
79
|
|
@@ -68,6 +68,11 @@ module AridCache
|
|
68
68
|
|
69
69
|
self.cache_key = object.arid_cache_key(key, opts_for_cache_key)
|
70
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
|
71
76
|
end
|
72
77
|
|
73
78
|
def fetch_count
|
@@ -89,10 +94,6 @@ module AridCache
|
|
89
94
|
execute_find
|
90
95
|
elsif cached.is_a?(AridCache::CacheProxy::Result)
|
91
96
|
if cached.has_ids?
|
92
|
-
|
93
|
-
# Infer the class of the objects we are returning
|
94
|
-
self.klass = cached.klass || object_base_class
|
95
|
-
|
96
97
|
fetch_from_cache
|
97
98
|
else
|
98
99
|
execute_find
|
@@ -116,11 +117,9 @@ module AridCache
|
|
116
117
|
if paginate?
|
117
118
|
|
118
119
|
# Return a paginated collection
|
119
|
-
|
120
120
|
if cached.ids.empty?
|
121
121
|
|
122
122
|
# No ids, return an empty WillPaginate result
|
123
|
-
|
124
123
|
[].paginate(opts_for_paginate)
|
125
124
|
|
126
125
|
elsif combined_options.include?(:order)
|
@@ -128,14 +127,12 @@ module AridCache
|
|
128
127
|
# An order has been specified. We have to go to the database
|
129
128
|
# and paginate there because the contents of the requested
|
130
129
|
# page will be different.
|
131
|
-
|
132
130
|
klass.paginate(cached.ids, { :total_entries => cached.ids.size }.merge(opts_for_find.merge(opts_for_paginate)))
|
133
131
|
|
134
132
|
else
|
135
133
|
|
136
134
|
# Order is unchanged. We can paginate in memory and only select
|
137
135
|
# those ids that we actually need. This is the most efficient.
|
138
|
-
|
139
136
|
paged_ids = cached.ids.paginate(opts_for_paginate)
|
140
137
|
paged_ids.replace(klass.find_all_by_id(paged_ids, opts_for_find(paged_ids)))
|
141
138
|
|
@@ -146,13 +143,11 @@ module AridCache
|
|
146
143
|
# We are returning a regular (non-paginated) result.
|
147
144
|
# If we don't have any ids, that's an empty result
|
148
145
|
# in any language.
|
149
|
-
|
150
146
|
[]
|
151
147
|
|
152
148
|
elsif combined_options.include?(:order)
|
153
149
|
|
154
150
|
# An order has been specified, so use it.
|
155
|
-
|
156
151
|
klass.find_all_by_id(cached.ids, opts_for_find)
|
157
152
|
|
158
153
|
else
|
@@ -160,7 +155,6 @@ module AridCache
|
|
160
155
|
# No order has been specified, so we have to maintain
|
161
156
|
# the current order. We do this by passing some extra
|
162
157
|
# SQL which orders by the current array ordering.
|
163
|
-
|
164
158
|
offset, limit = combined_options.delete(:offset) || 0, combined_options.delete(:limit) || cached.count
|
165
159
|
ids = cached.ids[offset, limit]
|
166
160
|
|
@@ -201,7 +195,14 @@ module AridCache
|
|
201
195
|
Rails.cache.write(cache_key, cached, opts_for_cache)
|
202
196
|
|
203
197
|
self.cached = cached
|
204
|
-
|
198
|
+
# An order has been specified. We have to go to the database
|
199
|
+
# to order because we can't be sure that the current order is the same as the cache.
|
200
|
+
if cached.is_a?(AridCache::CacheProxy::Result) && combined_options.include?(:order)
|
201
|
+
self.klass = self.cached.klass # TODO used by fetch_from_cache needs refactor
|
202
|
+
fetch_from_cache
|
203
|
+
else
|
204
|
+
process_result_in_memory(records)
|
205
|
+
end
|
205
206
|
end
|
206
207
|
|
207
208
|
# Convert records to an array before calling paginate. If we don't do this
|
@@ -222,7 +223,7 @@ module AridCache
|
|
222
223
|
# to define it in a block and have a new cache for it:
|
223
224
|
#
|
224
225
|
# model.my_special_collection { the_collection(:order => 'new order', :limit => 10) }
|
225
|
-
def
|
226
|
+
def process_result_in_memory(records)
|
226
227
|
if opts.include?(:page)
|
227
228
|
records = records.respond_to?(:to_a) ? records.to_a : records
|
228
229
|
records.respond_to?(:paginate) ? records.paginate(opts_for_paginate) : records
|
data/spec/arid_cache_spec.rb
CHANGED
@@ -1,7 +1,39 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe "AridCache" do
|
4
|
-
|
5
|
-
|
4
|
+
describe 'results' do
|
5
|
+
before :each do
|
6
|
+
Company.destroy_all
|
7
|
+
@company1 = Company.make(:name => 'a')
|
8
|
+
@company2 = Company.make(:name => 'b')
|
9
|
+
Company.class_caches do
|
10
|
+
ordered_by_name { Company.all(:order => 'name ASC') }
|
11
|
+
end
|
12
|
+
Company.clear_caches
|
13
|
+
end
|
14
|
+
|
15
|
+
it "order should match the original order" do
|
16
|
+
3.times do |t|
|
17
|
+
results = Company.cached_ordered_by_name
|
18
|
+
results.size.should == 2
|
19
|
+
results[0].name.should == @company1.name
|
20
|
+
results[1].name.should == @company2.name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "order should match the order option" do
|
25
|
+
3.times do |t|
|
26
|
+
results = Company.cached_ordered_by_name(:order => 'name DESC')
|
27
|
+
results.size.should == 2
|
28
|
+
results[0].name.should == @company2.name
|
29
|
+
results[1].name.should == @company1.name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "with order option should go to the database to order" do
|
34
|
+
lambda {
|
35
|
+
Company.cached_ordered_by_name(:order => 'name DESC')
|
36
|
+
}.should query(2)
|
37
|
+
end
|
6
38
|
end
|
7
39
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,29 @@
|
|
1
|
-
|
2
|
-
$LOAD_PATH.unshift(File.join(
|
3
|
-
|
4
|
-
require '
|
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
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
Bundler.require
|
6
|
+
|
5
7
|
require 'spec/autorun'
|
8
|
+
require 'mock_rails'
|
9
|
+
require 'blueprint'
|
10
|
+
|
11
|
+
WillPaginate.enable_activerecord
|
12
|
+
AridCache.init_rails
|
6
13
|
|
7
|
-
require
|
8
|
-
require 'will_paginate'
|
14
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
|
9
15
|
|
10
16
|
Spec::Runner.configure do |config|
|
11
|
-
|
12
|
-
|
17
|
+
include ActiveRecordQueryMatchers
|
18
|
+
config.mock_with :mocha
|
19
|
+
|
20
|
+
config.before(:all) do
|
21
|
+
Sham.reset(:before_all)
|
22
|
+
end
|
23
|
+
|
24
|
+
config.before(:each) do
|
25
|
+
Sham.reset(:before_each)
|
26
|
+
full_example_description = "#{self.class.description} #{@method_name}"
|
27
|
+
RAILS_DEFAULT_LOGGER.info("\n\n#{full_example_description}\n#{'-' * (full_example_description.length)}")
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module ActiveRecordQueryMatchers
|
2
|
+
|
3
|
+
class ArQuery #:nodoc:
|
4
|
+
cattr_accessor :executed
|
5
|
+
|
6
|
+
@@recording_queries = false
|
7
|
+
def self.recording_queries?
|
8
|
+
@@recording_queries
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(expected, &block)
|
12
|
+
@expected = expected
|
13
|
+
@block = block
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(given_proc)
|
17
|
+
@eval_block = false
|
18
|
+
@eval_error = nil
|
19
|
+
ArQuery.executed = []
|
20
|
+
@@recording_queries = true
|
21
|
+
|
22
|
+
given_proc.call
|
23
|
+
|
24
|
+
if @expected.is_a?(Fixnum)
|
25
|
+
@actual = ArQuery.executed.length
|
26
|
+
@matched = @actual == @expected
|
27
|
+
else
|
28
|
+
@actual = ArQuery.executed.detect { |sql| @expected === sql }
|
29
|
+
@matched = !@actual.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
eval_block if @block && @matched && !negative_expectation?
|
33
|
+
|
34
|
+
ensure
|
35
|
+
ArQuery.executed = nil
|
36
|
+
@@recording_queries = false
|
37
|
+
return @matched && @eval_error.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def eval_block
|
41
|
+
@eval_block = true
|
42
|
+
begin
|
43
|
+
@block[ArQuery.executed]
|
44
|
+
rescue Exception => err
|
45
|
+
@eval_error = err
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def failure_message_for_should
|
50
|
+
if @eval_error
|
51
|
+
@eval_error.message
|
52
|
+
elsif @expected.is_a?(Fixnum)
|
53
|
+
"expected #{@expected}, got #{@actual}"
|
54
|
+
else
|
55
|
+
"expected to execute a query with pattern #{@expected.inspect}, but it wasn't"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def failure_message_for_should_not
|
60
|
+
if @expected.is_a?(Fixnum)
|
61
|
+
"did not expect #{@expected}"
|
62
|
+
else
|
63
|
+
"did not expect to execute a query with pattern #{@expected.inspect}, but it was executed"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def description
|
68
|
+
if @expected.is_a?(Fixnum)
|
69
|
+
@expected == 1 ? "execute 1 query" : "execute #{@expected} queries"
|
70
|
+
else
|
71
|
+
"execute query with pattern #{@expected.inspect}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Copied from raise_error
|
76
|
+
def negative_expectation?
|
77
|
+
@negative_expectation ||= !caller.first(3).find { |s| s =~ /should_not/ }.nil?
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
unless defined?(IGNORED_SQL)
|
83
|
+
# From active_record/test/cases/helper.rb :
|
84
|
+
ActiveRecord::Base.connection.class.class_eval do
|
85
|
+
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /SHOW FIELDS/]
|
86
|
+
def execute_with_query_record(sql, name = nil, &block)
|
87
|
+
if ArQuery.recording_queries?
|
88
|
+
ArQuery.executed << sql unless IGNORED_SQL.any? { |ignore| sql =~ ignore }
|
89
|
+
end
|
90
|
+
execute_without_query_record(sql, name, &block)
|
91
|
+
end
|
92
|
+
alias_method_chain :execute, :query_record
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# :call-seq:
|
97
|
+
# response.should query
|
98
|
+
# response.should query(expected)
|
99
|
+
# response.should query(expected) { |sql| ... }
|
100
|
+
# response.should_not query
|
101
|
+
# response.should_not query(expected)
|
102
|
+
#
|
103
|
+
# Accepts a Fixnum or a Regexp as argument.
|
104
|
+
#
|
105
|
+
# With no args, matches if exactly 1 query is executed.
|
106
|
+
# With a Fixnum arg, matches if the number of queries executed equals the given number.
|
107
|
+
# With a Regexp arg, matches if any query is executed with the given pattern.
|
108
|
+
#
|
109
|
+
# Pass an optional block to perform extra verifications of the queries matched.
|
110
|
+
# The argument of the block will receive an array of query strings that were executed.
|
111
|
+
#
|
112
|
+
# == Examples
|
113
|
+
#
|
114
|
+
# lambda { @object.posts }.should query
|
115
|
+
# lambda { @object.valid? }.should query(0)
|
116
|
+
# lambda { @object.save }.should query(3)
|
117
|
+
# lambda { @object.line_items }.should query(/SELECT DISTINCT/)
|
118
|
+
# lambda { @object.line_items }.should query(1) { |sql| sql[0].should =~ /SELECT DISTINCT/ }
|
119
|
+
#
|
120
|
+
# lambda { @object.posts }.should_not query
|
121
|
+
# lambda { @object.valid? }.should_not query(0)
|
122
|
+
# lambda { @object.save }.should_not query(3)
|
123
|
+
# lambda { @object.line_items }.should_not query(/SELECT DISTINCT/)
|
124
|
+
#
|
125
|
+
def query(expected = 1, &block)
|
126
|
+
ArQuery.new(expected, &block)
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
Spec::Matchers.define :include_keys do |*expected|
|
2
|
+
|
3
|
+
match do |actual|
|
4
|
+
check_all_present(actual, expected) == []
|
5
|
+
end
|
6
|
+
|
7
|
+
failure_message_for_should do |actual|
|
8
|
+
"expected key #{check_all_present(actual, expected).first} but did not see it in #{actual.keys.map(&:to_sym)}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def check_all_present actual, expected
|
12
|
+
keys_we_have = actual.keys.map(&:to_sym)
|
13
|
+
expected = [expected] unless expected.is_a?(Array)
|
14
|
+
remainder = expected.map(&:to_sym) - keys_we_have
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Spec::Matchers.define :match_object do |object, *expected_matching_keys|
|
19
|
+
|
20
|
+
match do |actual|
|
21
|
+
check_specified_keys_match(actual, object, expected_matching_keys) == []
|
22
|
+
end
|
23
|
+
|
24
|
+
failure_message_for_should do |actual|
|
25
|
+
offending_key = check_specified_keys_match(actual, object, expected_matching_keys).first
|
26
|
+
"expected match for #{offending_key} but it did not. Expected: #{object.send(offending_key).inspect} but got #{actual[offending_key].inspect}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def check_specified_keys_match actual, object, expected_matches
|
30
|
+
expected_matches = [expected_matches] unless expected_matches.is_a?(Array)
|
31
|
+
expected_matches.map(&:to_sym).map { |key| key unless (object.send(key) == actual[key]) }.compact
|
32
|
+
end
|
33
|
+
end
|
data/test/arid_cache_test.rb
CHANGED
@@ -76,7 +76,7 @@ class AridCacheTest < ActiveSupport::TestCase
|
|
76
76
|
end
|
77
77
|
|
78
78
|
test "passes on options to find" do
|
79
|
-
actual = @user.cached_companies(:order => 'users.id DESC')
|
79
|
+
actual = @user.cached_companies(:order => 'users.id DESC', :include => [:owner])
|
80
80
|
expected = @user.companies
|
81
81
|
assert_equal expected, actual
|
82
82
|
assert_equal expected.first, actual.first
|
data/test/console
CHANGED
@@ -1,8 +1,15 @@
|
|
1
|
-
|
2
|
-
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
3
|
-
libs = []
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
4
2
|
|
5
|
-
|
6
|
-
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
Bundler.require
|
6
|
+
require 'mock_rails'
|
7
|
+
require 'blueprint'
|
8
|
+
require 'irb'
|
7
9
|
|
8
|
-
|
10
|
+
WillPaginate.enable_activerecord
|
11
|
+
AridCache.init_rails
|
12
|
+
Blueprint.seeds
|
13
|
+
|
14
|
+
ARGV.clear
|
15
|
+
IRB.start
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class << ActiveRecord::Base.connection
|
2
|
+
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SHOW FIELDS /]
|
3
|
+
|
4
|
+
def execute_with_counting(sql, name = nil, &block)
|
5
|
+
$query_count ||= 0
|
6
|
+
$query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
|
7
|
+
execute_without_counting(sql, name, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
alias_method_chain :execute, :counting
|
11
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
# Create an in-memory sqlite3 database
|
4
|
+
ActiveRecord::Base.establish_connection(
|
5
|
+
:adapter => "sqlite3",
|
6
|
+
:database => ":memory:"
|
7
|
+
)
|
8
|
+
|
9
|
+
# Schema
|
10
|
+
ActiveRecord::Base.silence do
|
11
|
+
ActiveRecord::Migration.verbose = false
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define do
|
14
|
+
create_table "users", :force => true do |t|
|
15
|
+
t.column "name", :text
|
16
|
+
t.column "email", :text
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table "companies", :force => true do |t|
|
21
|
+
t.column "name", :text
|
22
|
+
t.column "owner_id", :integer
|
23
|
+
t.column "country_id", :integer
|
24
|
+
t.column "employees", :integer
|
25
|
+
t.timestamps
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table "empty_user_relations", :force => true do |t|
|
29
|
+
t.column "user_id", :integer
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Dir[File.join(File.dirname(__FILE__), 'models', '*.rb')].each { |f| require f }
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'fix_active_support_file_store_expires_in'
|
3
|
+
|
4
|
+
root_path = File.expand_path(File.join(File.dirname(__FILE__), '../../'))
|
5
|
+
RAILS_DEFAULT_LOGGER = ENV["STDOUT"] ? Logger.new(STDOUT) : Logger.new(File.join(root_path, '/test/log/test.log'))
|
6
|
+
RAILS_CACHE = ActiveSupport::Cache.lookup_store(:file_store, File.join(root_path, '/tmp/cache'))
|
7
|
+
|
8
|
+
# Mock Rails
|
9
|
+
Rails = Class.new do
|
10
|
+
cattr_accessor :logger, :cache
|
11
|
+
def self.cache
|
12
|
+
return RAILS_CACHE
|
13
|
+
end
|
14
|
+
def self.logger
|
15
|
+
return RAILS_DEFAULT_LOGGER
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Set loggers for all frameworks
|
20
|
+
for framework in ([ :active_record, :action_controller, :action_mailer ])
|
21
|
+
if Object.const_defined?(framework.to_s.camelize)
|
22
|
+
framework.to_s.camelize.constantize.const_get("Base").logger = Rails.logger
|
23
|
+
end
|
24
|
+
end
|
25
|
+
ActiveSupport::Dependencies.logger = Rails.logger
|
26
|
+
Rails.cache.logger = Rails.logger
|
27
|
+
|
28
|
+
# Include this last otherwise the logger isn't set properly
|
29
|
+
require 'db_prepare'
|
File without changes
|
File without changes
|
File without changes
|
data/test/test_helper.rb
CHANGED
@@ -1,57 +1,19 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) # AridCache lib
|
3
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) # test lib
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) # make requiring from test/lib easy
|
4
2
|
|
5
3
|
require 'fileutils'
|
6
4
|
require 'rubygems'
|
7
|
-
require '
|
8
|
-
require
|
9
|
-
require 'active_support/test_case'
|
5
|
+
require 'bundler/setup'
|
6
|
+
Bundler.require
|
10
7
|
require 'test/unit' # required by ActiveSupport::TestCase
|
11
|
-
require '
|
12
|
-
require 'ruby-debug'
|
13
|
-
|
14
|
-
# Activate Will Paginate
|
15
|
-
WillPaginate.enable_activerecord
|
8
|
+
require 'active_support/test_case'
|
16
9
|
|
17
|
-
|
18
|
-
require '
|
10
|
+
require 'mock_rails'
|
11
|
+
require 'blueprint'
|
12
|
+
require 'add_query_counting_to_active_record'
|
19
13
|
|
20
|
-
|
21
|
-
require 'arid_cache'
|
14
|
+
WillPaginate.enable_activerecord
|
22
15
|
AridCache.init_rails
|
23
|
-
|
24
|
-
# Setup logging
|
25
|
-
log = File.expand_path(File.join(File.dirname(__FILE__), 'log', 'test.log'))
|
26
|
-
RAILS_DEFAULT_LOGGER = ENV["STDOUT"] ? Logger.new(STDOUT) : Logger.new(log)
|
27
|
-
|
28
|
-
# Setup the cache. Use the file-store cache because the
|
29
|
-
# memory-store cache doesn't delete cache keys...I don't know why.
|
30
|
-
RAILS_CACHE = ActiveSupport::Cache.lookup_store(:file_store, "#{File.dirname(__FILE__)}/tmp/cache")
|
31
|
-
|
32
|
-
# Mock Rails
|
33
|
-
Rails = Class.new do
|
34
|
-
cattr_accessor :logger, :cache
|
35
|
-
def self.cache
|
36
|
-
return RAILS_CACHE
|
37
|
-
end
|
38
|
-
def self.logger
|
39
|
-
return RAILS_DEFAULT_LOGGER
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Set loggers for all frameworks
|
44
|
-
for framework in ([ :active_record, :action_controller, :action_mailer ])
|
45
|
-
if Object.const_defined?(framework.to_s.camelize)
|
46
|
-
framework.to_s.camelize.constantize.const_get("Base").logger = Rails.logger
|
47
|
-
end
|
48
|
-
end
|
49
|
-
ActiveSupport::Dependencies.logger = Rails.logger
|
50
|
-
Rails.cache.logger = Rails.logger
|
51
|
-
|
52
|
-
# Include this last otherwise the logger isn't set properly
|
53
|
-
require 'db/prepare'
|
16
|
+
Blueprint.seeds
|
54
17
|
|
55
18
|
ActiveRecord::Base.logger.info("#{"="*25} RUNNING UNIT TESTS #{"="*25}\n\t\t\t#{Time.now.to_s}\n#{"="*70}")
|
56
|
-
|
57
19
|
Array.class_eval { alias count size } if RUBY_VERSION < '1.8.7'
|
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:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 3
|
10
|
+
version: 1.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Karl Varga
|
@@ -15,13 +15,14 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-10-21 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
name: will_paginate
|
23
22
|
prerelease: false
|
24
|
-
|
23
|
+
type: :runtime
|
24
|
+
name: will_paginate
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
25
26
|
none: false
|
26
27
|
requirements:
|
27
28
|
- - ">="
|
@@ -30,12 +31,12 @@ dependencies:
|
|
30
31
|
segments:
|
31
32
|
- 0
|
32
33
|
version: "0"
|
33
|
-
|
34
|
-
version_requirements: *id001
|
34
|
+
requirement: *id001
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
|
-
name: will_paginate
|
37
36
|
prerelease: false
|
38
|
-
|
37
|
+
type: :development
|
38
|
+
name: will_paginate
|
39
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
39
40
|
none: false
|
40
41
|
requirements:
|
41
42
|
- - ">="
|
@@ -44,12 +45,12 @@ dependencies:
|
|
44
45
|
segments:
|
45
46
|
- 0
|
46
47
|
version: "0"
|
47
|
-
|
48
|
-
version_requirements: *id002
|
48
|
+
requirement: *id002
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
|
-
name: faker
|
51
50
|
prerelease: false
|
52
|
-
|
51
|
+
type: :development
|
52
|
+
name: faker
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
53
54
|
none: false
|
54
55
|
requirements:
|
55
56
|
- - ">="
|
@@ -58,12 +59,12 @@ dependencies:
|
|
58
59
|
segments:
|
59
60
|
- 0
|
60
61
|
version: "0"
|
61
|
-
|
62
|
-
version_requirements: *id003
|
62
|
+
requirement: *id003
|
63
63
|
- !ruby/object:Gem::Dependency
|
64
|
-
name: machinist
|
65
64
|
prerelease: false
|
66
|
-
|
65
|
+
type: :development
|
66
|
+
name: machinist
|
67
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
67
68
|
none: false
|
68
69
|
requirements:
|
69
70
|
- - ">="
|
@@ -72,8 +73,7 @@ dependencies:
|
|
72
73
|
segments:
|
73
74
|
- 0
|
74
75
|
version: "0"
|
75
|
-
|
76
|
-
version_requirements: *id004
|
76
|
+
requirement: *id004
|
77
77
|
description: |
|
78
78
|
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.
|
79
79
|
AridCache is designed for handling large, expensive ActiveRecord collections but is equally useful for caching anything else as well.
|
@@ -88,6 +88,8 @@ extra_rdoc_files:
|
|
88
88
|
- README.rdoc
|
89
89
|
files:
|
90
90
|
- .gitignore
|
91
|
+
- Gemfile
|
92
|
+
- Gemfile.lock
|
91
93
|
- LICENSE
|
92
94
|
- README.rdoc
|
93
95
|
- Rakefile
|
@@ -103,17 +105,21 @@ files:
|
|
103
105
|
- spec/arid_cache_spec.rb
|
104
106
|
- spec/spec.opts
|
105
107
|
- spec/spec_helper.rb
|
108
|
+
- spec/support/ar_query.rb
|
109
|
+
- spec/support/custom_methods.rb
|
110
|
+
- spec/support/matchers.rb
|
106
111
|
- tasks/arid_cache_tasks.rake
|
107
112
|
- test/arid_cache_test.rb
|
108
113
|
- test/console
|
109
|
-
- test/
|
110
|
-
- test/db/schema.rb
|
111
|
-
- test/lib/active_support/cache/file_store_extras.rb
|
114
|
+
- test/lib/add_query_counting_to_active_record.rb
|
112
115
|
- test/lib/blueprint.rb
|
116
|
+
- test/lib/db_prepare.rb
|
117
|
+
- test/lib/fix_active_support_file_store_expires_in.rb
|
118
|
+
- test/lib/mock_rails.rb
|
119
|
+
- test/lib/models/company.rb
|
120
|
+
- test/lib/models/empty_user_relation.rb
|
121
|
+
- test/lib/models/user.rb
|
113
122
|
- test/log/.gitignore
|
114
|
-
- test/models/company.rb
|
115
|
-
- test/models/empty_user_relation.rb
|
116
|
-
- test/models/user.rb
|
117
123
|
- test/test_helper.rb
|
118
124
|
has_rdoc: true
|
119
125
|
homepage: http://github.com/kjvarga/arid_cache
|
@@ -152,12 +158,16 @@ summary: Automates efficient caching of your ActiveRecord collections, gives you
|
|
152
158
|
test_files:
|
153
159
|
- spec/arid_cache_spec.rb
|
154
160
|
- spec/spec_helper.rb
|
161
|
+
- spec/support/ar_query.rb
|
162
|
+
- spec/support/custom_methods.rb
|
163
|
+
- spec/support/matchers.rb
|
155
164
|
- test/arid_cache_test.rb
|
156
|
-
- test/
|
157
|
-
- test/db/schema.rb
|
158
|
-
- test/lib/active_support/cache/file_store_extras.rb
|
165
|
+
- test/lib/add_query_counting_to_active_record.rb
|
159
166
|
- test/lib/blueprint.rb
|
160
|
-
- test/
|
161
|
-
- test/
|
162
|
-
- test/
|
167
|
+
- test/lib/db_prepare.rb
|
168
|
+
- test/lib/fix_active_support_file_store_expires_in.rb
|
169
|
+
- test/lib/mock_rails.rb
|
170
|
+
- test/lib/models/company.rb
|
171
|
+
- test/lib/models/empty_user_relation.rb
|
172
|
+
- test/lib/models/user.rb
|
163
173
|
- test/test_helper.rb
|
data/test/db/prepare.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'active_record'
|
2
|
-
require 'active_record/fixtures'
|
3
|
-
|
4
|
-
# Create an in-memory test database and load the fixures into it
|
5
|
-
ActiveRecord::Base.establish_connection(
|
6
|
-
:adapter => "sqlite3",
|
7
|
-
:database => ":memory:"
|
8
|
-
)
|
9
|
-
|
10
|
-
# Schema
|
11
|
-
ActiveRecord::Base.silence do
|
12
|
-
ActiveRecord::Migration.verbose = false
|
13
|
-
load(File.join(File.dirname(__FILE__), 'schema.rb'))
|
14
|
-
end
|
15
|
-
|
16
|
-
# Models
|
17
|
-
Dir[File.join(File.dirname(__FILE__), '..', 'models', '*.rb')].each { |f| require f }
|
18
|
-
|
19
|
-
# Populate
|
20
|
-
require 'blueprint'
|
21
|
-
Blueprint.seeds
|
22
|
-
|
23
|
-
class << ActiveRecord::Base.connection
|
24
|
-
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SHOW FIELDS /]
|
25
|
-
|
26
|
-
def execute_with_counting(sql, name = nil, &block)
|
27
|
-
$query_count ||= 0
|
28
|
-
$query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
|
29
|
-
execute_without_counting(sql, name, &block)
|
30
|
-
end
|
31
|
-
|
32
|
-
alias_method_chain :execute, :counting
|
33
|
-
end
|
data/test/db/schema.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
ActiveRecord::Schema.define do
|
2
|
-
create_table "users", :force => true do |t|
|
3
|
-
t.column "name", :text
|
4
|
-
t.column "email", :text
|
5
|
-
t.timestamps
|
6
|
-
end
|
7
|
-
|
8
|
-
create_table "companies", :force => true do |t|
|
9
|
-
t.column "name", :text
|
10
|
-
t.column "owner_id", :integer
|
11
|
-
t.column "country_id", :integer
|
12
|
-
t.column "employees", :integer
|
13
|
-
t.timestamps
|
14
|
-
end
|
15
|
-
|
16
|
-
create_table "empty_user_relations", :force => true do |t|
|
17
|
-
t.column "user_id", :integer
|
18
|
-
end
|
19
|
-
end
|