bulk_cache_fetcher 0.0.3 → 1.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2012f42799511be205c0a27036b7a447dbf56b3
4
- data.tar.gz: 54172b0aea06cbec9d7549bbe9963bb5ebcb7bea
3
+ metadata.gz: 164f9631e7c60c573ea490d677851e3ae2c1994a
4
+ data.tar.gz: a27506b07291839747eff96c8b6098c12eac5d8c
5
5
  SHA512:
6
- metadata.gz: ea8c5960bc5df03785da93819ba29fae48390ccdcc5c6fae2fb25bb67b8e0052229e38e67eb3be8444a508c05c2573648ac22531f9aa5bcf9034087418d26102
7
- data.tar.gz: 5142b31a0a0c5e8fab81ef56acabf45c35e44beade392b077950763e4abdff93ae521efcd070d39c7a36fd5727d5d95341629f8411c26623b9d784d185d3fc72
6
+ metadata.gz: e24adcc2d3df76a46634720f77cbcf9d22fa5e66120a996bd42999ec7dfd3375649d7d17be8d28b1b4b17dbae300a44890cfb50e19a8d3b6fa144b56695b97b2
7
+ data.tar.gz: 84a0977c2db53ad7dec3d13fc446094f775faff28e6c8191d51cbdd3cc63d2d023d87118dd34dd2e7a5653f48abb9f6b57baddb1ca347926c8188444d85a034a
data/README.md CHANGED
@@ -14,6 +14,23 @@ Bulk Cache Fetcher allows you to query the cache for a list of
14
14
  objects, and gives you the opportunity to fetch all of the cache
15
15
  misses at once, using whatever `:include`s you want.
16
16
 
17
+ For example, if you have a list of objects by id to fetch, and a list
18
+ of keys you want them to be cached under, you'd use Bulk Cache Fetcher
19
+ like this:
20
+
21
+ ```ruby
22
+ identifiers = {:cache_key_1 => 1, :cache_key_2 => 2, :cache_key_3 => 3}
23
+ BulkCacheFetcher.new(Rails.cache).fetch(identifiers) do |uncached_keys_and_ids|
24
+ ids = uncached_keys_and_ids.values
25
+ BlogPost.where(:id => ids).includes([:author, :comments])
26
+ end
27
+ ```
28
+
29
+ This will include and cache each `BlogPost`, with comments and
30
+ authors, as if you did the `.where.includes` without caching. If a
31
+ `BlogPost` is cached already, it won't fetch it (or its includes) from
32
+ the database.
33
+
17
34
  ## Installation
18
35
 
19
36
  Add this line to your application's Gemfile:
@@ -5,7 +5,7 @@
5
5
  # Rails' nested caching while avoiding the n+1 queries problem in the
6
6
  # uncached case.
7
7
  class BulkCacheFetcher
8
- VERSION = '0.0.3'
8
+ VERSION = '1.0.0'
9
9
 
10
10
  # Creates a new bulk cache fetcher, backed by +cache+. Cache must
11
11
  # respond to the standard Rails cache API, described on
@@ -28,41 +28,38 @@ class BulkCacheFetcher
28
28
  # objects, so you can use it for things like setting cache
29
29
  # expiration.
30
30
  def fetch(object_identifiers, options = {}, &finder_block)
31
- cached_objects, uncached_identifiers = partition(object_identifiers)
32
- coalesce(cached_objects, find(uncached_identifiers, options, &finder_block))
31
+ object_identifiers = normalize(object_identifiers)
32
+ cached_keys_with_objects, uncached_identifiers = partition(object_identifiers)
33
+ found_objects = find(uncached_identifiers, options, &finder_block)
34
+ coalesce(cache_keys(object_identifiers), cached_keys_with_objects, found_objects)
33
35
  end
34
36
 
35
37
  private
36
38
 
37
- # Partitions a list of identifiers into two lists. The first list
38
- # contains all of the objects we were able to serve from the cache,
39
- # while the second is a list of all of the identifiers for objects
40
- # that weren't cached.
39
+ # Splits a list of identifiers into two objects. The first is a hash
40
+ # of <tt>{cache_key: object}</tt> for all the objects we were able to serve
41
+ # from the cache. The second is a list of all of the identifiers for
42
+ # objects that weren't cached.
41
43
  def partition(object_identifiers)
42
- object_identifiers = normalize(object_identifiers)
43
- cached_objects = []
44
44
  uncached_identifiers = object_identifiers.dup
45
45
 
46
46
  cache_keys = cache_keys(object_identifiers)
47
- found_objects = @cache.read_multi(*cache_keys)
47
+ cached_keys_with_objects = @cache.read_multi(*cache_keys)
48
48
 
