onyx-cache-money 0.2.5.6 → 0.2.5.7

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cash/accessor.rb CHANGED
@@ -11,11 +11,15 @@ module Cash
11
11
  def fetch(keys, options = {}, &block)
12
12
  case keys
13
13
  when Array
14
- keys = keys.collect { |key| cache_key(key) }
15
- hits = repository.get_multi(keys)
16
- if (missed_keys = keys - hits.keys).any?
17
- missed_values = block.call(missed_keys)
18
- hits.merge!(missed_keys.zip(Array(missed_values)).to_hash)
14
+ cache_and_actual_keys = keys.inject({}) { |memo, key| memo[cache_key(key)] = key; memo }
15
+ cache_keys = keys.collect {|key| cache_key(key)}
16
+
17
+ hits = repository.get_multi(cache_keys)
18
+ if (missed_cache_keys = cache_keys - hits.keys).any?
19
+ actual_missed_keys = missed_cache_keys.collect {|missed_cache_key| cache_and_actual_keys[missed_cache_key]}
20
+ missed_values = block.call(actual_missed_keys)
21
+
22
+ hits.merge!(missed_cache_keys.zip(Array(missed_values)).to_hash)
19
23
  end
20
24
  hits
21
25
  else
@@ -26,7 +30,11 @@ module Cash
26
30
  def get(keys, options = {}, &block)
27
31
  case keys
28
32
  when Array
29
- fetch(keys, options, &block)
33
+ fetch(keys, options) do |missed_keys|
34
+ results = yield(missed_keys)
35
+ results.each_with_index {|result, index| add(missed_keys[index], result, options)}
36
+ results
37
+ end
30
38
  else
31
39
  fetch(keys, options) do
32
40
  if block_given?
@@ -66,7 +74,8 @@ module Cash
66
74
  end
67
75
 
68
76
  def cache_key(key)
69
- "#{name}:#{cache_config.version}/#{key.to_s.gsub(' ', '+')}"
77
+ ready = key =~ /#{name}:#{cache_config.version}/
78
+ ready ? key : "#{name}:#{cache_config.version}/#{key.to_s.gsub(' ', '+')}"
70
79
  end
71
80
  end
72
81
 
data/lib/cash/finders.rb CHANGED
@@ -10,7 +10,7 @@ module Cash
10
10
  def self.extended(active_record_class)
11
11
  class << active_record_class
12
12
  alias_method_chain :find_every, :cache
13
- alias_method_chain :find_from_ids, :cache
13
+ # alias_method_chain :find_from_ids, :cache
14
14
  alias_method_chain :calculate, :cache
15
15
  end
16
16
  end
@@ -25,9 +25,9 @@ module Cash
25
25
  end
26
26
 
27
27
  # User.find(1), User.find(1, 2, 3), User.find([1, 2, 3]), User.find([])
28
- def find_from_ids_with_cache(ids, options)
29
- Query::PrimaryKey.perform(self, ids, options, scope(:find))
30
- end
28
+ # def find_from_ids_with_cache(ids, options)
29
+ # Query::PrimaryKey.perform(self, ids, options, scope(:find))
30
+ # end
31
31
 
32
32
  # User.count(:all), User.count, User.sum(...)
33
33
  def calculate_with_cache(operation, column_name, options = {})
@@ -1,7 +1,8 @@
1
1
  module Cash
2
2
  module Query
3
3
  class Abstract
4
- delegate :with_exclusive_scope, :get, :table_name, :indices, :find_from_ids_without_cache, :cache_key, :columns_hash, :to => :@active_record
4
+ delegate :with_exclusive_scope, :get, :quoted_table_name, :connection, :indices,
5
+ :find_every_without_cache, :cache_key, :columns_hash, :quote_value, :to => :@active_record
5
6
 
6
7
  def self.perform(*args)
7
8
  new(*args).perform
@@ -51,7 +52,7 @@ module Cash
51
52
  def cacheable?(*optionss)
52
53
  optionss.each { |options| return unless safe_options_for_cache?(options) }
53
54
  partial_indices = optionss.collect { |options| attribute_value_pairs_for_conditions(options[:conditions]) }
54
- return if partial_indices.include?(nil)
55
+ return if partial_indices.flatten.include?(nil)
55
56
  attribute_value_pairs = partial_indices.sum.sort { |x, y| x[0] <=> y[0] }
56
57
  if index = indexed_on?(attribute_value_pairs.collect { |pair| pair[0] })
57
58
  if index.matches?(self)
@@ -70,7 +71,20 @@ module Cash
70
71
  end
71
72
 
72
73
  def cache_keys(attribute_value_pairs)
73
- attribute_value_pairs.flatten.join('/')
74
+ cache_keys = collect_cache_keys(attribute_value_pairs)
75
+ cache_keys.size == 1 ? cache_keys.first : cache_keys
76
+ end
77
+
78
+ def collect_cache_keys(pairs)
79
+ return [] if pairs.empty?
80
+ key, values = pairs.shift
81
+ Array(values).inject([]) do |memo,value|
82
+ partial_keys = collect_cache_keys(pairs.clone)
83
+
84
+ memo << "#{key}/#{value}" if partial_keys.empty?
85
+ partial_keys.each { |partial_key| memo << "#{key}/#{value}/#{partial_key}" }
86
+ memo
87
+ end
74
88
  end
