last_mod_cache 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +9 -0
- data/VERSION +1 -1
- data/lib/last_mod_cache.rb +63 -24
- data/spec/last_mod_cache_spec.rb +82 -21
- data/spec/spec_helper.rb +6 -2
- metadata +4 -4
data/README.rdoc
CHANGED
|
@@ -38,6 +38,15 @@ Any model that includes the LastModCache module will have several "with_cache" m
|
|
|
38
38
|
# Dynamic finders are also available in caching versions
|
|
39
39
|
MyModel.find_by_name_with_cache("test")
|
|
40
40
|
MyModel.find_all_by_name_and_value_with_cache("test", 4)
|
|
41
|
+
|
|
42
|
+
# Associations can also be loaded from cache if the associated classes include LastModCache
|
|
43
|
+
class Widget < ActiveRecord::Base
|
|
44
|
+
include LastModCache
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
MyModel.belongs_to :widget
|
|
48
|
+
|
|
49
|
+
MyModel.first.widget_with_cache
|
|
41
50
|
|
|
42
51
|
== Configuring
|
|
43
52
|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.1
|
data/lib/last_mod_cache.rb
CHANGED
|
@@ -4,13 +4,18 @@ module LastModCache
|
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
|
|
6
6
|
DYNAMIC_FINDER_METHOD_PATTERN = /^find_(all_)?by_(.+)_with_cache$/
|
|
7
|
+
ASSOCIATION_WITH_CACHE_PATTERN = /^(.+)_with_cache$/
|
|
7
8
|
|
|
8
9
|
included do
|
|
9
10
|
class_eval do
|
|
10
11
|
class << self
|
|
12
|
+
# Alias class method_missing
|
|
11
13
|
alias_method_chain(:method_missing, :last_mod_cache)
|
|
12
14
|
end
|
|
13
15
|
end
|
|
16
|
+
# Alias instance method missing
|
|
17
|
+
alias_method_chain(:method_missing, :last_mod_cache)
|
|
18
|
+
|
|
14
19
|
class_attribute :last_mod_cache, :updated_at_column, :instance_reader => false, :instance_writer => false
|
|
15
20
|
self.last_mod_cache = Rails.cache if defined?(Rails)
|
|
16
21
|
self.updated_at_column = :updated_at
|
|
@@ -29,16 +34,33 @@ module LastModCache
|
|
|
29
34
|
# BlogPosts.where(:blog_id => my_blog.id).order("published_at DESC").limit(20).with_cache
|
|
30
35
|
def with_cache(cache_options = nil)
|
|
31
36
|
raise NotImplementedError.new("LastModCache is not available on #{klass}") unless klass.include?(LastModCache)
|
|
32
|
-
bind_variables =
|
|
33
|
-
|
|
34
|
-
bind_variables = bind_values.collect do |column, value|
|
|
35
|
-
column.type_cast(value)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
klass.all_with_cache(:sql => to_sql, :cache => cache_options, :bind_values => bind_variables) do
|
|
37
|
+
bind_variables = respond_to?(:bind_values) ? bind_values.collect{|column, value| column.type_cast(value)} : []
|
|
38
|
+
klass.all_with_cache(:sql => to_sql, :bind_values => bind_variables, :cache => cache_options) do
|
|
39
39
|
to_a
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
|
+
|
|
43
|
+
# Add +first_with_cache+ to the end of a relation chain to perform the find and store the results in cache.
|
|
44
|
+
# Options for cache storage can be set with the optional +cache_options+ parameter. This method is
|
|
45
|
+
# equivalent to calling +first+ on the relation so that no more relations can be chained after it is called.
|
|
46
|
+
#
|
|
47
|
+
# Example:
|
|
48
|
+
#
|
|
49
|
+
# Blog.where(:name => "My Blog").first_with_cache
|
|
50
|
+
def first_with_cache(cache_options = nil)
|
|
51
|
+
raise NotImplementedError.new("LastModCache is not available on #{klass}") unless klass.include?(LastModCache)
|
|
52
|
+
limited_scope = limit(1)
|
|
53
|
+
conn = klass.connection
|
|
54
|
+
key_scope = limited_scope.select(["#{conn.quote_table_name(klass.table_name)}.#{conn.quote_column_name(klass.primary_key)} AS #{conn.quote_column_name('id')}", "#{conn.quote_table_name(klass.table_name)}.#{conn.quote_column_name(klass.updated_at_column)} AS #{conn.quote_column_name('updated_at')}"])
|
|
55
|
+
bind_variables = limited_scope.respond_to?(:bind_values) ? limited_scope.bind_values.collect{|column, value| column.type_cast(value)} : []
|
|
56
|
+
Proxy.new do
|
|
57
|
+
id, timestamp = klass.send(:id_and_updated_at, :sql => key_scope.to_sql, :bind_values => bind_variables)
|
|
58
|
+
record = last_mod_cache.fetch(klass.send(:updated_at_cache_key, :first_with_cache, {:sql => limited_scope.to_sql, :bind_values => bind_values}, timestamp), cache_options) do
|
|
59
|
+
limited_scope.where(klass.primary_key => id).first if id
|
|
60
|
+
end
|
|
61
|
+
record.freeze if record
|
|
62
|
+
end
|
|
63
|
+
end
|
|
42
64
|
end
|
|
43
65
|
|
|
44
66
|
# Class methods mixed into an ActiveRecord model that includes LastModCache.
|
|
@@ -69,8 +91,8 @@ module LastModCache
|
|
|
69
91
|
cache_options, options = extract_cache_options(options)
|
|
70
92
|
conditions = options.delete(:conditions)
|
|
71
93
|
Proxy.new do
|
|
72
|
-
id, timestamp = id_and_updated_at(conditions)
|
|
73
|
-
block ||= lambda{ all(options.merge(:limit => 1, :conditions => {
|
|
94
|
+
id, timestamp = id_and_updated_at(:conditions => conditions)
|
|
95
|
+
block ||= lambda{ all(options.merge(:limit => 1, :conditions => {primary_key => id})).first if id }
|
|
74
96
|
record = last_mod_cache.fetch(updated_at_cache_key(:first_with_cache, options.merge(:conditions => conditions), timestamp), cache_options, &block)
|
|
75
97
|
record.freeze if record
|
|
76
98
|
end
|
|
@@ -86,9 +108,9 @@ module LastModCache
|
|
|
86
108
|
cache_options, options = extract_cache_options(options)
|
|
87
109
|
finder = lambda{ options.blank? ? find(id_or_ids) : find(id_or_ids, options) }
|
|
88
110
|
if id_or_ids.is_a?(Array)
|
|
89
|
-
all_with_cache(options.merge(:conditions => {
|
|
111
|
+
all_with_cache(options.merge(:conditions => {primary_key => id_or_ids}, :cache => cache_options), &finder)
|
|
90
112
|
else
|
|
91
|
-
first_with_cache(options.merge(:conditions => {
|
|
113
|
+
first_with_cache(options.merge(:conditions => {primary_key => id_or_ids}, :cache => cache_options), &finder)
|
|
92
114
|
end
|
|
93
115
|
end
|
|
94
116
|
|
|
@@ -128,6 +150,15 @@ module LastModCache
|
|
|
128
150
|
end
|
|
129
151
|
end
|
|
130
152
|
|
|
153
|
+
# Get the maximum value in the updated at column and the count of all records in the database.
|
|
154
|
+
# This information can be used to generate self-expiring cache keys for models that change infrequently.
|
|
155
|
+
def max_updated_at_and_count
|
|
156
|
+
result = connection.select_one("SELECT MAX(#{connection.quote_column_name(updated_at_column)}) AS #{connection.quote_column_name('updated_at')}, COUNT(*) AS #{connection.quote_column_name('row_size')} FROM #{connection.quote_table_name(table_name)}")
|
|
157
|
+
updated_at = result['updated_at']
|
|
158
|
+
updated_at = columns_hash[updated_at_column.to_s].type_cast(updated_at) if updated_at.is_a?(String)
|
|
159
|
+
[updated_at, result['row_size'].to_i]
|
|
160
|
+
end
|
|
161
|
+
|
|
131
162
|
private
|
|
132
163
|
|
|
133
164
|
# Construct a cache key based on a timestamp.
|
|
@@ -137,20 +168,16 @@ module LastModCache
|
|
|
137
168
|
key
|
|
138
169
|
end
|
|
139
170
|
|
|
140
|
-
# Get the maximum value in the updated at column and the count of all records in the database.
|
|
141
|
-
def max_updated_at_and_count
|
|
142
|
-
result = connection.select_one("SELECT MAX(#{connection.quote_column_name(updated_at_column)}) AS #{connection.quote_column_name('updated_at')}, COUNT(*) AS #{connection.quote_column_name('row_size')} FROM #{connection.quote_table_name(table_name)}")
|
|
143
|
-
updated_at = result['updated_at']
|
|
144
|
-
updated_at = columns_hash[updated_at_column.to_s].type_cast(updated_at) if updated_at.is_a?(String)
|
|
145
|
-
[updated_at, result['row_size'].to_i]
|
|
146
|
-
end
|
|
147
|
-
|
|
148
171
|
# Get the id and updated at value for the first row that matches the conditions.
|
|
149
|
-
def id_and_updated_at(
|
|
172
|
+
def id_and_updated_at(options)
|
|
150
173
|
column = columns_hash[updated_at_column.to_s]
|
|
151
|
-
sql =
|
|
152
|
-
sql
|
|
153
|
-
|
|
174
|
+
sql = options[:sql]
|
|
175
|
+
unless sql
|
|
176
|
+
sql = "SELECT #{connection.quote_column_name(primary_key)} AS #{connection.quote_column_name('id')}, #{connection.quote_column_name(updated_at_column)} AS #{connection.quote_column_name('updated_at')} FROM #{connection.quote_table_name(table_name)}"
|
|
177
|
+
sql << " WHERE #{sanitize_sql_for_conditions(options[:conditions])}" if options[:conditions]
|
|
178
|
+
end
|
|
179
|
+
# Support older versions for ActiveRecord that don't implement bind_values
|
|
180
|
+
result = (options[:bind_values].blank? ? connection.select_all(sql) : connection.select_all(sql, nil, options[:bind_values])).first
|
|
154
181
|
if result
|
|
155
182
|
updated_at = result['updated_at']
|
|
156
183
|
updated_at = columns_hash[updated_at_column.to_s].type_cast(updated_at) if updated_at.is_a?(String)
|
|
@@ -203,11 +230,23 @@ module LastModCache
|
|
|
203
230
|
sql = self.class.send(:sanitize_sql, ["UPDATE #{conn.quote_table_name(self.class.table_name)} SET #{conn.quote_column_name(col_name)} = ? WHERE #{conn.quote_column_name(self.class.primary_key)} = ?", timestamp, id])
|
|
204
231
|
conn.update(sql)
|
|
205
232
|
end
|
|
233
|
+
|
|
234
|
+
def method_missing_with_last_mod_cache(method, *args, &block) #:nodoc:
|
|
235
|
+
match = method.to_s.match(ASSOCIATION_WITH_CACHE_PATTERN)
|
|
236
|
+
association_name = match[1].to_sym if match
|
|
237
|
+
reflection = self.class.reflect_on_association(association_name) if association_name
|
|
238
|
+
if reflection && (reflection.macro == :belongs_to)
|
|
239
|
+
foreign_key = reflection.respond_to?(:foreign_key) ? reflection.foreign_key : reflection.primary_key_name
|
|
240
|
+
reflection.klass.first_with_cache(:conditions => {(reflection.options[:primary_key] || reflection.klass.primary_key) => self[foreign_key]})
|
|
241
|
+
else
|
|
242
|
+
method_missing_without_last_mod_cache(method, *args, &block)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
206
245
|
end
|
|
207
246
|
|
|
208
247
|
# Proxy class that sends all method calls to a block.
|
|
209
248
|
class Proxy #:nodoc:
|
|
210
|
-
required_methods = {"__send__" => true, "__id__" => true}
|
|
249
|
+
required_methods = {"__send__" => true, "__id__" => true, "object_id" => true}
|
|
211
250
|
instance_methods.each do |m|
|
|
212
251
|
undef_method(m) unless required_methods.include?(m.to_s)
|
|
213
252
|
end
|
data/spec/last_mod_cache_spec.rb
CHANGED
|
@@ -78,6 +78,28 @@ describe LastModCache do
|
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
context "cache key information" do
|
|
82
|
+
it "should get the maximum updated at timestamp and the count of all rows in a table" do
|
|
83
|
+
model_one_record_one
|
|
84
|
+
model_one_record_two
|
|
85
|
+
model_one_record_two.updated_at.should_not be_nil
|
|
86
|
+
LastModCache::Test::ModelOne.max_updated_at_and_count.should == [model_one_record_two.updated_at, 2]
|
|
87
|
+
|
|
88
|
+
model_one_record_three
|
|
89
|
+
LastModCache::Test::ModelOne.max_updated_at_and_count.should == [model_one_record_three.updated_at, 3]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should get the maximum updated at timestamp and the count of all rows in a table when using a custom timestamp column" do
|
|
93
|
+
model_four_record_one
|
|
94
|
+
model_four_record_two
|
|
95
|
+
model_four_record_two.last_modified.should_not be_nil
|
|
96
|
+
LastModCache::Test::ModelFour.max_updated_at_and_count.should == [model_four_record_two.last_modified, 2]
|
|
97
|
+
|
|
98
|
+
model_four_record_three
|
|
99
|
+
LastModCache::Test::ModelFour.max_updated_at_and_count.should == [model_four_record_three.last_modified, 3]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
81
103
|
context "find all" do
|
|
82
104
|
before :each do
|
|
83
105
|
model_one_record_one
|
|
@@ -280,20 +302,20 @@ describe LastModCache do
|
|
|
280
302
|
model_one_record_one
|
|
281
303
|
model_one_record_two
|
|
282
304
|
model_one_record_three
|
|
283
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :first_with_cache, :conditions => {
|
|
305
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :first_with_cache, :conditions => {"id" => model_one_record_two.id}, :updated_at => model_one_record_two.updated_at.to_f}
|
|
284
306
|
LastModCache::Test::ModelOne.find_with_cache(model_one_record_two.id).should == model_one_record_two
|
|
285
307
|
Rails.cache.read(cache_key).should == model_one_record_two
|
|
286
308
|
end
|
|
287
309
|
|
|
288
310
|
it "should find a single record by id from the cache" do
|
|
289
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :first_with_cache, :conditions => {
|
|
311
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :first_with_cache, :conditions => {"id" => model_one_record_two.id}, :updated_at => model_one_record_two.updated_at.to_f}
|
|
290
312
|
Rails.cache.write(cache_key, model_one_record_two)
|
|
291
313
|
LastModCache::Test::ModelOne.poke_column_value(model_one_record_two.id, :value, 0)
|
|
292
314
|
LastModCache::Test::ModelOne.find_with_cache(model_one_record_two.id).value.should == 2
|
|
293
315
|
end
|
|
294
316
|
|
|
295
317
|
it "should invalidate a single record by id cache entry when the record is modified" do
|
|
296
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :first_with_cache, :conditions => {
|
|
318
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :first_with_cache, :conditions => {"id" => model_one_record_two.id}, :updated_at => model_one_record_two.updated_at.to_f}
|
|
297
319
|
Rails.cache.write(cache_key, model_one_record_two)
|
|
298
320
|
model_one_record_two.update_attribute(:value, 0)
|
|
299
321
|
LastModCache::Test::ModelOne.find_with_cache(model_one_record_two.id).value.should == 0
|
|
@@ -303,27 +325,27 @@ describe LastModCache do
|
|
|
303
325
|
model_one_record_one
|
|
304
326
|
model_one_record_two
|
|
305
327
|
model_one_record_three
|
|
306
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :conditions => {
|
|
328
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :conditions => {"id" => [model_one_record_one.id, model_one_record_two.id]}, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3}
|
|
307
329
|
LastModCache::Test::ModelOne.find_with_cache([model_one_record_one.id, model_one_record_two.id]).should == [model_one_record_one, model_one_record_two]
|
|
308
330
|
Rails.cache.read(cache_key).should == [model_one_record_one, model_one_record_two]
|
|
309
331
|
end
|
|
310
332
|
|
|
311
333
|
it "should find multiple records by id from the cache" do
|
|
312
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :conditions => {
|
|
334
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :conditions => {"id" => [model_one_record_one.id, model_one_record_two.id]}, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3}
|
|
313
335
|
Rails.cache.write(cache_key, [model_one_record_one, model_one_record_two])
|
|
314
336
|
LastModCache::Test::ModelOne.poke_column_value(model_one_record_one.id, :value, 0)
|
|
315
337
|
LastModCache::Test::ModelOne.find_with_cache([model_one_record_one.id, model_one_record_two.id]).first.value.should == 1
|
|
316
338
|
end
|
|
317
339
|
|
|
318
340
|
it "should invalidate a multiple records by id cache entry when any record is modified" do
|
|
319
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :conditions => {
|
|
341
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :conditions => {"id" => [model_one_record_one.id, model_one_record_two.id]}, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3}
|
|
320
342
|
Rails.cache.write(cache_key, [model_one_record_one, model_one_record_two])
|
|
321
343
|
model_one_record_one.update_attribute(:value, 0)
|
|
322
344
|
LastModCache::Test::ModelOne.find_with_cache([model_one_record_one.id, model_one_record_two.id]).first.value.should == 0
|
|
323
345
|
end
|
|
324
346
|
|
|
325
347
|
it "should invalidate a multiple records by id cache entry when any record is deleted" do
|
|
326
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :conditions => {
|
|
348
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :conditions => {"id" => [model_one_record_one.id, model_one_record_three.id]}, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3}
|
|
327
349
|
Rails.cache.write(cache_key, [model_one_record_one, model_one_record_three])
|
|
328
350
|
|
|
329
351
|
LastModCache::Test::ModelOne.poke_column_value(model_one_record_one.id, :value, 0)
|
|
@@ -356,7 +378,7 @@ describe LastModCache do
|
|
|
356
378
|
model_one_record_two.reload
|
|
357
379
|
LastModCache::Test::ModelTwo.find_with_cache(model_two_record_two.id).value.should == 2
|
|
358
380
|
|
|
359
|
-
cache_key = {:class => "LastModCache::Test::ModelTwo", :method => :first_with_cache, :conditions => {
|
|
381
|
+
cache_key = {:class => "LastModCache::Test::ModelTwo", :method => :first_with_cache, :conditions => {"id" => model_two_record_two.id}, :updated_at => model_one_record_two.updated_at.to_f}
|
|
360
382
|
LastModCache::Test.cache.write(cache_key, model_two_record_two.dup)
|
|
361
383
|
LastModCache::Test::ModelTwo.poke_column_value(model_two_record_two.id, :value, 0)
|
|
362
384
|
LastModCache::Test::ModelTwo.find_with_cache(model_two_record_two.id).value.should == 2
|
|
@@ -451,7 +473,7 @@ describe LastModCache do
|
|
|
451
473
|
it "should cache the result of a relation chain" do
|
|
452
474
|
relation = LastModCache::Test::ModelOne.where(:name => ["one", "two"]).order("value DESC")
|
|
453
475
|
relation.with_cache.should == [model_one_record_two, model_one_record_one]
|
|
454
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3, :sql => relation.to_sql, :bind_values =>
|
|
476
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3, :sql => relation.to_sql, :bind_values => []}
|
|
455
477
|
Rails.cache.read(cache_key).should == [model_one_record_two, model_one_record_one]
|
|
456
478
|
Rails.cache.write(cache_key, [model_one_record_one, model_one_record_three])
|
|
457
479
|
relation.with_cache.should == [model_one_record_one, model_one_record_three]
|
|
@@ -481,19 +503,30 @@ describe LastModCache do
|
|
|
481
503
|
end
|
|
482
504
|
end
|
|
483
505
|
|
|
484
|
-
context "
|
|
506
|
+
context "associations" do
|
|
485
507
|
before :each do
|
|
486
|
-
|
|
508
|
+
model_one_record_two.things.create(:name => "thing_0")
|
|
509
|
+
LastModCache::Test::Widget.create!(:name => "widget_0")
|
|
510
|
+
model_one_record_one.widget = widget
|
|
511
|
+
LastModCache::Test::Widget.create!(:name => "widget_2")
|
|
487
512
|
model_one_record_one.things.create(:name => "thing_1")
|
|
488
513
|
model_one_record_one.things.create(:name => "thing_2")
|
|
489
514
|
model_one_record_one.save!
|
|
490
515
|
model_one_record_one.reload
|
|
491
|
-
|
|
492
|
-
model_one_record_three
|
|
516
|
+
model_one_record_three.things.create(:name => "thing_3")
|
|
493
517
|
end
|
|
494
518
|
|
|
519
|
+
let(:widget){ LastModCache::Test::Widget.create!(:name => "widget_1") }
|
|
495
520
|
let(:includes){ [:widget, {:things => :widget}] }
|
|
496
521
|
|
|
522
|
+
it "should cache belongs_to associations" do
|
|
523
|
+
model_one_record_one.widget_with_cache.name.should == "widget_1"
|
|
524
|
+
cache_key = {:class => "LastModCache::Test::Widget", :method => :first_with_cache, :updated_at => widget.updated_at.to_f, :conditions => {"id" => widget.id}}
|
|
525
|
+
Rails.cache.read(cache_key).name.should == "widget_1"
|
|
526
|
+
Rails.cache.write(cache_key, LastModCache::Test::Widget.new(:name => "widget_a"))
|
|
527
|
+
model_one_record_one.widget_with_cache.name.should == "widget_a"
|
|
528
|
+
end
|
|
529
|
+
|
|
497
530
|
it "should cache included associations when finding many records" do
|
|
498
531
|
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :conditions => {:name => "one"}, :include => includes, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3}
|
|
499
532
|
LastModCache::Test::ModelOne.all_with_cache(:conditions => {:name => "one"}, :include => includes).should == [model_one_record_one]
|
|
@@ -523,7 +556,7 @@ describe LastModCache do
|
|
|
523
556
|
end
|
|
524
557
|
|
|
525
558
|
it "should cache included associations when finding a record by id" do
|
|
526
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :first_with_cache, :conditions => {
|
|
559
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :first_with_cache, :conditions => {"id" => model_one_record_one.id}, :include => includes, :updated_at => model_one_record_one.updated_at.to_f}
|
|
527
560
|
LastModCache::Test::ModelOne.find_with_cache(model_one_record_one.id, :include => includes).should == model_one_record_one
|
|
528
561
|
cached = Rails.cache.read(cache_key)
|
|
529
562
|
if cached.respond_to?(:association)
|
|
@@ -539,7 +572,7 @@ describe LastModCache do
|
|
|
539
572
|
it "should cache included associations when finding with a Relation" do
|
|
540
573
|
relation = LastModCache::Test::ModelOne.where(:name => "one").includes(includes)
|
|
541
574
|
relation.with_cache.should == [model_one_record_one]
|
|
542
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3, :sql => relation.to_sql, :bind_values =>
|
|
575
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3, :sql => relation.to_sql, :bind_values => []}
|
|
543
576
|
cached = Rails.cache.read(cache_key).first
|
|
544
577
|
if cached.respond_to?(:association)
|
|
545
578
|
cached.association(:widget).loaded?.should == true
|
|
@@ -554,7 +587,7 @@ describe LastModCache do
|
|
|
554
587
|
it "should cache eager load associations when finding with a Relation" do
|
|
555
588
|
relation = LastModCache::Test::ModelOne.where(:name => "one").eager_load(includes)
|
|
556
589
|
relation.with_cache.should == [model_one_record_one]
|
|
557
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3, :sql => relation.to_sql, :bind_values =>
|
|
590
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3, :sql => relation.to_sql, :bind_values => []}
|
|
558
591
|
cached = Rails.cache.read(cache_key).first
|
|
559
592
|
if cached.respond_to?(:association)
|
|
560
593
|
cached.association(:widget).loaded?.should == true
|
|
@@ -569,7 +602,7 @@ describe LastModCache do
|
|
|
569
602
|
it "should cache preload associations when finding with a Relation" do
|
|
570
603
|
relation = LastModCache::Test::ModelOne.where(:name => "one").preload(includes)
|
|
571
604
|
relation.with_cache.should == [model_one_record_one]
|
|
572
|
-
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3, :sql => relation.to_sql, :bind_values =>
|
|
605
|
+
cache_key = {:class => "LastModCache::Test::ModelOne", :method => :all_with_cache, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3, :sql => relation.to_sql, :bind_values => []}
|
|
573
606
|
cached = Rails.cache.read(cache_key).first
|
|
574
607
|
if cached.respond_to?(:association)
|
|
575
608
|
cached.association(:widget).loaded?.should == true
|
|
@@ -586,14 +619,20 @@ describe LastModCache do
|
|
|
586
619
|
it "should proxy all methods except __id__" do
|
|
587
620
|
proxy = LastModCache::Proxy.new{ nil }
|
|
588
621
|
proxy.nil?.should == true
|
|
589
|
-
proxy.object_id.should == nil.object_id
|
|
590
622
|
proxy.send(:nil?).should == true
|
|
591
|
-
|
|
623
|
+
|
|
624
|
+
proxy = LastModCache::Proxy.new{ "abc" }
|
|
625
|
+
proxy.nil?.should == false
|
|
626
|
+
proxy.size.should == 3
|
|
592
627
|
end
|
|
593
628
|
|
|
594
629
|
it "should only evaluate the block once" do
|
|
595
|
-
|
|
596
|
-
|
|
630
|
+
i = 0
|
|
631
|
+
obj = Object.new
|
|
632
|
+
proxy = LastModCache::Proxy.new{ i += 1; obj }
|
|
633
|
+
proxy.should == obj
|
|
634
|
+
proxy.nil?
|
|
635
|
+
i.should == 1
|
|
597
636
|
end
|
|
598
637
|
|
|
599
638
|
it "should lazy evaluate the block" do
|
|
@@ -605,4 +644,26 @@ describe LastModCache do
|
|
|
605
644
|
lambda{ proxy.not_a_method }.should raise_error(NoMethodError)
|
|
606
645
|
end
|
|
607
646
|
end
|
|
647
|
+
|
|
648
|
+
context "SQL caching" do
|
|
649
|
+
before :each do
|
|
650
|
+
model_one_record_one
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
it "should cache sql used for finding one record" do
|
|
654
|
+
LastModCache::Test::ModelOne.connection.cache do
|
|
655
|
+
LastModCache::Test::ModelOne.find_by_name_with_cache("one").should == model_one_record_one
|
|
656
|
+
LastModCache::Test::ModelOne.connection.should_not_receive(:select)
|
|
657
|
+
LastModCache::Test::ModelOne.find_by_name_with_cache("one").should == model_one_record_one
|
|
658
|
+
end
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
it "should cache sql used for finding many record" do
|
|
662
|
+
LastModCache::Test::ModelOne.connection.cache do
|
|
663
|
+
LastModCache::Test::ModelOne.find_all_by_name_with_cache("one").should == [model_one_record_one]
|
|
664
|
+
LastModCache::Test::ModelOne.connection.should_not_receive(:select)
|
|
665
|
+
LastModCache::Test::ModelOne.find_all_by_name_with_cache("one").should == [model_one_record_one]
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
end
|
|
608
669
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -39,12 +39,14 @@ module LastModCache
|
|
|
39
39
|
|
|
40
40
|
module PokeRecordValue
|
|
41
41
|
def poke_column_value(id, column, value)
|
|
42
|
-
sql = ["UPDATE #{connection.quote_table_name(table_name)} SET #{connection.quote_column_name(column)} = ? WHERE
|
|
42
|
+
sql = ["UPDATE #{connection.quote_table_name(table_name)} SET #{connection.quote_column_name(column)} = ? WHERE #{connection.quote_column_name(primary_key)} = ?", value, id]
|
|
43
43
|
connection.update(sanitize_sql_array(sql))
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
class Thing < ActiveRecord::Base
|
|
48
|
+
include LastModCache
|
|
49
|
+
|
|
48
50
|
belongs_to :widget
|
|
49
51
|
|
|
50
52
|
class << self
|
|
@@ -60,6 +62,7 @@ module LastModCache
|
|
|
60
62
|
end
|
|
61
63
|
|
|
62
64
|
class Widget < ActiveRecord::Base
|
|
65
|
+
include LastModCache
|
|
63
66
|
class << self
|
|
64
67
|
def setup
|
|
65
68
|
connection.create_table(table_name) do |t|
|
|
@@ -116,12 +119,13 @@ module LastModCache
|
|
|
116
119
|
extend PokeRecordValue
|
|
117
120
|
include LastModCache
|
|
118
121
|
self.updated_at_column = :last_modified
|
|
122
|
+
self.primary_key = "identifier"
|
|
119
123
|
|
|
120
124
|
before_save{|r| r.last_modified = Time.now.to_f}
|
|
121
125
|
|
|
122
126
|
class << self
|
|
123
127
|
def setup
|
|
124
|
-
connection.create_table(table_name) do |t|
|
|
128
|
+
connection.create_table(table_name, :primary_key => :identifier) do |t|
|
|
125
129
|
t.string :name
|
|
126
130
|
t.integer :value
|
|
127
131
|
t.float :last_modified
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: last_mod_cache
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 21
|
|
5
5
|
prerelease:
|
|
6
6
|
segments:
|
|
7
7
|
- 1
|
|
8
8
|
- 0
|
|
9
|
-
-
|
|
10
|
-
version: 1.0.
|
|
9
|
+
- 1
|
|
10
|
+
version: 1.0.1
|
|
11
11
|
platform: ruby
|
|
12
12
|
authors:
|
|
13
13
|
- Brian Durand
|
|
@@ -15,7 +15,7 @@ autorequire:
|
|
|
15
15
|
bindir: bin
|
|
16
16
|
cert_chain: []
|
|
17
17
|
|
|
18
|
-
date: 2011-07-
|
|
18
|
+
date: 2011-07-15 00:00:00 -05:00
|
|
19
19
|
default_executable:
|
|
20
20
|
dependencies:
|
|
21
21
|
- !ruby/object:Gem::Dependency
|