49
49
  cache_keys.each do |cache_key|
50
- cached_object = found_objects[cache_key]
51
- uncached_identifiers.delete(cache_key) if cached_object
52
- cached_objects << cached_object
50
+ uncached_identifiers.delete(cache_key) if cached_keys_with_objects.has_key?(cache_key)
53
51
  end
54
52
 
55
- [cached_objects, uncached_identifiers]
53
+ [cached_keys_with_objects, uncached_identifiers]
56
54
  end
57
55
 
58
56
  # Finds all of the objects identified by +identifiers+, using the
59
57
  # +finder_block+. Will pass +options+ on to the cache.
60
58
  def find(identifiers, options = {}, &finder_block)
61
- unless identifiers.empty?
62
- Array(finder_block.(identifiers)).tap do |objects|
63
- verify_equal_key_and_value_counts!(identifiers, objects)
64
- cache_all(identifiers, objects, options)
65
- end
59
+ return [] if identifiers.empty?
60
+ Array(finder_block.call(identifiers)).tap do |objects|
61
+ verify_equal_key_and_value_counts!(identifiers, objects)
62
+ cache_all(identifiers, objects, options)
66
63
  end
67
64
  end
68
65
 
@@ -78,11 +75,12 @@ class BulkCacheFetcher
78
75
  keys.zip(values) { |k, v| @cache.write(cache_key(k), v, options) }
79
76
  end
80
77
 
81
- # With an array looking like <tt>[nil, 1, nil, 2]</tt>, replaces the
82
- # nils with values taken from +found_objects+, in order.
83
- def coalesce(array_with_nils, found_objects)
78
+ # Given a list of +cache_keys+, either find associated objects from
79
+ # +cached_keys_with_objects, or grab them from +found_objects+, in
80
+ # order.
81
+ def coalesce(cache_keys, cached_keys_with_objects, found_objects)
84
82
  found_objects = Array(found_objects)
85
- array_with_nils.map { |obj| obj ? obj : found_objects.shift }
83
+ cache_keys.map { |key| cached_keys_with_objects.fetch(key) { found_objects.shift } }
86
84
  end
87
85
 
88
86
  # Returns the part of the identifier that we can use as the cache
@@ -13,14 +13,14 @@ class InMemoryCache
13
13
  def read_multi(*keys)
14
14
  results = {}
15
15
  keys.each do |key|
16
- results[key] = read(key) if read(key)
16
+ results[key] = read(key) if cache.has_key?(key)
17
17
  end
18
18
  results
19
19
  end
20
20
 
21
21
  end
22
22
 
23
- class BulkCacheFetcherTest < Minitest::Unit::TestCase
23
+ class BulkCacheFetcherTest < Minitest::Test
24
24
 
25
25
  def setup
26
26
  @cache = InMemoryCache.new
@@ -126,4 +126,13 @@ class BulkCacheFetcherTest < Minitest::Unit::TestCase
126
126
  end
127
127
  assert_equal(2, @cache.read(:one))
128
128
  end
129
+
130
+ def test_doesnt_break_if_nils_are_cached
131
+ @cache.write(:two, nil)
132
+ results = @cache_fetcher.fetch([:one, :two, :three]) do |keys|
133
+ [1, 3]
134
+ end
135
+ assert_equal(nil, results[1])
136
+ assert_equal(3, results[2])
137
+ end
129
138
  end
@@ -1,2 +1 @@
1
- require 'minitest/unit'
2
1
  require 'minitest/autorun'
metadata CHANGED
@@ -1,55 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bulk_cache_fetcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Weiss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-04 00:00:00.000000000 Z
11
+ date: 2015-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.3'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  description: Fetches cache misses in bulk
@@ -59,8 +59,8 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
- - .gitignore
63
- - .travis.yml
62
+ - ".gitignore"
63
+ - ".travis.yml"
64
64
  - Gemfile
65
65
  - LICENSE.txt
66
66
  - README.md
@@ -79,17 +79,17 @@ require_paths:
79
79
  - lib
80
80
  required_ruby_version: !ruby/object:Gem::Requirement
81
81
  requirements:
82
- - - '>='
82
+ - - ">="
83
83
  - !ruby/object:Gem::Version
84
84
  version: '0'
85
85
  required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  requirements: []
91
91
  rubyforge_project:
92
- rubygems_version: 2.1.5
92
+ rubygems_version: 2.4.5
93
93
  signing_key:
94
94
  specification_version: 4
95
95
  summary: Bulk Cache Fetcher allows you to query the cache for a list of objects, and