couch_potato 1.14.0 → 1.15.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
  SHA256:
3
- metadata.gz: dd4dede2152745db1f3f0bf7ff3a94dfea3c7eb427beb02292034866dd7dac22
4
- data.tar.gz: 29ca77512b6bdeebcdd31847deac4957792946519dda4f658f60dd90c9dd83e4
3
+ metadata.gz: ffcaae6b2815d237b48cc3e4ad553f66920fe4df2c84c59ae38177260949ba6b
4
+ data.tar.gz: bd266a1033fa83fcba822a19e3de86b2c1aac2d80ba2dd1d3aef0920d4819d8f
5
5
  SHA512:
6
- metadata.gz: 21728691cd7eb4b23bd6600899655cc7fa1dbb6a15a6d40c669b53941fab30bf38cb8a95fa9a3e8f85e05d44af116f58cd20a424eedd60d417972db893c07aff
7
- data.tar.gz: d2d26373afd0bcce487b35703be3bd7bd08cc7e4e108f98e8279bf81c41715b30f5590fdf8080c4b136ec95e356c857b5c50bef123b6971e67294e79cdeb180a
6
+ metadata.gz: 16f3e1748e3b3314f1cf3d658fb175dd90c5e7c7cd717ab42477961c364bac527b262642a29f208c4aad200e9a31ce7470c0a538db8bf8d641e13da92f85debb
7
+ data.tar.gz: '089f1c9e5dc7238b659a4d1b2d0f3d341a084fe52f6d9c2d058ea8440f197f7d64b6f5a9d78c8cebbd19b131aede038329ab6c0f79b503d220f1f20d2e7d8a20'
data/CHANGES.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## Changes
2
2
 
3
+ # 1.15.0
4
+
5
+ - cache loading multiple documents
6
+ - keep the database cache when switching to another database and back to the original one
7
+
3
8
  # 1.14.0
4
9
 
5
10
  - add database_collection to models to help avoid n+1 requests
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/enumerable'
4
+
3
5
  module CouchPotato
4
6
  class Database
5
7
  class ValidationsFailedError < ::StandardError; end
@@ -143,7 +145,9 @@ module CouchPotato
143
145
  # returns nil if the single document could not be found. when passing an array and some documents
144
146
  # could not be found these are omitted from the returned array
145
147
  def load_document(id)
146
- cached = cache && id.is_a?(String) && cache[id]
148
+ return load_documents(id) if id.is_a?(Array)
149
+
150
+ cached = cache && cache[id]
147
151
  if cache
148
152
  if cached
149
153
  ActiveSupport::Notifications.instrument('couch_potato.load.cached') do
@@ -159,6 +163,23 @@ module CouchPotato
159
163
  end
160
164
  alias load load_document
161
165
 
166
+ def load_documents(ids)
167
+ return [] if ids.empty?
168
+
169
+ uncached_ids = ids - (cache&.keys || [])
170
+ uncached_docs_by_id = bulk_load(uncached_ids).index_by {|doc| doc.id if doc.respond_to?(:id) }
171
+ if cache
172
+ uncached_ids.each do |id|
173
+ doc = uncached_docs_by_id[id]
174
+ cache[id] = doc if doc
175
+ end
176
+ end
177
+ cached_docs_by_id = ActiveSupport::Notifications.instrument('couch_potato.load.cached') do
178
+ cache&.slice(*ids) || {}
179
+ end
180
+ ids.filter_map { |id| (cached_docs_by_id[id]) || uncached_docs_by_id[id] }
181
+ end
182
+
162
183
  # loads one or more documents by its id(s)
163
184
  # behaves like #load except it raises a CouchPotato::NotFound if any of the documents could not be found
164
185
  def load!(id)
@@ -176,6 +197,8 @@ module CouchPotato
176
197
  # returns the underlying CouchRest::Database instance
177
198
  attr_reader :couchrest_database
178
199
 
200
+ attr_accessor :switched_databases
201
+
179
202
  # returns a new database instance connected to the CouchDB database
180
203
  # with the given name. the name is passed through the
181
204
  # additional_databases configuration to resolve it to a database
@@ -183,21 +206,29 @@ module CouchPotato
183
206
  # if the current database has a cache, the new database will receive
184
207
  # a cleared copy of it.
185
208
  def switch_to(database_name)
186
- resolved_database_name = CouchPotato.resolve_database_name(database_name)
187
- self
209
+ self.switched_databases ||= {}
210
+ if (db = switched_databases[database_name || :default])
211
+ return db
212
+ end
213
+ switched_databases[name || :default] ||= self
214
+
215
+
216
+ resolved_database_name = CouchPotato.resolve_database_name(database_name) unless database_name == :default
217
+ couchrest_database = resolved_database_name ? CouchPotato.couchrest_database_for_name(resolved_database_name) : CouchPotato.couchrest_database
218
+ new_db = self
188
219
  .class