75
89
 
76
90
  def safe_options_for_cache?(options)
@@ -85,6 +99,7 @@ module Cash
85
99
  when String
86
100
  parse_indices_from_condition(conditions)
87
101
  when Array
102
+ return nil if conditions.last.is_a?(Hash)
88
103
  parse_indices_from_condition(*conditions)
89
104
  when NilClass
90
105
  []
@@ -93,17 +108,19 @@ module Cash
93
108
 
94
109
  AND = /\s+AND\s+/i
95
110
  TABLE_AND_COLUMN = /(?:(?:`|")?(\w+)(?:`|")?\.)?(?:`|")?(\w+)(?:`|")?/ # Matches: `users`.id, `users`.`id`, users.id, id
96
- VALUE = /'?(\d+|\?|(?:(?:[^']|'')*))'?/ # Matches: 123, ?, '123', '12''3'
97
- KEY_EQ_VALUE = /^\(?#{TABLE_AND_COLUMN}\s+=\s+#{VALUE}\)?$/ # Matches: KEY = VALUE, (KEY = VALUE)
98
- ORDER = /^#{TABLE_AND_COLUMN}\s*(ASC|DESC)?$/i # Matches: COLUMN ASC, COLUMN DESC, COLUMN
111
+ VALUE = /'?(\d+|\?|(?:(?:[^']|'')*?))'?/ # Matches: 123, ?, '123', '12 ''3'
112
+ KEY_EQ_VALUE = /^\(?#{TABLE_AND_COLUMN}\s+(?:=|IN)\s+\(?(?:#{VALUE})\)?\)?$/ # Matches: KEY = VALUE, (KEY = VALUE), KEY IN (VALUE,VALUE,..)
113
+ ORDER = /^#{TABLE_AND_COLUMN}\s*(ASC|DESC)?$/i # Matches: COLUMN ASC, COLUMN DESC, COLUMN
99
114
 
100
115
  def parse_indices_from_condition(conditions = '', *values)
101
116
  values = values.dup
102
117
  conditions.split(AND).inject([]) do |indices, condition|
103
- matched, table_name, column_name, sql_value = *(KEY_EQ_VALUE.match(condition))
118
+ matched, table_name, column_name, sql_values = *(KEY_EQ_VALUE.match(condition))
104
119
  if matched
105
- value = sql_value == '?' ? values.shift : columns_hash[column_name].type_cast(sql_value)
106
- indices << [column_name, value]
120
+ actual_values = sql_values.split(',').collect do |sql_value|
121
+ sql_value == '?' ? values.shift : columns_hash[column_name].type_cast(sql_value)
122
+ end
123
+ indices << [column_name, actual_values]
107
124
  else
108
125
  return nil
109
126
  end
@@ -148,14 +165,30 @@ module Cash
148
165
  objects
149
166
  else
150
167
  cache_keys = objects.collect { |id| "id/#{id}" }
151
- objects = get(cache_keys, &method(:find_from_keys))
168
+ objects = get(cache_keys) {|missed_keys| find_from_keys(missed_keys)}
152
169
  convert_to_array(cache_keys, objects)
153
170
  end
154
171
  end
155
172
 
156
- def find_from_keys(*missing_keys)
157
- missing_ids = Array(missing_keys).flatten.collect { |key| key.split('/')[2].to_i }
158
- find_from_ids_without_cache(missing_ids, {})
173
+ def find_from_keys(missing_keys, options = {})
174
+ missing_keys_pairs = Array(missing_keys).flatten.collect { |key| key.split('/') }
175
+
176
+ keys_values = missing_keys_pairs.inject({}) do |memo, missing_keys_pair|
177
+ while missing_keys_pair.any?
178
+ key = missing_keys_pair.shift
179
+ memo[key] ||= []
180
+ memo[key] << missing_keys_pair.shift
181
+ end
182
+ memo
183
+ end
184
+
185
+ conditions = keys_values.collect do |key,values|
186
+ converted_values = values.collect {|value| quote_value(value, columns_hash[key])}
187
+ quoted_table_and_column_name = "#{quoted_table_name}.#{connection.quote_column_name(key)}"
188
+ converted_values.size == 1 ? "#{quoted_table_and_column_name} = #{converted_values}" : "#{quoted_table_and_column_name} IN (#{converted_values.join(',')})"
189
+ end
190
+
191
+ find_every_without_cache(options.merge(:conditions => conditions.join(" AND ")))
159
192
  end
160
193
  end
161
194
  end
@@ -4,8 +4,9 @@ module Cash
4
4
  delegate :find_every_without_cache, :to => :@active_record
5
5
 
6
6
  protected
7
- def miss(_, miss_options)
8
- find_every_without_cache(miss_options)
7
+ def miss(missed_keys, miss_options)
8
+ # find_every_without_cache(miss_options)
9
+ misses = find_from_keys(missed_keys, miss_options)
9
10
  end
10
11
 
11
12
  def uncacheable
@@ -170,8 +170,9 @@ module Cash
170
170
 
171
171
  describe '#find(1, :conditions => ...)' do
172
172
  it "does not use the database" do
173
+ Story.create!
173
174
  story = Story.create!
174
- character = Character.create!(:name => name = 'barbara', :story_id => story)
175
+ character = Character.create!(:name => name = 'barbara', :story_id => story.id)
175
176
  mock(Character.connection).execute.never
176
177
  Character.send :with_scope, :find => { :conditions => { :story_id => story.id } } do
177
178
  Character.find(character.id, :conditions => { :name => name }).should == character
@@ -335,13 +336,101 @@ module Cash
335
336
  end
336
337
  end
337
338
 
339
+ describe '#find(:conditions => ["... :attr", {:attr => 1}])' do
340
+ it 'retrieves story from database - no support yet for retrieving from cache' do
341
+ Story.find(:all, :conditions => ["id = :id", {:id => @story.id}]).should == [@story]
342
+ end
343
+ end
344
+
338
345
  describe '#find(1)' do
339
346
  it 'populates the cache' do
340
347
  Story.find(@story.id)
341
348
  Story.fetch("id/#{@story.id}").should == [@story]
342
349
  end
343
350
  end
351
+
352
+ describe '#find(1,2)' do
353
+ it 'populates the cache' do
354
+ another_story = Story.create!
355
+ $memcache.flush_all
356
+
357
+ Story.find(@story.id, another_story.id).should == [@story, another_story]
358
+ Story.fetch("id/#{@story.id}").should == @story
359
+ Story.fetch("id/#{another_story.id}").should == another_story
360
+ end
361
+
362
+ it "populates the cache and retrieves from the cache" do
363
+ story1 = Story.create!
364
+ story2 = Story.create!
365
+ story3 = Story.create!
366
+ $memcache.flush_all
367
+
368
+ Story.find(story1.id, story2.id, story3.id).should == [story1, story2, story3]
369
+ mock(Story.connection).execute.never
370
+ Story.find(story1.id, story2.id).should == [story1, story2]
371
+ end
372
+
373
+ it "uses database for missing keys" do
374
+ story1 = Story.create!
375
+ story2 = Story.create!
376
+ story3 = Story.create!
377
+ $memcache.flush_all
378
+
379
+ Story.find(story1.id, story3.id).should == [story1, story3]
380
+ Story.fetch("id/#{story2.id}").should be_nil
381
+
382
+ mock(Story).find_every_without_cache(:limit => nil, :conditions => "\"stories\".\"id\" = #{story2.id}") do
383
+ story2
384
+ end
385
+ Story.find(story1.id, story2.id)
386
+ end
387
+
388
+ it "populates and retrieves from cache when passing in a hash" do
389
+ story1 = Story.create!
390
+ story2 = Story.create!
391
+ $memcache.flush_all
392
+
393
+ Story.find(:all, :conditions => {:id => [story1.id, story2.id]})
394
+ mock(Story.connection).execute.never
395
+ Story.find(story1.id, story2.id)
396
+ end
344
397
 
398
+ it "populates and retrieves from cache when passing in a parametrized conditions" do
399
+ story1 = Story.create!
400
+ story2 = Story.create!
401
+ $memcache.flush_all
402
+
403
+ Story.find(:all, :conditions => ["id IN (?,?)", story1.id, story2.id])
404
+ mock(Story.connection).execute.never
405
+ Story.find(story1.id, story2.id)
406
+ end
407
+
408
+ it "populates and retrieves from cache when passing in a list of non-numeric keys" do
409
+ pending "need to fix regex to properly parse this"
410
+ story1 = Story.create! :title => 'one'
411
+ story2 = Story.create! :title => 'two'
412
+ $memcache.flush_all
413
+
414
+ Story.find(:all, :conditions => ["title IN ('one', 'two')"])
415
+ # We need to add this extra call as cache money will always go to the database
416
+ # one more time when finding by a non-id field first
417
+ Story.find_all_by_title(['one', 'two'])
418
+ mock(Story.connection).execute.never
419
+ Story.find(story1.id, story2.id)
420
+ end
421
+
422
+ it "should not create a key over 250 characters on a find_all_by_ids" do
423
+ 75.times do
424
+ Story.create!
425
+ end
426
+ ids = Story.find(:all).map(&:id)
427
+ $memcache.flush_all
428
+ lambda do
429
+ Story.find(:all, :conditions => {:id => ids})
430
+ end.should_not raise_error(ArgumentError)
431
+ end
432
+ end
433
+
345
434
  describe 'when there is a with_scope' do
346
435
  it "uses the database, not the cache" do
347
436
  Story.send :with_scope, :find => { :conditions => { :title => @story.title }} do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onyx-cache-money
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5.6
4
+ version: 0.2.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Kallen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-11-24 00:00:00 -08:00
12
+ date: 2009-05-26 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15