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 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