couch_potato 1.10.0 → 1.11.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: 699af51806a63fca2975c8eff17636647a5ca7c12f9131df7e30a5f843ab3028
4
- data.tar.gz: 1e305be23ca0606bef97e7f89b06f166f694a7ec937a2641c782274dc9a8cf51
3
+ metadata.gz: fc65386f8f391b222c93733fbf83cb00050bc37b2fa1787be8d73e4847b2fa43
4
+ data.tar.gz: a2c6fe4017c646b3bceaa9baaa236044ffe286fd3380095296a602034511d817
5
5
  SHA512:
6
- metadata.gz: bee39cf067a428f14b646948dc0ceea5e5b0bfde2f0eafd9e1ea025dbd5833bcfc8c328f8457aaa4ecb72aabf07a2e327f329296d1224dafb8c7967cf37d1eeb
7
- data.tar.gz: 4f371624eb17e5d03fb25a13ae9375705f1b143fccb30c8e7cbfdeaeb98d8549b7cc29f9b7a7fe83ec0ac06abef1200670ea092aebd5e46bb7aa94265ee48191
6
+ metadata.gz: 5f7c933270982e090183aacdb7fdd52e76e2739476e7e4d87cbcad1239c62b4973f31ce62278f181c33612bc6c2dc8ee7abbc2ffca0bef4560b919b8a37359c8
7
+ data.tar.gz: 7f79c9a6b3c31f5f1fc0eea3f4a16da6e7cb6ace5a68232a9a127d7d056b5144d88714e12e128a64eba34a9377e073311408077958c14b818c0f4037853a59a5
@@ -16,7 +16,7 @@ jobs:
16
16
  strategy:
17
17
  fail-fast: false
18
18
  matrix:
19
- ruby: [2.6, 2.7, "jruby"]
19
+ ruby: [2.7, '3.0', "jruby"]
20
20
  gemfile:
21
21
  - "active_support_5_0"
22
22
  - "active_support_5_1"
@@ -25,10 +25,16 @@ jobs:
25
25
  - "active_support_6_1"
26
26
  - "active_support_7_0"
27
27
  exclude:
28
- - ruby: 2.6
29
- gemfile: "active_support_7_0"
30
28
  - ruby: "jruby"
31
29
  gemfile: "active_support_7_0"
30
+ - ruby: "3.0"
31
+ gemfile: "active_support_5_0"
32
+ - ruby: "3.0"
33
+ gemfile: "active_support_5_1"
34
+ - ruby: "3.0"
35
+ gemfile: "active_support_5_2"
36
+ - ruby: "3.0"
37
+ gemfile: "active_support_6_0"
32
38
  steps:
33
39
  - uses: actions/checkout@v2
34
40
  - name: Set up CouchDB
data/CHANGES.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## Changes
2
2
 
3
+ # 1.11.0
4
+
5
+ - improve view_in_batches performance by switching to using startkey_docid over skip
6
+
7
+ ### 1.10.1
8
+
9
+ - support passing an empty array to CouchPotato::Database#load
10
+
3
11
  ### 1.10.0
4
12
 
5
13
  - add spec/support for Rails 7
data/couch_potato.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.add_dependency 'json', '~> 2.3'
19
19
 
20
20
  s.add_development_dependency 'rake', '~>12.0'
21
- s.add_development_dependency 'rspec', '~>3.5.0'
21
+ s.add_development_dependency 'rspec', '~>3.10.0'
22
22
  s.add_development_dependency 'timecop'
23
23
  s.add_development_dependency 'tzinfo'
24
24
 
@@ -65,12 +65,27 @@ module CouchPotato
65
65
  # to a given block in batches of the given size, making multiple
66
66
  # requests with according skip/limit params sent to CouchDB.
67
67
  def view_in_batches(spec, batch_size: default_batch_size)
68
+ rows = nil
68
69
  batch = 0
69
70
  loop do
70
- spec.view_parameters = spec.view_parameters.merge({ skip: batch * batch_size, limit: batch_size })
71
- results = view(spec)
72
- yield results
73
- break if results.size < batch_size
71
+ spec.view_parameters = spec
72
+ .view_parameters
73
+ .merge({limit: batch_size})
74
+ .merge(
75
+ if rows
76
+ {
77
+ startkey: rows&.last&.dig('key'),
78
+ startkey_docid: rows&.last&.dig('id'),
79
+ skip: 1
80
+ }
81
+ else
82
+ {}
83
+ end
84
+ )
85
+ result = raw_view(spec)
86
+ rows = result['rows']
87
+ yield process_view_results(result, spec)
88
+ break if rows.size < batch_size
74
89
 
75
90
  batch += 1
76
91
  end
@@ -198,27 +213,34 @@ module CouchPotato
198
213
 
199
214
  def view_without_caching(spec)
200
215
  ActiveSupport::Notifications.instrument('couch_potato.view', name: "#{spec.design_document}/#{spec.view_name}") do
