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 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.0
1
+ 1.0.1
@@ -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 = nil
33
- if respond_to?(:bind_values)
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 => {:id => id})).first if id }
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 => {:id => id_or_ids}, :cache => cache_options), &finder)
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 => {:id => id_or_ids}, :cache => cache_options), &finder)
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(conditions)
172
+ def id_and_updated_at(options)
150
173
  column = columns_hash[updated_at_column.to_s]
151
- 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)}"
152
- sql << " WHERE #{sanitize_sql_for_conditions(conditions)}" if conditions
153
- result = connection.select_one(sql)
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
@@ -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 => {:id => model_one_record_two.id}, :updated_at => model_one_record_two.updated_at.to_f}
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 => {:id => model_one_record_two.id}, :updated_at => model_one_record_two.updated_at.to_f}
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 => {:id => model_one_record_two.id}, :updated_at => model_one_record_two.updated_at.to_f}
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 => {:id => [model_one_record_one.id, model_one_record_two.id]}, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3}
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 => {:id => [model_one_record_one.id, model_one_record_two.id]}, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3}
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 => {:id => [model_one_record_one.id, model_one_record_two.id]}, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3}
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 => {:id => [model_one_record_one.id, model_one_record_three.id]}, :updated_at => LastModCache::Test::ModelOne.maximum(:updated_at).to_f, :row_count => 3}
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 => {:id => model_two_record_two.id}, :updated_at => model_one_record_two.updated_at.to_f}
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 => nil}
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 "eager load associations" do
506
+ context "associations" do
485
507
  before :each do
486
- model_one_record_one.widget = LastModCache::Test::Widget.create!(:name => "widget_1")
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
- model_one_record_two
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 => {:id => model_one_record_one.id}, :include => includes, :updated_at => model_one_record_one.updated_at.to_f}
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 => nil}
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 => nil}
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 => nil}
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
- proxy.__id__.should_not == nil.object_id
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
- proxy = LastModCache::Proxy.new{ Object.new }
596
- proxy.object_id.should == proxy.object_id
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 id = ?", value, id]
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: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 0
10
- version: 1.0.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-05 00:00:00 -05:00
18
+ date: 2011-07-15 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency