cache-object 0.0.3 → 0.0.4

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: 1353358a8c97cf8dae4728bb24db9b369507b7ce
4
- data.tar.gz: 990bdcf05d78a4108349e951968f2000d036237c
3
+ metadata.gz: e8ed3e3acf8e4c9fe23e4a3d0f73b067e1570318
4
+ data.tar.gz: d7f3afcb8be6c0d3065c97e7a1e7f027c25621bf
5
5
  SHA512:
6
- metadata.gz: 990c15962eb6c8584c1be9472e568de3907c55cfe62f378312b4aba26b3558aaa04abfcc3b4ad7a263c18466974389772478849324ad42307b986935d974f9a2
7
- data.tar.gz: 4f987b2214a753daab449ff3572eaee7f61942ceeced694f7c7142d09c4cf868937c78ef3b055bd3c36ab5c990b251348dff26a1f905d0b8434e4005894724d7
6
+ metadata.gz: b77a6a97aa5e46e6d9d01e16f97fd1e9f724c7029fc21c230ffc622f7bb9b67545cb81407b7010063329f5a3f9b881d949aafc3f1aebdcb3b00ef867097fbdb1
7
+ data.tar.gz: a0418a92720e49b01648b7bc6730f6d56070d8919c5845e8e32292de0d51197405521053952382d5ccde59e7925382f1a75ef45decde9e96fc4736ba14a58fa0
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "rake"
25
25
 
26
26
  spec.add_development_dependency "rspec", ">= 3.0"
27
+ spec.add_development_dependency "rspec-collection_matchers", ">= 1.0"
27
28
  spec.add_development_dependency "guard"
28
29
  spec.add_development_dependency "guard-rspec"
29
30
  spec.add_development_dependency "sqlite3"
@@ -3,6 +3,7 @@ require 'cache/object/config'
3
3
  require 'cache/object/active_record'
4
4
  require 'cache/object/adapter'
5
5
  require 'cache/object/key_generator'
6
+ require 'cache/object/multi_get'
6
7
 
7
8
  module Cache
8
9
  module Object
@@ -22,10 +22,6 @@ module Cache
22
22
  end
23
23
 
24
24
  def load_from_cache(attributes)
25
- attributes.each_pair do |key, value|
26
- attributes[key] = value
27
- end
28
-
29
25
  @attributes = self.class.initialize_attributes(attributes)
30
26
  @relation = nil
31
27
 
@@ -55,7 +51,6 @@ module Cache
55
51
  module ClassMethods
56
52
  def _load(args)
57
53
  attributes = Marshal.load(args)
58
-
59
54
  object = allocate
60
55
  object.load_from_cache(attributes)
61
56
  object
@@ -73,6 +68,10 @@ module Cache
73
68
  end
74
69
  end
75
70
 
71
+ def fetch_all(ids)
72
+ Cache::Object::MultiGet.new(self).fetch_all(ids)
73
+ end
74
+
76
75
  def object_cache_on(*attrs)
77
76
  self._object_cache_attr_mappings << attrs
78
77
  define_singleton_method("find_by_#{attrs.join('_and_')}") do |*args|
@@ -4,7 +4,7 @@ module Cache
4
4
  attr_reader :store
5
5
 
6
6
  def initialize(store)
7
- raise "Cache Store is nil" unless store
7
+ raise "Cache Store is nil, please initialize" unless store
8
8
  @store = store
9
9
  end
10
10
 
@@ -32,6 +32,10 @@ module Cache
32
32
  &block)
33
33
  end
34
34
 
35
+ def read_multi(args)
36
+ store.read_multi(*args)
37
+ end
38
+
35
39
  private
36
40
 
37
41
  def ttl
@@ -6,6 +6,8 @@ module Cache
6
6
  def initialize
7
7
  self.enabled = true
8
8
  self.ttl = 86400
9
+ self.adapter = nil
10
+ self.cache = nil
9
11
  end
10
12
 
11
13
  def adapter
@@ -9,9 +9,8 @@ module Cache
9
9
 
10
10
  def mapping_cache_keys
11
11
  mappings.map do |mapping|
12
- attributes = mapping.inject({}) do |memo, attr|
13
- memo[attr] = instance.send(attr)
14
- memo
12
+ attributes = {}.tap do |obj|
13
+ mapping.each { |attr| obj[attr] = instance.send(attr) }
15
14
  end
16
15
  KeyGenerator.key_for_mapping(instance.class.name, attributes)
17
16
  end
@@ -0,0 +1,36 @@
1
+ module Cache
2
+ module Object
3
+ class MultiGet
4
+ attr_reader :clazz
5
+
6
+ def initialize(clazz)
7
+ @clazz = clazz
8
+ end
9
+
10
+ def fetch_all(ids)
11
+ objs = cached_objects(ids)
12
+ remaining = missed_ids(ids, objs)
13
+ return objs if remaining.empty?
14
+ objs + load_from_db(remaining)
15
+ end
16
+
17
+ def load_from_db(ids)
18
+ primary_key = clazz.primary_key.to_sym
19
+ clazz.where(primary_key => ids).to_a.each(&:write_cache!)
20
+ end
21
+
22
+ def object_keys(ids)
23
+ ids.map { |id| Cache::Object::KeyGenerator.key_for_object(clazz.name, id) }
24
+ end
25
+
26
+ def cached_objects(ids)
27
+ keys = object_keys(ids)
28
+ Cache::Object.adapter.read_multi(keys).values
29
+ end
30
+
31
+ def missed_ids(ids, fetched_objects)
32
+ ids - fetched_objects.map(&:id)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,5 @@
1
1
  module Cache
2
2
  module Object
3
- VERSION = "0.0.3"
3
+ VERSION = "0.0.4"
4
4
  end
5
5
  end
@@ -58,7 +58,6 @@ RSpec.describe Cache::Object::ActiveRecord do
58
58
  end
59
59
  end
60
60
 
61
-
62
61
  describe "caching methods" do
63
62
  describe "#write_cache" do
64
63
  it "calls write cache" do
@@ -68,7 +67,6 @@ RSpec.describe Cache::Object::ActiveRecord do
68
67
  end
69
68
  end
70
69
 
71
-
72
70
  describe "#expire_cache" do
73
71
  it "calls write cache" do
74
72
  expect(adapter_instance).to receive(:delete).with(an_instance_of(Cache::Object::InstanceDecorator))
@@ -108,6 +106,15 @@ RSpec.describe Cache::Object::ActiveRecord do
108
106
  end
109
107
  end
110
108
 
109
+ describe ".fetch_all" do
110
+ it "should call through to multi_get" do
111
+ multi_getter = double(fetch_all: true)
112
+ expect(Cache::Object::MultiGet).to receive(:new).with(clazz) { multi_getter }
113
+ expect(multi_getter).to receive(:fetch_all).with([1,2,4])
114
+ clazz.fetch_all([1, 2, 4])
115
+ end
116
+ end
117
+
111
118
  describe "object_cache_on" do
112
119
 
113
120
  it "creates_finder_methods" do
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Cache::Object::MultiGet do
4
+
5
+ let(:fake_clazz) { double(name: "MyObj", primary_key: :foo) }
6
+ let(:obj_arr) { 1.upto(3).map { |i| double(class: fake_clazz, id: i) } }
7
+ let(:multi_get) { Cache::Object::MultiGet.new(fake_clazz) }
8
+ let(:cache_store) { double("CacheStore", write: true) }
9
+ let(:adapter) { Cache::Object::Adapter.new(cache_store) }
10
+
11
+ describe "#object_keys" do
12
+ it "maps keys correctly" do
13
+ expect(multi_get.object_keys(1..3)).to eq(["MyObj-1", "MyObj-2", "MyObj-3"])
14
+ end
15
+ end
16
+
17
+ describe "#cached_objects" do
18
+ it "fetches all the mapped keys" do
19
+ expect(Cache::Object).to receive(:adapter) { adapter }
20
+ expect(adapter).to receive(:read_multi).with(["MyObj-1", "MyObj-2", "MyObj-3"]) { double(values: true) }
21
+ multi_get.cached_objects(1..3)
22
+ end
23
+ end
24
+
25
+ describe "#missed_ids" do
26
+ let(:initial_ids) { [1, 2, 3, 4, 5, 6] }
27
+ it "computes missed ids" do
28
+ expect(multi_get.missed_ids(initial_ids, obj_arr)).to eq([4, 5, 6])
29
+ end
30
+ end
31
+
32
+ describe "#load_remaining" do
33
+ it "performs missed queries" do
34
+ expect(fake_clazz).to receive(:where).with(:foo => [1, 2, 3]).once { [double(write_cache!: true)] }
35
+ multi_get.load_from_db([1, 2, 3])
36
+ end
37
+ end
38
+
39
+ describe "#fetch_all" do
40
+ describe "with all expected ids" do
41
+ it "never calls through to db" do
42
+ expect(multi_get).to receive(:cached_objects).with([1, 2, 3]) { obj_arr }
43
+ expect(multi_get).to receive(:load_from_db).never
44
+ multi_get.fetch_all([1, 2, 3])
45
+ end
46
+ end
47
+
48
+ describe "with a cache miss" do
49
+ it "calls through to db" do
50
+ expect(multi_get).to receive(:cached_objects).with([1, 2, 3]) { [ obj_arr[0] ]}
51
+ expect(multi_get).to receive(:load_from_db).with([2, 3]) { [obj_arr[1], obj_arr[2]] }
52
+ expect(multi_get.fetch_all([1, 2, 3])).to have(3).items
53
+ end
54
+ end
55
+ end
56
+
57
+ end
@@ -7,13 +7,15 @@ RSpec.describe "Caching" do
7
7
 