201
- results = CouchPotato::View::ViewQuery.new(
202
- couchrest_database,
203
- spec.design_document,
204
- { spec.view_name => {
205
- map: spec.map_function,
206
- reduce: spec.reduce_function
207
- } },
208
- ({ spec.list_name => spec.list_function } unless spec.list_name.nil?),
209
- spec.lib,
210
- spec.language
211
- ).query_view!(spec.view_parameters)
212
- processed_results = spec.process_results results
213
- if processed_results.respond_to?(:database=)
214
- processed_results.database = self
215
- elsif processed_results.respond_to?(:each)
216
- processed_results.each do |document|
217
- document.database = self if document.respond_to?(:database=)
218
- end
216
+ process_view_results(raw_view(spec), spec)
217
+ end
218
+ end
219
+
220
+ def process_view_results(results, spec)
221
+ processed_results = spec.process_results results
222
+ if processed_results.respond_to?(:database=)
223
+ processed_results.database = self
224
+ elsif processed_results.respond_to?(:each)
225
+ processed_results.each do |document|
226
+ document.database = self if document.respond_to?(:database=)
219
227
  end
220
- processed_results
221
228
  end
229
+ processed_results
230
+ end
231
+
232
+ def raw_view(spec)
233
+ CouchPotato::View::ViewQuery.new(
234
+ couchrest_database,
235
+ spec.design_document,
236
+ { spec.view_name => {
237
+ map: spec.map_function,
238
+ reduce: spec.reduce_function
239
+ } },
240
+ ({ spec.list_name => spec.list_function } unless spec.list_name.nil?),
241
+ spec.lib,
242
+ spec.language
243
+ ).query_view!(spec.view_parameters)
222
244
  end
223
245
 
224
246
  def load_document_without_caching(id)
@@ -269,6 +291,8 @@ module CouchPotato
269
291
  end
270
292
 
271
293
  def bulk_load(ids)
294
+ return [] if ids.empty?
295
+
272
296
  response = couchrest_database.bulk_load ids
273
297
  docs = response['rows'].map { |row| row['doc'] }.compact
274
298
  docs.each do |doc|
@@ -1,4 +1,4 @@
1
1
  module CouchPotato
2
- VERSION = '1.10.0'.freeze
2
+ VERSION = '1.11.0'.freeze
3
3
  RSPEC_VERSION = '4.0.2'.freeze
4
4
  end
data/spec/railtie_spec.rb CHANGED
@@ -87,7 +87,7 @@ describe "railtie" do
87
87
  it 'assigns additional_databases to config' do
88
88
  allow(File).to receive_messages(:read => "test:\n database: test\n additional_databases:\n db2: test2")
89
89
 
90
- expect(CouchPotato::Config).to receive(:additional_databases=).with('db2' => 'test2')
90
+ expect(CouchPotato::Config).to receive(:additional_databases=).with({'db2' => 'test2'})
91
91
 
92
92
  CouchPotato.rails_init
93
93
  end
@@ -98,6 +98,10 @@ describe CouchPotato::Database, 'load' do
98
98
 
99
99
  db.load(['1'])
100
100
  end
101
+
102
+ it 'returns an empty array when passing an empty array' do
103
+ expect(db.load([])).to eq([])
104
+ end
101
105
  end
102
106
  end
103
107
 
@@ -426,11 +430,11 @@ describe CouchPotato::Database, '#view_in_batches' do
426
430
  let(:view_query) do
427
431
  instance_double(
428
432
  CouchPotato::View::ViewQuery,
429
- query_view!: { 'rows' => [result] }
433
+ query_view!: { 'rows' => [] }
430
434
  )
431
435
  end
432
- let(:result) { double('result') }
433
- let(:spec) { double('view spec', process_results: [result]).as_null_object }
436
+ let(:processed_result) { double(:processed_result) }
437
+ let(:spec) { double('view spec', process_results: processed_result).as_null_object }
434
438
  let(:couchrest_db) { double('couchrest db').as_null_object }
435
439
  let(:db) { CouchPotato::Database.new(couchrest_db) }
436
440
 
@@ -439,22 +443,41 @@ describe CouchPotato::Database, '#view_in_batches' do
439
443
  .to receive_messages(new: view_query)
440
444
  end
441
445
 
442
- it 'sets skip/limit for each batch' do
443
- allow(spec).to receive(:process_results).and_return([result, result], [result]) # run twice
446
+ it 'sets no skip/startkey/startkey_docid for the first batch' do
444
447
  allow(spec).to receive(:view_parameters) { { key: 'x' } }
445
448
 
446
449
  expect(spec).to receive(:view_parameters=)
447
- .with(key: 'x', skip: 0, limit: 2)
450
+ .with({key: 'x', limit: 2})
451
+
452
+ db.view_in_batches(spec, batch_size: 2) { |results| }
453
+ end
454
+
455
+ it 'sets skip/startkey/startkey_docid for each other batch' do
456
+ allow(spec).to receive(:view_parameters) { { key: 'x' } }
457
+ allow(view_query).to receive(:query_view!)
458
+ .and_return({'rows' => [{}, {'key' => 'k1', 'id' => 'id1'}]}, {'rows' => [{}]})
459
+ allow(spec).to receive(:view_parameters=)
460
+
448
461
  expect(spec).to receive(:view_parameters=)