189
- .new(CouchPotato.couchrest_database_for_name(resolved_database_name), name: database_name)
220
+ .new(couchrest_database, name: database_name == :default ? nil : database_name)
190
221
  .tap(&copy_clear_cache_proc)
222
+
223
+ new_db.switched_databases = switched_databases
224
+ new_db
191
225
  end
192
226
 
193
227
  # returns a new database instance connected to the default CouchDB database.
194
228
  # if the current database has a cache, the new database will receive
195
229
  # a cleared copy of it.
196
230
  def switch_to_default
197
- self
198
- .class
199
- .new(CouchPotato.couchrest_database)
200
- .tap(&copy_clear_cache_proc)
231
+ switch_to(:default)
201
232
  end
202
233
 
203
234
  private
@@ -248,14 +279,10 @@ module CouchPotato
248
279
  raise "Can't load a document without an id (got nil)" if id.nil?
249
280
 
250
281
  ActiveSupport::Notifications.instrument('couch_potato.load') do
251
- if id.is_a?(Array)
252
- bulk_load id
253
- else
254
- instance = couchrest_database.get(id)
255
- instance.database = self if instance
256
- instance
257
- end
258
- end
282
+ instance = couchrest_database.get(id)
283
+ instance.database = self if instance
284
+ instance
285
+ end
259
286
  end
260
287
 
261
288
  def view_cache_id(spec)
@@ -294,11 +321,13 @@ module CouchPotato
294
321
  def bulk_load(ids)
295
322
  return [] if ids.empty?
296
323
 
297
- response = couchrest_database.bulk_load ids
298
- docs = response['rows'].map { |row| row['doc'] }.compact
299
- docs.each do |doc|
300
- doc.database = self if doc.respond_to?(:database=)
301
- doc.database_collection = docs if doc.respond_to?(:database_collection=)
324
+ ActiveSupport::Notifications.instrument('couch_potato.load') do
325
+ response = couchrest_database.bulk_load ids
326
+ docs = response['rows'].map { |row| row['doc'] }.compact
327
+ docs.each do |doc|
328
+ doc.database = self if doc.respond_to?(:database=)
329
+ doc.database_collection = docs if doc.respond_to?(:database_collection=)
330
+ end
302
331
  end
303
332
  end
304
333
 
@@ -1,4 +1,4 @@
1
1
  module CouchPotato
2
- VERSION = '1.14.0'.freeze
2
+ VERSION = '1.15.0'.freeze
3
3
  RSPEC_VERSION = '4.1.0'.freeze
4
4
  end
@@ -18,41 +18,92 @@ RSpec.describe 'database caching' do
18
18
  {}
19
19
  end
20
20
 
21
- it 'gets an object from the cache the 2nd time via #load_documemt' do
22
- expect(couchrest_db).to receive(:get).with('1').exactly(1).times
21
+ context 'for a single document' do
22
+ it 'gets an object from the cache the 2nd time via #load_documemt' do
23
+ expect(couchrest_db).to receive(:get).with('1').exactly(1).times
23
24
 
24
- db.load_document '1'
25
- db.load_document '1'
26
- end
25
+ db.load_document '1'
26
+ db.load_document '1'
27
+ end
27
28
 
28
- it 'gets an object from the cache the 2nd time via #load' do
29
- expect(couchrest_db).to receive(:get).with('1').exactly(1).times
29
+ it 'gets an object from the cache the 2nd time via #load' do
30
+ expect(couchrest_db).to receive(:get).with('1').exactly(1).times
30
31
 
31
- db.load '1'
32
- db.load '1'
33
- end
32
+ db.load '1'
33
+ db.load '1'
34
+ end
34
35
 
35
- it 'gets an object from the cache the 2nd time via #load!' do
36
- expect(couchrest_db).to receive(:get).with('1').exactly(1).times
36
+ it 'gets an object from the cache the 2nd time via #load!' do
37
+ expect(couchrest_db).to receive(:get).with('1').exactly(1).times
37
38
 
38
- db.load! '1'
39
- db.load! '1'
40
- end
39
+ db.load! '1'
40
+ db.load! '1'
41
+ end
41
42
 
42
- it 'returns the correct object' do
43
- doc = double(:doc, 'database=': nil)
44
- allow(couchrest_db).to receive_messages(get: doc)
43
+ it 'returns the correct object' do
44
+ doc = double(:doc, 'database=': nil)
45
+ allow(couchrest_db).to receive_messages(get: doc)
45
46
 
46
- db.load_document '1'
47
- expect(db.load_document('1')).to eql(doc)
47
+ db.load_document '1'
48
+ expect(db.load_document('1')).to eql(doc)
49
+ end
48
50
  end
49
51
 
