Skip to content

Commit 36d41a1

Browse files
committed
Allow fetching multiple values from the cache at once
Add a simple API for fetching a list of entries from the cache, where any missing entries are computed by a supplied block.
1 parent 6023a50 commit 36d41a1

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

activesupport/CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1-
* No changes.
1+
* Add a `fetch_multi` method to the cache stores. The method provides
2+
an easy to use API for fetching multiple values from the cache.
3+
4+
Example:
5+
6+
# Calculating scores is expensive, so we only do it for posts
7+
# that have been updated. Cache keys are automatically extracted
8+
# from objects that define a #cache_key method.
9+
scores = Rails.cache.fetch_multi(*posts) do |post|
10+
calculate_score(post)
11+
end
12+
13+
*Daniel Schierbeck*
214

315
Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activesupport/CHANGELOG.md) for previous changes.

activesupport/lib/active_support/cache.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,34 @@ def read_multi(*names)
352352
results
353353
end
354354

355+
# Fetches data from the cache, using the given keys. If there is data in
356+
# the cache with the given keys, then that data is returned. Otherwise,
357+
# the supplied block is called for each key for which there was no data,
358+
# and the result will be written to the cache and returned.
359+
#
360+
# Options are passed to the underlying cache implementation.
361+
#
362+
# Returns an array with the data for each of the names. For example:
363+
#
364+
# cache.write("bim", "bam")
365+
# cache.fetch_multi("bim", "boom") {|key| key * 2 }
366+
# #=> ["bam", "boomboom"]
367+
#
368+
def fetch_multi(*names)
369+
options = names.extract_options!
370+
options = merged_options(options)
371+
372+
results = read_multi(*names, options)
373+
374+
names.map do |name|
375+
results.fetch(name) do
376+
value = yield name
377+
write(name, value, options)
378+
value
379+
end
380+
end
381+
end
382+
355383
# Writes the value to the cache, with the key.
356384
#
357385
# Options are passed to the underlying cache implementation.

activesupport/test/caching_test.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,26 @@ def test_read_multi_with_expires
257257
assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
258258
end
259259

260+
def test_fetch_multi
261+
@cache.write('foo', 'bar')
262+
@cache.write('fud', 'biz')
263+
264+
values = @cache.fetch_multi('foo', 'fu', 'fud') {|value| value * 2 }
265+
266+
assert_equal(["bar", "fufu", "biz"], values)
267+
assert_equal("fufu", @cache.read('fu'))
268+
end
269+
270+
def test_multi_with_objects
271+
foo = stub(:title => "FOO!", :cache_key => "foo")
272+
bar = stub(:cache_key => "bar")
273+
274+
@cache.write('bar', "BAM!")
275+
276+
values = @cache.fetch_multi(foo, bar) {|object| object.title }
277+
assert_equal(["FOO!", "BAM!"], values)
278+
end
279+
260280
def test_read_and_write_compressed_small_data
261281
@cache.write('foo', 'bar', :compress => true)
262282
assert_equal 'bar', @cache.read('foo')

0 commit comments

Comments
 (0)