last_mod_cache 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|