50
- it 'does not cache bulk loads' do
51
- allow(couchrest_db).to receive_messages(bulk_load: {'rows' => []})
52
- expect(couchrest_db).to receive(:bulk_load).with(['1']).exactly(2).times
52
+ context 'for multiple documents' do
53
+ let(:doc1) { double(:doc1, 'database=': nil, id: '1') }
54
+ let(:doc2) { double(:doc12, 'database=': nil, id: '2') }
55
+
56
+ it 'only loads uncached documents' do
57
+ allow(couchrest_db).to receive(:bulk_load).with(['1']).and_return('rows' => [{'doc' => doc1}])
58
+ allow(couchrest_db).to receive(:bulk_load).with(['2']).and_return('rows' => [{'doc' => doc2}])
59
+
60
+
61
+ db.load_document(['1'])
62
+ db.load_document(['1', '2'])
63
+
64
+ expect(couchrest_db).to have_received(:bulk_load).with(['1']).exactly(1).times
65
+ expect(couchrest_db).to have_received(:bulk_load).with(['2']).exactly(1).times
66
+ end
67
+
68
+ it 'loads nothing if all documents are cached' do
69
+ allow(couchrest_db).to receive(:bulk_load).with(['1', '2'])
70
+ .and_return('rows' => [{'doc' => doc1}, {'doc' => doc2}])
71
+
72
+ db.load_document(['1', '2'])
73
+ db.load_document(['1', '2'])
74
+
75
+ expect(couchrest_db).to have_received(:bulk_load).with(['1', '2']).exactly(1).times
76
+ end
77
+
78
+ it 'returns all requested documents' do
79
+ allow(couchrest_db).to receive(:bulk_load).with(['1']).and_return('rows' => [{'doc' => doc1}])
80
+ allow(couchrest_db).to receive(:bulk_load).with(['2']).and_return('rows' => [{'doc' => doc2}])
81
+
82
+
83
+ db.load_document(['1'])
84
+ result = db.load_document(['1', '2'])
85
+
86
+ expect(result).to eql([doc1, doc2])
87
+ end
88
+
89
+ it 'does not cache documents that do not respond to id' do
90
+ doc1 = {
91
+ 'id' => '1',
92
+ }
93
+ doc2 = {
94
+ 'id' => '2',
95
+ }
96
+ allow(couchrest_db).to receive(:bulk_load).with(['1', '2'])
97
+ .and_return('rows' => [{'doc' => doc1}, {'doc' => doc1}])
98
+
99
+ db.load_document(['1', '2'])
100
+ db.load_document(['1', '2'])
101
+
102
+ expect(couchrest_db).to have_received(:bulk_load).with(['1', '2']).exactly(2).times
103
+ end
104
+ end
53
105
 
54
- db.load_document ['1']
55
- db.load_document ['1']
106
+ context 'when switching the database' do
56
107
  end
57
108
 
58
109
  it 'clears the cache when destroying a document via #destroy_document' do
@@ -72,8 +72,8 @@ describe CouchPotato::Database, 'load' do
72
72
  end
73
73
 
74
74
  context 'when several ids given' do
75
- let(:doc1) { DbTestUser.new }
76
- let(:doc2) { DbTestUser.new }
75
+ let(:doc1) { DbTestUser.new(id: '1') }
76
+ let(:doc2) { DbTestUser.new(id: '2') }
77
77
  let(:response) do
78
78
  { 'rows' => [{ 'doc' => nil }, { 'doc' => doc1 }, { 'doc' => doc2 }] }
79
79
  end
@@ -98,7 +98,7 @@ describe CouchPotato::Database, 'load' do
98
98
  end
99
99
 
100
100
  it 'does not write itself to a document that has no database= method' do
101
- doc1 = double(:doc1)
101
+ doc1 = double(:doc1, id: '1')
102
102
  allow(doc1).to receive(:respond_to?) { false }
103
103
  allow(couchrest_db).to receive(:bulk_load) do
104
104
  { 'rows' => [{ 'doc' => doc1 }] }
@@ -116,7 +116,7 @@ describe CouchPotato::Database, 'load' do
116
116
  end
117
117
 
118
118
  it 'does not set database_collection on a document that has no database_collection= method' do
119
- doc1 = double(:doc1)
119
+ doc1 = double(:doc1, id: '1')
120
120
  allow(doc1).to receive(:respond_to?) { false }
121
121
  allow(couchrest_db).to receive(:bulk_load) do
122
122
  { 'rows' => [{ 'doc' => doc1 }] }
@@ -571,6 +571,14 @@ describe CouchPotato::Database, '#switch_to' do
571
571
 
572
572
  expect(new_db.cache).to be_nil
573
573
  end
574
+
575
+ it 're-uses the original cache when switching back to the original database' do
576
+ db.cache = { key: 'value' }
577
+ new_db = db.switch_to('db2')
578
+ original_db = new_db.switch_to_default
579
+
580
+ expect(original_db.cache).to have_key(:key)
581
+ end
574
582
  end
575
583
 
576
584
  describe CouchPotato::Database, '#switch_to_default' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couch_potato
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.14.0
4
+ version: 1.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Lang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-25 00:00:00.000000000 Z
11
+ date: 2024-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel