djmaze-arid_cache 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,29 @@
1
+ root_path = File.expand_path(File.join(File.dirname(__FILE__), '..'))
2
+ $LOAD_PATH.unshift(File.join(root_path, '/test/lib')) # add test/lib to the load path
3
+
4
+ require 'bundler/setup'
5
+ Bundler.require
6
+
7
+ require 'spec/autorun'
8
+ require 'mock_rails'
9
+ require 'blueprint'
10
+
11
+ WillPaginate.enable_activerecord
12
+ AridCache.init_rails
13
+
14
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
15
+
16
+ Spec::Runner.configure do |config|
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,7 @@
1
+ class String
2
+
3
+ def json_to_hash
4
+ HashWithIndifferentAccess.new JSON.parse(self)
5
+ end
6
+
7
+ 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
@@ -0,0 +1,414 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class AridCacheTest < ActiveSupport::TestCase
4
+ def setup
5
+ FileUtils.rm_r(Rails.cache.cache_path) if File.exists?(Rails.cache.cache_path)
6
+ @user = User.first
7
+ end
8
+
9
+ test "initializes needed objects" do
10
+ assert_instance_of AridCache::Store, AridCache.store
11
+ assert_same AridCache::CacheProxy, AridCache.cache
12
+ end
13
+
14
+ test "should respond to methods" do
15
+ assert User.respond_to?(:clear_caches)
16
+ assert @user.respond_to?(:clear_caches)
17
+ assert_instance_of AridCache::Store, AridCache.store
18
+ assert_same AridCache::CacheProxy, AridCache.cache
19
+ end
20
+
21
+ test "should not clobber model methods" do
22
+ assert_respond_to @user, :name
23
+ assert_respond_to Company.first, :name
24
+ assert_nothing_raised { @user.name }
25
+ assert_nothing_raised { Company.first.name }
26
+ end
27
+
28
+ test "should not clobber method_missing" do
29
+ assert_nothing_raised { @user.is_high? }
30
+ assert @user.is_high?
31
+ end
32
+
33
+ test "should not clobber respond_to?" do
34
+ assert @user.respond_to?(:respond_not_overridden)
35
+ end
36
+
37
+ test "should raise an error on invalid dynamic caches" do
38
+ assert_raises ArgumentError do
39
+ @user.cached_invalid_companies
40
+ end
41
+ end
42
+
43
+ test "should create dynamic caches given valid arguments" do
44
+ assert_nothing_raised { @user.cached_companies }
45
+ end
46
+
47
+ test "counts queries correctly" do
48
+ assert_queries(1) { User.all }
49
+ end
50
+
51
+ test "returns valid results" do
52
+ @one = @user.cached_companies
53
+ assert_equal @user.companies, @one
54
+ assert_equal @user.companies.count, @one.size
55
+ end
56
+
57
+ test "paginates results" do
58
+ results = @user.cached_companies(:page => 1, :per_page => 3, :order => 'name desc')
59
+ assert_kind_of WillPaginate::Collection, results
60
+ assert_equal 3, results.size
61
+ assert_equal @user.companies.count, results.total_entries
62
+ assert_equal 1, results.current_page
63
+ end
64
+
65
+ test "should work for different pages" do
66
+ results = @user.cached_companies(:page => 2, :per_page => 3)
67
+ assert_kind_of WillPaginate::Collection, results
68
+ assert results.size <= 3
69
+ assert_equal @user.companies.count, results.total_entries
70
+ assert_equal 2, results.current_page
71
+ end
72
+
73
+ test "ignores random parameters" do
74
+ result = @user.cached_companies(:invalid => :params, 'random' => 'values', :user_id => 3)
75
+ assert_equal @user.companies, result
76
+ end
77
+
78
+ test "passes on options to find" do
79
+ actual = @user.cached_companies(:order => 'users.id DESC', :include => [:owner])
80
+ expected = @user.companies
81
+ assert_equal expected, actual
82
+ assert_equal expected.first, actual.first
83
+ end
84
+
85
+ test "caches the count when it gets records" do
86
+ assert_queries(1) do
87
+ @user.cached_companies
88
+ @user.cached_companies_count
89
+ end
90
+ end
91
+
92
+ test "gets the count only if it's requested first" do
93
+ count = @user.companies.count
94
+ assert_queries(1) do
95
+ assert_equal count, @user.cached_companies_count
96
+ assert_equal count, @user.cached_companies_count
97
+ end
98
+ assert_queries(1) do
99
+ assert_equal count, @user.cached_companies.size
100
+ assert_equal count, @user.cached_companies_count
101
+ end
102
+ end
103
+
104
+ test "applies limit and offset" do
105
+ result = @user.cached_limit_companies do
106
+ companies
107
+ end
108
+ limit_two_result = @user.cached_limit_companies(:limit => 2)
109
+ assert_equal 2, limit_two_result.size
110
+ assert_equal result[0, 2], limit_two_result
111
+
112
+ limit_three_result = @user.cached_limit_companies(:limit => 3)
113
+ assert_equal 3, limit_three_result.size
114
+ assert_equal result[0, 3], limit_three_result
115
+
116
+ limit_two_offset_one_result = @user.cached_limit_companies(:limit => 2, :offset => 1)
117
+ assert_equal 2, limit_two_offset_one_result.size
118
+ assert_equal result[1, 2], limit_two_offset_one_result
119
+
120
+ offset_one_result = @user.cached_limit_companies(:offset => 1)
121
+ assert_equal result.size-1, offset_one_result.size
122
+ assert_equal result[1, result.size], offset_one_result
123
+
124
+ assert_equal @user.companies.all(:limit => 2, :offset => 1), @user.cached_limit_companies(:limit => 2, :offset => 1)
125
+ assert_equal @user.companies.size, @user.cached_limit_companies.size
126
+
127
+ # Careful of this Rails bug: https://rails.lighthouseapp.com/projects/8994/tickets/1349-named-scope-with-group-by-bug
128
+ User.cached_successful_limit_companies do
129
+ User.successful.all
130
+ end
131
+ assert_equal 2, User.cached_successful_limit_companies(:limit => 2).size
132
+ assert_equal 3, User.cached_successful_limit_companies(:limit => 3).size
133
+ assert_equal User.successful.all(:limit => 2, :offset => 1), User.cached_successful_limit_companies(:limit => 2, :offset => 1)
134
+ assert_equal User.successful.all.size, User.cached_successful_limit_companies.size
135
+ end
136
+
137
+ test "should requery the database for limits with order" do
138
+ @user.cached_companies # prime the cache
139
+ assert_equal @user.companies.find(:all, :limit => 3, :order => 'name DESC'), @user.cached_companies(:limit => 3, :order => 'name DESC')
140
+ assert_equal @user.companies.find(:all, :limit => 3, :order => 'name ASC'), @user.cached_companies(:limit => 3, :order => 'name ASC')
141
+ end
142
+
143
+ test "should activate will paginate" do
144
+ assert_nothing_raised do
145
+ User.paginate(:page => 1)
146
+ end
147
+ end
148
+
149
+ test "should requery the database for paginate with order" do
150
+ @user.cached_companies # prime the cache
151
+ assert_equal @user.companies.paginate(:page => 1, :per_page => 3, :order => 'name DESC'), @user.cached_companies(:page => 1, :per_page => 3, :order => 'name DESC')
152
+ assert_equal @user.companies.paginate(:page => 1, :per_page => 3, :order => 'name ASC'), @user.cached_companies(:page => 1, :per_page => 3, :order => 'name ASC')
153
+ end
154
+
155
+ test "pagination should not result in an extra query" do
156
+ assert_queries(1) do
157
+ @user.cached_big_companies(:page => 1)
158
+ end
159
+ assert_queries(1) do
160
+ User.cached_companies(:page => 1)
161
+ end
162
+ end
163
+
164
+ test "should support a 'force' option" do
165
+ # ActiveRecord caches the result of the proc, so we need to
166
+ # use different instances of the user to test the force option.
167
+ uncached_user = User.first
168
+ companies = @user.companies
169
+ size = companies.size
170
+ assert_queries(1) do
171
+ assert_equal companies, @user.cached_companies
172
+ assert_equal size, @user.cached_companies_count
173
+ assert_equal size, uncached_user.cached_companies_count
174
+ end
175
+ assert_queries(2) do
176
+ assert_equal companies, uncached_user.cached_companies(:force => true)
177
+ assert_equal size, uncached_user.cached_companies_count(:force => true)
178
+ end
179
+ end
180
+
181
+ test "should handle various different model instances" do
182
+ one = User.first
183
+ two = User.first :offset => 1
184
+ assert_not_same one, two
185
+ assert_equal one.companies, one.cached_companies
186
+ assert_equal two.companies, two.cached_companies
187
+ end
188
+
189
+ test "should handle arrays of non-active record instances" do
190
+ assert_equal @user.pet_names, @user.cached_pet_names
191
+ assert_equal @user.pet_names, @user.cached_pet_names
192
+ assert_equal @user.pet_names.count, @user.cached_pet_names_count
193
+ end
194
+
195
+ test "should empty the Rails cache" do
196
+ @user.cached_companies
197
+ User.cached_companies
198
+ assert Rails.cache.exist?(@user.arid_cache_key('companies'))
199
+ assert Rails.cache.exist?(User.arid_cache_key('companies'))
200
+ User.clear_caches
201
+ assert !Rails.cache.exist?(@user.arid_cache_key('companies'))
202
+ assert !Rails.cache.exist?(User.arid_cache_key('companies'))
203
+ end
204
+
205
+ test "should support expiring caches" do
206
+ # The first query should put the count in the cache. The second query
207
+ # should read the count from the cache. The third query should
208
+ # reload the cache.
209
+ assert_queries(2) do
210
+ User.cached_companies_count(:expires_in => 1.second)
211
+ User.cached_companies_count(:expires_in => 1.second)
212
+ sleep(1)
213
+ User.cached_companies_count(:expires_in => 1.second)
214
+ end
215
+ end
216
+
217
+ test "should support an auto-expire option" do
218
+ assert_match %r[users/#{@user.id}-companies], @user.arid_cache_key('companies')
219
+ assert_equal @user.arid_cache_key('companies'), @user.arid_cache_key('companies', :auto_expire => false)
220
+ assert_match %r[users/#{@user.id}-\d{14}-companies], @user.arid_cache_key('companies', :auto_expire => true)
221
+
222
+ # It doesn't apply to class caches, but shouldn't affect it either
223
+ assert_equal User.arid_cache_key('companies'), User.arid_cache_key('companies', :auto_expire => true)
224
+ end
225
+
226
+ test "should turn off auto-expire by default" do
227
+ assert_queries(2) do
228
+ @user.cached_companies_count
229
+ @user.touch
230
+ @user.cached_companies_count
231
+ end
232
+ end
233
+
234
+ test "should reload auto-expired caches" do
235
+ assert_queries(2) do
236
+ @user.cached_companies_count(:auto_expire => true)
237
+ @user.cached_companies_count(:auto_expire => true)
238
+ @user.updated_at = Time.now + 1.seconds
239
+ @user.cached_companies_count(:auto_expire => true)
240
+ @user.cached_companies_count(:auto_expire => true)
241
+ end
242
+ end
243
+
244
+ test "should support configuring instance caches" do
245
+ User.instance_caches { best_companies { companies } }
246
+ assert_equal @user.companies, @user.cached_best_companies
247
+ end
248
+
249
+ test "instance caches should work on all instances of the class" do
250
+ User.instance_caches { best_companies { companies } }
251
+ assert_equal @user.cached_best_companies, User.first.cached_best_companies
252
+ end
253
+
254
+ test "should support configuring class caches" do
255
+ User.class_caches { successful_users { successful } }
256
+ assert_equal User.successful, User.cached_successful_users
257
+ end
258
+
259
+ test "should create valid store keys" do
260
+ assert_equal 'user-key', AridCache.store.send(:class_store_key, User, 'key')
261
+ assert_equal 'users-key', AridCache.store.send(:instance_store_key, User, 'key')
262
+ assert_equal AridCache.store.send(:instance_store_key, User, 'key'), AridCache.store.send(:object_store_key, @user, 'key')
263
+ assert_equal AridCache.store.send(:class_store_key, User, 'key'), AridCache.store.send(:object_store_key, User, 'key')
264
+ end
265
+
266
+ test "configuring caches should not perform any queries" do
267
+ User.instance_caches do
268
+ best_companies { companies }
269
+ end
270
+ User.class_caches do
271
+ best_companies(:order => 'name DESC') { companies.find(:all, :order => 'name DESC') }
272
+ end
273
+ end
274
+
275
+ test "should support options in the cache configuration" do
276
+ User.instance_caches(:auto_expire => true) do
277
+ expires_in = 1.second
278
+ best_companies(:expires_in => expires_in) { companies }
279
+ end
280
+ assert_queries(2) do
281
+ @user.cached_best_companies_count
282
+ @user.updated_at = Time.now + 1.seconds
283
+ @user.cached_best_companies_count
284
+ @user.cached_best_companies_count
285
+ end
286
+ User.class_caches do
287
+ most_successful(:order => 'name DESC') { successful.find(:all, :order => 'name DESC') }
288
+ end
289
+ # Call it twice to ensure that on the second call the order is applied when retrieving the
290
+ # records by id
291
+ assert_equal User.successful.find(:all, :order => 'name DESC'), User.cached_most_successful
292
+ assert_equal User.successful.find(:all, :order => 'name DESC'), User.cached_most_successful
293
+ end
294
+
295
+ test "should not raise an error if all cached ids cannot be found" do
296
+ @user.cached_companies
297
+ key = @user.arid_cache_key('companies')
298
+ cached_result = Rails.cache.read(key)
299
+ cached_result.ids.push(24342234, 243234132)
300
+ Rails.cache.write(key, cached_result)
301
+ assert_nothing_raised { @user.cached_companies }
302
+ assert_equal @user.cached_companies, @user.companies
303
+ end
304
+
305
+ test "should not raise an error if all cached ids cannot be found while paginating" do
306
+ @user.cached_companies
307
+ key = @user.arid_cache_key('companies')
308
+ cached_result = Rails.cache.read(key)
309
+ cached_result.ids.push(24342234, 243234132)
310
+ Rails.cache.write(key, cached_result)
311
+ assert_nothing_raised { @user.cached_companies(:page => 1, :order => 'name DESC') }
312
+ assert_equal @user.cached_companies(:page => 1, :order => 'name DESC'), @user.companies.paginate(:page => 1, :order => 'name DESC')
313
+ end
314
+
315
+ test "should handle empty collections" do
316
+ @user.cached_empty_collection { [] }
317
+ assert_nothing_raised { @user.cached_empty_collection }
318
+ assert_nothing_raised { @user.cached_empty_collection }
319
+ end
320
+
321
+ test "should return proper paginate results when we include order" do
322
+ total = @user.companies.count
323
+ assert_queries(2) do
324
+ @user.cached_companies
325
+ page = @user.cached_companies(:page => 2, :per_page => 3, :order => 'name desc') # call again to get cached results
326
+ assert_equal total, page.total_entries
327
+ assert_equal 2, page.current_page
328
+ assert_equal (total/3.0).ceil, page.total_pages
329
+ end
330
+ end
331
+
332
+ test "should support calling store methods directly with a block" do
333
+ assert_nothing_raised do
334
+ AridCache.cache.new(@user, 'my-fancy-key') do
335
+ companies
336
+ end.fetch
337
+ end
338
+ assert_equal @user.companies.all(:order => 'name DESC'), AridCache.cache.new(@user, 'my-fancy-key', :order => 'name DESC') { companies }.fetch
339
+ end
340
+
341
+ test "empty user relation should be empty" do
342
+ assert_equal [], @user.empty_user_relations
343
+ end
344
+
345
+ test "should not query the database for ids when the list is empty" do
346
+ assert_queries(1) do
347
+ @user.cached_empty_user_relations
348
+ result = @user.cached_empty_user_relations
349
+ assert_kind_of Array, result
350
+ end
351
+ end
352
+
353
+ test "should not query the database for ids when the list is empty and we are paginating" do
354
+ assert_queries(1) do
355
+ @user.cached_empty_user_relations(:page => 1)
356
+ result = @user.cached_empty_user_relations(:page => 1)
357
+ assert_kind_of WillPaginate::Collection, result
358
+ end
359
+ end
360
+
361
+ test "should not query the database for ids when the list is empty and we are applying limits etc" do
362
+ assert_queries(1) do
363
+ @user.cached_empty_user_relations(:limit => 10)
364
+ result = @user.cached_empty_user_relations(:limit => 10)
365
+ assert_kind_of Array, result
366
+ end
367
+ end
368
+
369
+ test "should identify the MySQL2 adapter as a MySQL database" do
370
+ original_adapter_name = ::ActiveRecord::Base.connection.adapter_name
371
+ ::ActiveRecord::Base.connection.instance_eval { def adapter_name; "Mysql2"; end }
372
+ assert_equal "Mysql2", ::ActiveRecord::Base.connection.adapter_name
373
+ assert_equal true, ::ActiveRecord::Base.is_mysql_adapter?
374
+
375
+ # Restore it otherwise the other tests break
376
+ ::ActiveRecord::Base.connection.instance_eval { def adapter_name; "SQLite3"; end }
377
+ assert_equal "SQLite3", ::ActiveRecord::Base.connection.adapter_name
378
+ ::ActiveRecord::Base.is_mysql_adapter = nil
379
+ assert_equal false, ::ActiveRecord::Base.is_mysql_adapter?
380
+ end
381
+
382
+ #
383
+ # Tests requiring manual verification by looking at the SQL logs.
384
+ # TODO move these to a separate class.
385
+ #
386
+
387
+ test "should preserve original ordering" do
388
+ # TODO. This one is hard to test because SQL usually keeps the same ordering
389
+ # and it's difficult to make it do otherwise. Best to just inspect the queries
390
+ # in the log.
391
+ @user.cached_companies
392
+ @user.cached_companies
393
+ end
394
+
395
+ test "should paginate collections in memory" do
396
+ # TODO. Tough to test because we can't just count queries
397
+ # have to look at the SQL in the logs for this one.
398
+ @user.cached_companies(:page => 2, :per_page => 3)
399
+ @user.cached_companies(:page => 1, :per_page => 3)
400
+ end
401
+
402
+ protected
403
+
404
+ def assert_queries(num = 1)
405
+ $query_count = 0
406
+ yield
407
+ ensure
408
+ assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
409
+ end
410
+
411
+ def assert_no_queries(&block)
412
+ assert_queries(0, &block)
413
+ end
414
+ end