onyx-cache-money 0.2.5.7 → 0.2.5.8

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cash/accessor.rb CHANGED
@@ -11,15 +11,11 @@ module Cash
11
11
  def fetch(keys, options = {}, &block)
12
12
  case keys
13
13
  when Array
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)
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)
23
19
  end
24
20
  hits
25
21
  else
@@ -30,11 +26,7 @@ module Cash
30
26
  def get(keys, options = {}, &block)
31
27
  case keys
32
28
  when Array
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
29
+ fetch(keys, options, &block)
38
30
  else
39
31
  fetch(keys, options) do
40
32
  if block_given?
@@ -74,8 +66,7 @@ module Cash
74
66
  end
75
67
 
76
68
  def cache_key(key)
77
- ready = key =~ /#{name}:#{cache_config.version}/
78
- ready ? key : "#{name}:#{cache_config.version}/#{key.to_s.gsub(' ', '+')}"
69
+ "#{name}:#{cache_config.version}/#{key.to_s.gsub(' ', '+')}"
79
70
  end
80
71
  end
81
72
 
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,8 +1,7 @@
1
1
  module Cash
2
2
  module Query
3
3
  class Abstract
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
4
+ delegate :with_exclusive_scope, :get, :table_name, :indices, :find_from_ids_without_cache, :cache_key, :columns_hash, :to => :@active_record
6
5
 
7
6
  def self.perform(*args)
8
7
  new(*args).perform
@@ -52,7 +51,7 @@ module Cash
52
51
  def cacheable?(*optionss)
53
52
  optionss.each { |options| return unless safe_options_for_cache?(options) }
54
53
  partial_indices = optionss.collect { |options| attribute_value_pairs_for_conditions(options[:conditions]) }
55
- return if partial_indices.flatten.include?(nil)
54
+ return if partial_indices.include?(nil)
56
55
  attribute_value_pairs = partial_indices.sum.sort { |x, y| x[0] <=> y[0] }
57
56
  if index = indexed_on?(attribute_value_pairs.collect { |pair| pair[0] })
58
57
  if index.matches?(self)
@@ -71,20 +70,7 @@ module Cash
71
70
  end
72
71
 
73
72
  def cache_keys(attribute_value_pairs)
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
73
+ attribute_value_pairs.flatten.join('/')
88
74
  end
89
75
 
90
76
  def safe_options_for_cache?(options)
@@ -95,10 +81,13 @@ module Cash
95
81
  def attribute_value_pairs_for_conditions(conditions)
96
82
  case conditions
97
83
  when Hash
84
+ # avoid key too long error when passing in array of ids
85
+ return nil if conditions.values.any? {|value| value.is_a?(Array)}
98
86
  conditions.to_a.collect { |key, value| [key.to_s, value] }
99
87
  when String
100
88
  parse_indices_from_condition(conditions)
101
89
  when Array
90
+ # do not cache find(:conditions => ["... :attr", {:attr => 1}]
102
91
  return nil if conditions.last.is_a?(Hash)
103
92
  parse_indices_from_condition(*conditions)
104
93
  when NilClass
@@ -108,19 +97,17 @@ module Cash
108
97
 
109
98
  AND = /\s+AND\s+/i
