onyx-cache-money 0.2.5.7 → 0.2.5.8

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