8
8
  before do
9
9
  CreateModelsForTest.migrate(:up)
10
+ cache = ::ActiveSupport::Cache::MemoryStore.new
10
11
  Cache::Object.configure do |c|
11
- c.cache = ::ActiveSupport::Cache::MemoryStore.new
12
+ c.cache = cache
12
13
  end
13
14
  end
14
15
 
15
16
  after do
16
17
  CreateModelsForTest.migrate(:down)
18
+ Cache::Object.instance_variable_set(:@configuration, nil)
17
19
  end
18
20
 
19
21
  let!(:user) { User.create(age: 13, name: "Bob") }
@@ -52,6 +54,30 @@ RSpec.describe "Caching" do
52
54
  end
53
55
  end
54
56
 
57
+ describe "#fetch_all" do
58
+ let!(:u1) { 1.upto(3).map { |i| User.create(age: 13, name: "name#{i}") } }
59
+ it "Should call db once for all in one read" do
60
+
61
+ expect {
62
+ User.fetch_all(u1.map(&:id))
63
+ }.to change { ActiveRecord::QueryCounter.query_count }.by(0)
64
+ end
65
+
66
+ it "Should call the db again after cache flush" do
67
+ Cache::Object.configuration.cache.clear
68
+
69
+ expect {
70
+ User.fetch_all(u1.map(&:id))
71
+ }.to change { ActiveRecord::QueryCounter.query_count }.by(1)
72
+
73
+ # Should hit cache
74
+ expect {
75
+ User.fetch_all(u1.map(&:id))
76
+ }.to change { ActiveRecord::QueryCounter.query_count }.by(0)
77
+ end
78
+ end
79
+
80
+
55
81
  describe "when user id destroyed" do
56
82
  it "tries to run a query" do
57
83
  user.destroy
@@ -74,4 +100,6 @@ RSpec.describe "Caching" do
74
100
  }.to change { ActiveRecord::QueryCounter.query_count }.by(1)
75
101
  end
76
102
  end
103
+
104
+
77
105
  end
@@ -1,15 +1,17 @@
1
1
  require 'cache/object'
2
+ require 'rspec/collection_matchers'
2
3
  RSpec.configure do |config|
3
4
 
4
- config.before do
5
+ config.before(:each) do
5
6
  Cache::Object.instance_variable_set(:@configuration, nil)
7
+ Cache::Object.instance_variable_set(:@adapter, nil)
6
8
  end
7
9
 
8
- config.after do
10
+ config.after(:each) do
9
11
  Cache::Object.instance_variable_set(:@configuration, nil)
12
+ Cache::Object.instance_variable_set(:@adapter, nil)
10
13
  end
11
14
 
12
15
  config.raise_errors_for_deprecations!
13
16
  config.disable_monkey_patching!
14
-
15
17
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cache-object
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Camuto
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-collection_matchers
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: guard
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -139,11 +153,13 @@ files:
139
153
  - lib/cache/object/config.rb
140
154
  - lib/cache/object/instance_decorator.rb
141
155
  - lib/cache/object/key_generator.rb
156
+ - lib/cache/object/multi_get.rb
142
157
  - lib/cache/object/version.rb
143
158
  - spec/cache/object/active_record_spec.rb
144
159
  - spec/cache/object/adapter_spec.rb
145
160
  - spec/cache/object/config_spec.rb
146
161
  - spec/cache/object/instance_decorator_spec.rb
162
+ - spec/cache/object/multi_get_spec.rb
147
163
  - spec/features/cache_object_spec.rb
148
164
  - spec/spec_helper.rb
149
165
  - spec/support/models.rb
@@ -176,6 +192,7 @@ test_files:
176
192
  - spec/cache/object/adapter_spec.rb
177
193
  - spec/cache/object/config_spec.rb
178
194
  - spec/cache/object/instance_decorator_spec.rb
195
+ - spec/cache/object/multi_get_spec.rb
179
196
  - spec/features/cache_object_spec.rb
180
197
  - spec/spec_helper.rb
181
198
  - spec/support/models.rb