110
99
  TABLE_AND_COLUMN = /(?:(?:`|")?(\w+)(?:`|")?\.)?(?:`|")?(\w+)(?:`|")?/ # Matches: `users`.id, `users`.`id`, users.id, id
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
100
+ VALUE = /'?(\d+|\?|(?:(?:[^']|'')*))'?/ # Matches: 123, ?, '123', '12''3'
101
+ KEY_EQ_VALUE = /^\(?#{TABLE_AND_COLUMN}\s+=\s+#{VALUE}\)?$/ # Matches: KEY = VALUE, (KEY = VALUE)
102
+ ORDER = /^#{TABLE_AND_COLUMN}\s*(ASC|DESC)?$/i # Matches: COLUMN ASC, COLUMN DESC, COLUMN
114
103
 
115
104
  def parse_indices_from_condition(conditions = '', *values)
116
105
  values = values.dup
117
106
  conditions.split(AND).inject([]) do |indices, condition|
118
- matched, table_name, column_name, sql_values = *(KEY_EQ_VALUE.match(condition))
107
+ matched, table_name, column_name, sql_value = *(KEY_EQ_VALUE.match(condition))
119
108
  if matched
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]
109
+ value = sql_value == '?' ? values.shift : columns_hash[column_name].type_cast(sql_value)
110
+ indices << [column_name, value]
124
111
  else
125
112
  return nil
126
113
  end
@@ -165,30 +152,14 @@ module Cash
165
152
  objects
166
153
  else
167
154
  cache_keys = objects.collect { |id| "id/#{id}" }
168
- objects = get(cache_keys) {|missed_keys| find_from_keys(missed_keys)}
155
+ objects = get(cache_keys, &method(:find_from_keys))
169
156
  convert_to_array(cache_keys, objects)
170
157
  end
171
158
  end
172
159
 
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 ")))
160
+ def find_from_keys(*missing_keys)
161
+ missing_ids = Array(missing_keys).flatten.collect { |key| key.split('/')[2].to_i }
162
+ find_from_ids_without_cache(missing_ids, {})
192
163
  end
193
164
  end
194
165
  end
@@ -4,9 +4,8 @@ module Cash
4
4
  delegate :find_every_without_cache, :to => :@active_record
5
5
 
6
6
  protected
7
- def miss(missed_keys, miss_options)
8
- # find_every_without_cache(miss_options)
9
- misses = find_from_keys(missed_keys, miss_options)
7
+ def miss(_, miss_options)
8
+ find_every_without_cache(miss_options)
10
9
  end
11
10
 
12
11
  def uncacheable
@@ -170,9 +170,8 @@ module Cash
170
170
 
171
171
  describe '#find(1, :conditions => ...)' do
172
172
  it "does not use the database" do
173
- Story.create!
174
173
  story = Story.create!
175
- character = Character.create!(:name => name = 'barbara', :story_id => story.id)
174
+ character = Character.create!(:name => name = 'barbara', :story_id => story)
176
175
  mock(Character.connection).execute.never
177
176
  Character.send :with_scope, :find => { :conditions => { :story_id => story.id } } do
178
177
  Character.find(character.id, :conditions => { :name => name }).should == character
@@ -212,6 +211,19 @@ module Cash
212
211
  end
213
212
  end
214
213
 
214
+ describe "#find_all_by_id" do
215
+ it "should not create a key over 250 characters" do
216
+ 150.times do
217
+ Story.create!
218
+ end
219
+ ids = Story.find(:all).map(&:id)
220
+ $memcache.flush_all
221
+ lambda do
222
+ Story.find_all_by_id(ids)
223
+ end.should_not raise_error(ArgumentError)
224
+ end
225
+ end
226
+
215
227
  describe '#find(:all)' do
216
228
  it "uses the database, not the cache" do
217
229
  character = Character.create!
@@ -228,6 +240,28 @@ module Cash
228
240
  Story.find(:all, :conditions => { :title => story1.title }).should == [story1, story2]
229
241
  end
230
242
  end
243
+
244
+ it "should not create a key over 250 characters with hash for conditions" do
245
+ 150.times do
246
+ Story.create!
247
+ end
248
+ ids = Story.find(:all).map(&:id)
249
+ $memcache.flush_all
250
+ lambda do
251
+ Story.find(:all, :conditions => {:id => ids})
252
+ end.should_not raise_error(ArgumentError)
253
+ end
254
+
255
+ it "should not create a key over 250 characters with array for conditions" do
256
+ 150.times do
257
+ Story.create!
258
+ end
259
+ ids = Story.find(:all).map(&:id)
260
+ $memcache.flush_all
261
+ lambda do
262
+ Story.find(:all, :conditions => ["id IN (?)", ids])
263
+ end.should_not raise_error(ArgumentError)
264
+ end
231
265
  end
232
266
 
233
267
  describe '#find(:all, :limit => ..., :offset => ...)' do
@@ -262,7 +296,7 @@ module Cash
262
296
  end
263
297
  end
264
298
 
265
- describe '#find_by_attr' do
299
+ describe '#find_by_attr' do
266
300
  describe 'on indexed attributes' do
267
301
  describe '#find_by_id(id)' do
268
302
  it "does not use the database" do
@@ -335,102 +369,27 @@ module Cash
335
369
  Story.fetch("title/#{@story.title}").should == [@story.id]
336
370
  end
337
371
  end
338
-
372
+
339
373
  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]
374
+ it 'never retrieves from cache' do
375
+ mock(Story).add.never
376
+ Story.find(:all, :conditions => ["id = :id", {:id => @story.id}])
377
+ end
378
+
379
+ it 'populates the cache' do
380
+ pending "remove test above when this is fixed"
381
+ Story.find(:all, :conditions => ["id = :id", {:id => @story.id}])
382
+ Story.fetch("id/#{@story.id}").should == @story
342
383
  end
343
384
  end
344
-
385
+
345
386
  describe '#find(1)' do
346
387
  it 'populates the cache' do
347
388
  Story.find(@story.id)
348
389
  Story.fetch("id/#{@story.id}").should == [@story]
349
390
  end
350
391
  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
392
 
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
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
-
434
393
  describe 'when there is a with_scope' do
435
394
  it "uses the database, not the cache" do
436
395
  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.7
4
+ version: 0.2.5.8
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: 2009-05-26 00:00:00 -07:00
12
+ date: 2009-05-28 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15