449
- .with(key: 'x', skip: 2, limit: 2)
462
+ .with({key: 'x', limit: 2, startkey: 'k1', startkey_docid: 'id1', skip: 1})
450
463
 
451
464
  db.view_in_batches(spec, batch_size: 2) { |results| }
452
465
  end
453
466
 
467
+ it 'yields processed results to the block' do
468
+ allow(view_query).to receive(:query_view!)
469
+ .and_return({'rows' => [{'key' => 'k1', 'id' => 'id1'}]})
470
+ allow(spec).to receive(:view_parameters=)
471
+
472
+ expect { |x| db.view_in_batches(spec, batch_size: 2, &x) }.to yield_with_args(processed_result)
473
+ end
474
+
454
475
  it 'yields batches until running out of data' do
455
- allow(spec).to receive(:process_results).and_return([result, result], [result])
476
+ allow(view_query).to receive(:query_view!)
477
+ .and_return({'rows' => [{}, {}]}, {'rows' => [{}]})
478
+ allow(spec).to receive(:process_results).and_return([processed_result, processed_result], [processed_result])
456
479
 
457
- expect { |b| db.view_in_batches(spec, batch_size: 2, &b) }.to yield_successive_args([result, result], [result])
480
+ expect { |b| db.view_in_batches(spec, batch_size: 2, &b) }.to yield_successive_args([processed_result, processed_result], [processed_result])
458
481
  end
459
482
  end
460
483
 
@@ -15,10 +15,12 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
15
15
 
16
16
  it 'updates a view if it does not exist' do
17
17
  expect(db).to receive(:save_doc).with(
18
- 'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}, 'lib' => {'test' => '<lib_code>'}},
19
- 'lists' => {},
20
- "_id" => "_design/design",
21
- "language" => "javascript"
18
+ {
19
+ 'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}, 'lib' => {'test' => '<lib_code>'}},
20
+ 'lists' => {},
21
+ "_id" => "_design/design",
22
+ "language" => "javascript"
23
+ }
22
24
  )
23
25
 
24
26
  CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}}, nil, {'test' => "<lib_code>"}).query_view!
@@ -46,8 +48,11 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
46
48
 
47
49
  it 'updates a view in erlang if it does not exist' do
48
50
  expect(db).to receive(:save_doc).with(
49
- 'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}},
50
- 'lists' => {}, "_id" => "_design/design", "language" => "erlang")
51
+ {
52
+ 'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}},
53
+ 'lists' => {}, "_id" => "_design/design", "language" => "erlang"
54
+ }
55
+ )
51
56
 
52
57
  CouchPotato::View::ViewQuery.new(db, 'design',
53
58
  {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}},
@@ -120,7 +125,7 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
120
125
 
121
126
  it 'does not pass in reduce or lib keys if there is no lib or reduce object' do
122
127
  allow(db).to receive(:get).and_return({'views' => {}})
123
- expect(db).to receive(:save_doc).with('views' => {'view7' => {'map' => '<map code>'}})
128
+ expect(db).to receive(:save_doc).with({'views' => {'view7' => {'map' => '<map code>'}}})
124
129
  CouchPotato::View::ViewQuery.new(db, 'design', :view7 => {:map => '<map code>', :reduce => nil}).query_view!
125
130
  end
126
131
 
data/spec/views_spec.rb CHANGED
@@ -405,4 +405,15 @@ describe 'views' do
405
405
  expect(@db.view(Build.timeline(stale: 'ok'))).to be_empty
406
406
  end
407
407
  end
408
+
409
+ describe 'view_in_batches' do
410
+ it 'yields docs in batches until all gone' do
411
+ build1 = Build.new(time: 1).tap {|b| @db.save!(b) }
412
+ build2 = Build.new(time: 2).tap {|b| @db.save!(b) }
413
+ build3 = Build.new(time: 3).tap {|b| @db.save!(b) }
414
+
415
+ expect {|block| @db.view_in_batches(Build.timeline, batch_size: 2, &block)}
416
+ .to yield_successive_args([build1, build2], [build3])
417
+ end
418
+ end
408
419
  end
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.10.0
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Lang
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-28 00:00:00.000000000 Z
11
+ date: 2022-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -78,14 +78,14 @@ dependencies:
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: 3.5.0
81
+ version: 3.10.0
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: 3.5.0
88
+ version: 3.10.0
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: timecop
91
91
  requirement: !ruby/object:Gem::Requirement
@@ -219,7 +219,7 @@ files:
219
219
  homepage: http://github.com/langalex/couch_potato
220
220
  licenses: []
221
221
  metadata: {}
222
- post_install_message:
222
+ post_install_message:
223
223
  rdoc_options: []
224
224
  require_paths:
225
225
  - lib
@@ -234,8 +234,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
234
234
  - !ruby/object:Gem::Version
235
235
  version: '0'
236
236
  requirements: []
237
- rubygems_version: 3.1.6
238
- signing_key:
237
+ rubygems_version: 3.2.32
238
+ signing_key:
239
239
  specification_version: 4
240
240
  summary: Ruby persistence layer for CouchDB
241
241
  test_files: