djmaze-arid_cache 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +26 -0
- data/Gemfile +18 -0
- data/LICENSE +20 -0
- data/README.rdoc +394 -0
- data/Rakefile +78 -0
- data/VERSION +1 -0
- data/arid_cache.gemspec +104 -0
- data/init.rb +6 -0
- data/lib/arid_cache/active_record.rb +95 -0
- data/lib/arid_cache/cache_proxy.rb +368 -0
- data/lib/arid_cache/helpers.rb +86 -0
- data/lib/arid_cache/store.rb +125 -0
- data/lib/arid_cache.rb +47 -0
- data/rails/init.rb +1 -0
- data/spec/arid_cache/arid_cache_spec.rb +39 -0
- data/spec/arid_cache/cache_proxy_result_spec.rb +53 -0
- data/spec/arid_cache/cache_proxy_spec.rb +95 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +29 -0
- 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 +414 -0
- data/test/console +15 -0
- data/test/lib/add_query_counting_to_active_record.rb +11 -0
- data/test/lib/blueprint.rb +29 -0
- data/test/lib/db_prepare.rb +34 -0
- data/test/lib/fix_active_support_file_store_expires_in.rb +18 -0
- data/test/lib/mock_rails.rb +29 -0
- data/test/lib/models/company.rb +6 -0
- data/test/lib/models/empty_user_relation.rb +5 -0
- data/test/lib/models/user.rb +32 -0
- data/test/log/.gitignore +0 -0
- data/test/test_helper.rb +19 -0
- metadata +177 -0
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -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,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
|