couch_potato 1.7.0 → 1.10.1

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.
Files changed (58) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +50 -0
  3. data/.gitignore +3 -0
  4. data/CHANGES.md +180 -130
  5. data/Gemfile +4 -0
  6. data/README.md +61 -85
  7. data/Rakefile +11 -10
  8. data/couch_potato-rspec.gemspec +2 -1
  9. data/couch_potato.gemspec +9 -7
  10. data/gemfiles/active_support_5_0 +6 -0
  11. data/gemfiles/active_support_5_1 +7 -0
  12. data/gemfiles/active_support_5_2 +6 -0
  13. data/gemfiles/active_support_6_0 +6 -0
  14. data/gemfiles/active_support_6_1 +6 -0
  15. data/gemfiles/active_support_7_0 +6 -0
  16. data/lib/couch_potato/database.rb +170 -71
  17. data/lib/couch_potato/persistence/dirty_attributes.rb +3 -21
  18. data/lib/couch_potato/persistence/magic_timestamps.rb +3 -3
  19. data/lib/couch_potato/persistence/properties.rb +15 -10
  20. data/lib/couch_potato/persistence/simple_property.rb +0 -4
  21. data/lib/couch_potato/persistence/type_caster.rb +11 -6
  22. data/lib/couch_potato/persistence.rb +0 -1
  23. data/lib/couch_potato/railtie.rb +6 -11
  24. data/lib/couch_potato/validation.rb +8 -0
  25. data/lib/couch_potato/version.rb +2 -2
  26. data/lib/couch_potato/view/base_view_spec.rb +8 -32
  27. data/lib/couch_potato/view/custom_views.rb +4 -3
  28. data/lib/couch_potato/view/flex_view_spec.rb +121 -0
  29. data/lib/couch_potato/view/view_parameters.rb +34 -0
  30. data/lib/couch_potato.rb +37 -16
  31. data/spec/callbacks_spec.rb +45 -19
  32. data/spec/conflict_handling_spec.rb +1 -2
  33. data/spec/property_spec.rb +12 -3
  34. data/spec/railtie_spec.rb +10 -0
  35. data/spec/spec_helper.rb +4 -3
  36. data/spec/unit/active_model_compliance_spec.rb +7 -3
  37. data/spec/unit/attributes_spec.rb +54 -1
  38. data/spec/unit/caching_spec.rb +105 -0
  39. data/spec/unit/couch_potato_spec.rb +70 -5
  40. data/spec/unit/create_spec.rb +5 -4
  41. data/spec/unit/database_spec.rb +239 -135
  42. data/spec/unit/dirty_attributes_spec.rb +5 -26
  43. data/spec/unit/flex_view_spec_spec.rb +17 -0
  44. data/spec/unit/model_view_spec_spec.rb +1 -1
  45. data/spec/unit/rspec_stub_db_spec.rb +31 -0
  46. data/spec/unit/validation_spec.rb +42 -2
  47. data/spec/unit/view_query_spec.rb +12 -7
  48. data/spec/views_spec.rb +214 -103
  49. data/vendor/pouchdb-collate/LICENSE +202 -0
  50. data/vendor/pouchdb-collate/pouchdb-collate.js +430 -0
  51. metadata +47 -36
  52. data/.ruby-version +0 -1
  53. data/.travis.yml +0 -21
  54. data/gemfiles/active_support_4_0 +0 -11
  55. data/gemfiles/active_support_4_1 +0 -11
  56. data/gemfiles/active_support_4_2 +0 -11
  57. data/lib/couch_potato/persistence/deep_dirty_attributes.rb +0 -180
  58. data/spec/unit/deep_dirty_attributes_spec.rb +0 -434
data/spec/views_spec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  class Build
@@ -5,20 +7,35 @@ class Build
5
7
 
6
8
  property :state
7
9
  property :time
8
- property :type, :type => String, :default => 'Build'
9
-
10
- view :timeline, :key => :time
11
- view :count, :key => :time, :reduce => true
12
- view :minimal_timeline, :key => :time, :properties => [:state], :type => :properties
13
- view :key_array_timeline, :key => [:time, :state]
14
- view :custom_timeline, :map => "function(doc) { emit(doc._id, {state: 'custom_' + doc.state}); }", :type => :custom
15
- view :custom_timeline_returns_docs, :map => "function(doc) { emit(doc._id, null); }", :include_docs => true, :type => :custom
16
- view :custom_with_reduce, :map => "function(doc) {if(doc.foreign_key) {emit(doc.foreign_key, 1);} else {emit(doc._id, 1)}}", :reduce => "function(key, values) {return({\"count\": sum(values)});}", :group => true, :type => :custom
17
- view :custom_count_with_reduce, :map => "function(doc) {if(doc.foreign_key) {emit(doc.foreign_key, 1);} else {emit(doc._id, 1)}}", :reduce => "function(key, values) {return(sum(values));}", :group => true, :type => :custom
18
- view :raw, :type => :raw, :map => "function(doc) {emit(doc._id, doc.state)}"
19
- view :filtered_raw, :type => :raw, :map => "function(doc) {emit(doc._id, doc.state)}", :results_filter => lambda{|res| res['rows'].map{|row| row['value']}}
20
- view :with_view_options, :group => true, :key => :time
21
- view :all, :map => "function(doc) { if (doc && doc.type == 'Build') emit(doc._id, 1); }", :include_docs => true, :type => :custom
10
+ property :type, type: String, default: 'Build'
11
+
12
+ view :timeline, key: :time
13
+ view :count, key: :time, reduce: true
14
+ view :minimal_timeline, key: :time, properties: [:state], type: :properties
15
+ view :key_array_timeline, key: %i[time state]
16
+ view :custom_timeline, map: "function(doc) { emit(doc._id, {state: 'custom_' + doc.state}); }", type: :custom
17
+ view :custom_timeline_returns_docs, map: 'function(doc) { emit(doc._id, null); }', include_docs: true, type: :custom
18
+ view :custom_with_reduce, map: 'function(doc) {if(doc.foreign_key) {emit(doc.foreign_key, 1);} else {emit(doc._id, 1)}}', reduce: 'function(key, values) {return({"count": sum(values)});}', group: true, type: :custom
19
+ view :custom_count_with_reduce, map: 'function(doc) {if(doc.foreign_key) {emit(doc.foreign_key, 1);} else {emit(doc._id, 1)}}', reduce: 'function(key, values) {return(sum(values));}', group: true, type: :custom
20
+ view :raw, type: :raw, map: 'function(doc) {emit(doc._id, doc.state)}'
21
+ view :filtered_raw, type: :raw, map: 'function(doc) {emit(doc._id, doc.state)}', results_filter: ->(res) { res['rows'].map { |row| row['value'] } }
22
+ view :with_view_options, group: true, key: :time
23
+ view :all, map: "function(doc) { if (doc && doc.type == 'Build') emit(doc._id, 1); }", include_docs: true, type: :custom
24
+
25
+ module TimesToInt
26
+ def times_to_int
27
+ docs.map { |doc| doc.time.to_i }
28
+ end
29
+ end
30
+ view :flex_with_key, type: :flex, key: :time, extend_results: TimesToInt
31
+ view :flex_with_custom,
32
+ type: :flex,
33
+ map: <<~JS,
34
+ function(doc) {
35
+ emit(doc.time, 1);
36
+ }
37
+ JS
38
+ reduce: '_sum'
22
39
  end
23
40
 
24
41
  class CustomBuild < Build
@@ -30,8 +47,8 @@ class ErlangBuild
30
47
  property :name
31
48
  property :code
32
49
 
33
- view :by_name, :key => :name, :language => :erlang
34
- view :by_name_and_code, :key => [:name, :code], :language => :erlang
50
+ view :by_name, key: :name, language: :erlang
51
+ view :by_name_and_code, key: %i[name code], language: :erlang
35
52
  end
36
53
 
37
54
  describe 'views' do
@@ -42,7 +59,7 @@ describe 'views' do
42
59
 
43
60
  context 'in erlang' do
44
61
  it 'builds views with single keys' do
45
- build = ErlangBuild.new(:name => 'erlang')
62
+ build = ErlangBuild.new(name: 'erlang')
46
63
  @db.save_document build
47
64
 
48
65
  results = @db.view(ErlangBuild.by_name('erlang'))
@@ -50,98 +67,98 @@ describe 'views' do
50
67
  end
51
68
 
52
69
  it 'does not crash couchdb when a document does not have the key' do
53
- build = {'ruby_class' => 'ErlangBuild'}
70
+ build = { 'ruby_class' => 'ErlangBuild' }
54
71
  @db.couchrest_database.save_doc build
55
72
 
56
- results = @db.view(ErlangBuild.by_name(:key => nil))
73
+ results = @db.view(ErlangBuild.by_name(key: nil))
57
74
  expect(results.size).to eq(1)
58
75
  end
59
76
 
60
77
  it 'builds views with composite keys' do
61
- build = ErlangBuild.new(:name => 'erlang', :code => '123')
78
+ build = ErlangBuild.new(name: 'erlang', code: '123')
62
79
  @db.save_document build
63
80
 
64
- results = @db.view(ErlangBuild.by_name_and_code(['erlang', '123']))
81
+ results = @db.view(ErlangBuild.by_name_and_code(%w[erlang 123]))
65
82
  expect(results).to eq([build])
66
83
  end
67
84
 
68
85
  it 'can reduce over erlang views' do
69
- build = ErlangBuild.new(:name => 'erlang')
86
+ build = ErlangBuild.new(name: 'erlang')
70
87
  @db.save_document build
71
88
 
72
- results = @db.view(ErlangBuild.by_name(:reduce => true))
89
+ results = @db.view(ErlangBuild.by_name(reduce: true))
73
90
  expect(results).to eq(1)
74
91
  end
75
92
  end
76
93
 
77
- it "should return instances of the class" do
78
- @db.save_document Build.new(:state => 'success', :time => '2008-01-01')
94
+ it 'should return instances of the class' do
95
+ @db.save_document Build.new(state: 'success', time: '2008-01-01')
79
96
  results = @db.view(Build.timeline)
80
97
  expect(results.map(&:class)).to eq([Build])
81
98
  end
82
99
 
83
- it "should return the ids if there document was not included" do
84
- build = Build.new(:state => 'success', :time => '2008-01-01')
100
+ it 'should return the ids if there document was not included' do
101
+ build = Build.new(state: 'success', time: '2008-01-01')
85
102
  @db.save_document build
86
- results = @db.view(Build.timeline(:include_docs => false))
103
+ results = @db.view(Build.timeline(include_docs: false))
87
104
  expect(results).to eq([build.id])
88
105
  end
89
106
 
90
- it "should pass the view options to the view query" do
107
+ it 'should pass the view options to the view query' do
91
108
  query = double 'query'
92
109
  allow(CouchPotato::View::ViewQuery).to receive(:new).and_return(query)
93
- expect(query).to receive(:query_view!).with(hash_including(:key => 1)).and_return('rows' => [])
94
- @db.view Build.timeline(:key => 1)
110
+ expect(query).to receive(:query_view!).with(hash_including(key: 1)).and_return('rows' => [])
111
+ @db.view Build.timeline(key: 1)
95
112
  end
96
113
 
97
114
  it "should not return documents that don't have a matching JSON.create_id" do
98
- CouchPotato.couchrest_database.save_doc({:time => 'x'})
115
+ CouchPotato.couchrest_database.save_doc({ time: 'x' })
99
116
  expect(@db.view(Build.timeline)).to eq([])
100
117
  end
101
118
 
102
- it "should count documents" do
103
- @db.save_document! Build.new(:state => 'success', :time => '2008-01-01')
104
- expect(@db.view(Build.count(:reduce => true))).to eq(1)
119
+ it 'should count documents' do
120
+ @db.save_document! Build.new(state: 'success', time: '2008-01-01')
121
+ expect(@db.view(Build.count(reduce: true))).to eq(1)
105
122
  end
106
123
 
107
- it "should count zero documents" do
108
- expect(@db.view(Build.count(:reduce => true))).to eq(0)
124
+ it 'should count zero documents' do
125
+ expect(@db.view(Build.count(reduce: true))).to eq(0)
109
126
  end
110
127
 
111
- describe "with multiple keys" do
112
- it "should return the documents with matching keys" do
113
- build = Build.new(:state => 'success', :time => '2008-01-01')
128
+ describe 'with multiple keys' do
129
+ it 'should return the documents with matching keys' do
130
+ build = Build.new(state: 'success', time: '2008-01-01')
114
131
  @db.save! build
115
- expect(@db.view(Build.timeline(:keys => ['2008-01-01']))).to eq([build])
132
+ expect(@db.view(Build.timeline(keys: ['2008-01-01']))).to eq([build])
116
133
  end
117
134
 
118
- it "should not return documents with non-matching keys" do
119
- build = Build.new(:state => 'success', :time => '2008-01-01')
135
+ it 'should not return documents with non-matching keys' do
136
+ build = Build.new(state: 'success', time: '2008-01-01')
120
137
  @db.save! build
121
- expect(@db.view(Build.timeline(:keys => ['2008-01-02']))).to be_empty
138
+ expect(@db.view(Build.timeline(keys: ['2008-01-02']))).to be_empty
122
139
  end
123
140
  end
124
141
 
125
- describe "properties defined" do
126
- it "assigns the configured properties" do
142
+ describe 'properties defined' do
143
+ it 'assigns the configured properties' do
127
144
  CouchPotato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build')
128
145
  expect(@db.view(Build.minimal_timeline).first.state).to eql('success')
129
146
  end
130
147
 
131
- it "does not assign the properties not configured" do
148
+ it 'does not assign the properties not configured' do
132
149
  CouchPotato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build')
133
150
  expect(@db.view(Build.minimal_timeline).first.time).to be_nil
134
151
  end
135
152
 
136
- it "assigns the id even if it is not configured" do
153
+ it 'assigns the id even if it is not configured' do
137
154
  id = CouchPotato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build')['id']
138
155
  expect(@db.view(Build.minimal_timeline).first._id).to eql(id)
139
156
  end
140
157
  end
141
158
 
142
- describe "no properties defined" do
143
- it "should assign all properties to the objects by default" do
144
- id = CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build'})['id']
159
+ describe 'no properties defined' do
160
+ it 'should assign all properties to the objects by default' do
161
+ id = CouchPotato.couchrest_database.save_doc({ :state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build' })['id']
145
162
  result = @db.view(Build.timeline).first
146
163
  expect(result.state).to eq('success')
147
164
  expect(result.time).to eq('2008-01-01')
@@ -149,116 +166,210 @@ describe 'views' do
149
166
  end
150
167
  end
151
168
 
152
- describe "map function given" do
153
- it "should still return instances of the class" do
154
- CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
169
+ describe 'map function given' do
170
+ it 'should still return instances of the class' do
171
+ CouchPotato.couchrest_database.save_doc({ state: 'success', time: '2008-01-01' })
155
172
  expect(@db.view(Build.custom_timeline).map(&:class)).to eq([Build])
156
173
  end
157
174
 
158
- it "should assign the properties from the value" do
159
- CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
175
+ it 'should assign the properties from the value' do
176
+ CouchPotato.couchrest_database.save_doc({ state: 'success', time: '2008-01-01' })
160
177
  expect(@db.view(Build.custom_timeline).map(&:state)).to eq(['custom_success'])
161
178
  end
162
179
 
163
- it "should assign the id" do
164
- doc = CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
180
+ it 'should assign the id' do
181
+ doc = CouchPotato.couchrest_database.save_doc({ state: 'success', time: '2008-01-01' })
165
182
  expect(@db.view(Build.custom_timeline).map(&:_id)).to eq([doc['id']])
166
183
  end
167
184
 
168
- it "should leave the other properties blank" do
169
- CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
185
+ it 'should leave the other properties blank' do
186
+ CouchPotato.couchrest_database.save_doc({ state: 'success', time: '2008-01-01' })
170
187
  expect(@db.view(Build.custom_timeline).map(&:time)).to eq([nil])
171
188
  end
172
189
 
173
- describe "that returns null documents" do
174
- it "should return instances of the class" do
175
- CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
190
+ describe 'that returns null documents' do
191
+ it 'should return instances of the class' do
192
+ CouchPotato.couchrest_database.save_doc({ state: 'success', time: '2008-01-01' })
176
193
  expect(@db.view(Build.custom_timeline_returns_docs).map(&:class)).to eq([Build])
177
194
  end
178
195
 
179
- it "should assign the properties from the value" do
180
- CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
196
+ it 'should assign the properties from the value' do
197
+ CouchPotato.couchrest_database.save_doc({ state: 'success', time: '2008-01-01' })
181
198
  expect(@db.view(Build.custom_timeline_returns_docs).map(&:state)).to eq(['success'])
182
199
  end
183
200
 
184
- it "should still return instance of class if document included JSON.create_id" do
185
- CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => "Build"})
201
+ it 'should still return instance of class if document included JSON.create_id' do
202
+ CouchPotato.couchrest_database.save_doc({ :state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build' })
186
203
  view_data = @db.view(Build.custom_timeline_returns_docs)
187
204
  expect(view_data.map(&:class)).to eq([Build])
188
205
  expect(view_data.map(&:state)).to eq(['success'])
189
206
  end
190
207
  end
191
208
 
192
- describe "additional reduce function given" do
193
- it "should still assign the id" do
209
+ describe 'additional reduce function given' do
210
+ it 'should still assign the id' do
194
211
  doc = CouchPotato.couchrest_database.save_doc({})
195
- CouchPotato.couchrest_database.save_doc({:foreign_key => doc['id']})
212
+ CouchPotato.couchrest_database.save_doc({ foreign_key: doc['id'] })
196
213
  expect(@db.view(Build.custom_with_reduce).map(&:_id)).to eq([doc['id']])
197
214
  end
198
215
 
199
- describe "when the additional reduce function is a typical count" do
200
- it "should parse the reduce count" do
216
+ describe 'when the additional reduce function is a typical count' do
217
+ it 'should parse the reduce count' do
201
218
  doc = CouchPotato.couchrest_database.save_doc({})
202
- CouchPotato.couchrest_database.save_doc({:foreign_key => doc['id']})
203
- expect(@db.view(Build.custom_count_with_reduce(:reduce => true))).to eq(2)
219
+ CouchPotato.couchrest_database.save_doc({ foreign_key: doc['id'] })
220
+ expect(@db.view(Build.custom_count_with_reduce(reduce: true))).to eq(2)
204
221
  end
205
222
  end
206
223
  end
207
224
  end
208
225
 
209
- describe "with array as key" do
210
- it "should create a map function with the composite key" do
211
- expect(CouchPotato::View::ViewQuery).to receive(:new) do |db, design_name, view, list|
226
+ describe 'with array as key' do
227
+ it 'should create a map function with the composite key' do
228
+ expect(CouchPotato::View::ViewQuery).to receive(:new) do |_db, _design_name, view, _list|
212
229
  expect(view['key_array_timeline'][:map]).to match(/emit\(\[doc\['time'\], doc\['state'\]\]/)
213
230
 
214
- double('view query', :query_view! => {'rows' => []})
231
+ double('view query', query_view!: { 'rows' => [] })
215
232
  end
216
233
  @db.view Build.key_array_timeline
217
234
  end
218
235
  end
219
236
 
220
- describe "raw view" do
221
- it "should return the raw data" do
222
- @db.save_document Build.new(:state => 'success', :time => '2008-01-01')
237
+ describe 'raw view' do
238
+ it 'should return the raw data' do
239
+ @db.save_document Build.new(state: 'success', time: '2008-01-01')
223
240
  expect(@db.view(Build.raw)['rows'][0]['value']).to eq('success')
224
241
  end
225
242
 
226
- it "should return filtred raw data" do
227
- @db.save_document Build.new(:state => 'success', :time => '2008-01-01')
243
+ it 'should return filtred raw data' do
244
+ @db.save_document Build.new(state: 'success', time: '2008-01-01')
228
245
  expect(@db.view(Build.filtered_raw)).to eq(['success'])
229
246
  end
230
247
 
231
- it "should pass view options declared in the view declaration to the query" do
232
- view_query = double 'view_query'
233
- allow(CouchPotato::View::ViewQuery).to receive(:new).and_return(view_query)
234
- expect(view_query).to receive(:query_view!).with(hash_including(:group => true)).and_return({'rows' => []})
235
- @db.view(Build.with_view_options)
248
+ it 'should pass view options declared in the view declaration to the query' do
249
+ view_query = double 'view_query'
250
+ allow(CouchPotato::View::ViewQuery).to receive(:new).and_return(view_query)
251
+ expect(view_query).to receive(:query_view!).with(hash_including(group: true)).and_return({ 'rows' => [] })
252
+ @db.view(Build.with_view_options)
253
+ end
254
+ end
255
+
256
+ describe 'flex view' do
257
+ it 'supports a given key' do
258
+ @db.save_document Build.new(id: 'b1', state: 'success', time: '2008-01-01')
259
+ @db.save_document Build.new(state: 'success', time: '2008-01-02')
260
+
261
+ rows = @db.view(Build.flex_with_key('2008-01-01')).raw['rows']
262
+
263
+ expect(rows.map { |row| row['id'] }).to eq(['b1'])
264
+ end
265
+
266
+ it 'supports a given map function' do
267
+ @db.save_document Build.new(id: 'b1', time: '2008-01-01')
268
+ @db.save_document Build.new(time: '2008-01-02')
269
+
270
+ rows = @db.view(Build.flex_with_custom('2008-01-01')).raw['rows']
271
+
272
+ expect(rows.map { |row| row['id'] }).to eq(['b1'])
273
+ end
274
+
275
+ it 'supports a given reduce function' do
276
+ @db.save_document Build.new(time: '2008-01-01')
277
+ @db.save_document Build.new(time: '2008-01-02')
278
+
279
+ raw = @db.view(Build.flex_with_custom(reduce: true)).raw
280
+
281
+ expect(raw['rows'][0]['value']).to eq(2)
282
+ end
283
+
284
+ it 'returns ids' do
285
+ @db.save_document Build.new(id: 'b1')
286
+ @db.save_document Build.new(id: 'b2')
287
+
288
+ ids = @db.view(Build.flex_with_key).ids
289
+
290
+ expect(ids).to eq(%w[b1 b2])
291
+ end
292
+
293
+ it 'returns keys' do
294
+ @db.save_document Build.new(time: '1')
295
+ @db.save_document Build.new(time: '2')
296
+
297
+ ids = @db.view(Build.flex_with_key).keys
298
+
299
+ expect(ids).to eq(%w[1 2])
300
+ end
301
+
302
+ it 'returns values' do
303
+ @db.save_document Build.new(time: '1')
304
+ @db.save_document Build.new(time: '2')
305
+
306
+ ids = @db.view(Build.flex_with_key).values
307
+
308
+ expect(ids).to eq([1, 1])
309
+ end
310
+
311
+ it 'returns docs' do
312
+ @db.save_document Build.new(time: '1')
313
+
314
+ docs = @db.view(Build.flex_with_key(include_docs: true)).docs
315
+
316
+ expect(docs.map(&:time)).to eq(['1'])
317
+ expect(docs.first.database).to eq(@db)
318
+ end
319
+
320
+ it 'returns the value of reduce' do
321
+ @db.save_document Build.new(time: '1')
322
+ @db.save_document Build.new(time: '1')
323
+
324
+ value = @db.view(Build.flex_with_key(reduce: true)).reduce_value
325
+
326
+ expect(value).to eq(2)
327
+ end
328
+
329
+ it 'returns the raw results' do
330
+ @db.save_document Build.new(id: 'b1', time: '1')
331
+
332
+ raw = @db.view(Build.flex_with_key).raw
333
+
334
+ expect(raw).to eq(
335
+ 'total_rows' => 1, 'offset' => 0,
336
+ 'rows' => [{ 'id' => 'b1', 'key' => '1', 'value' => 1 }]
337
+ )
338
+ end
339
+
340
+ it 'returns the results via a custom method' do
341
+ @db.save_document Build.new(id: 'b1', time: '100')
342
+ @db.save_document Build.new(id: 'b2', time: '200')
343
+
344
+ custom = @db.view(Build.flex_with_key(include_docs: true)).times_to_int
345
+
346
+ expect(custom).to eq([100, 200])
236
347
  end
237
348
  end
238
349
 
239
- describe "inherited views" do
240
- it "should support parent views for objects of the subclass" do
241
- @db.save_document CustomBuild.new(:state => 'success', :time => '2008-01-01')
350
+ describe 'inherited views' do
351
+ it 'should support parent views for objects of the subclass' do
352
+ @db.save_document CustomBuild.new(state: 'success', time: '2008-01-01')
242
353
  expect(@db.view(CustomBuild.timeline).size).to eq(1)
243
354
  expect(@db.view(CustomBuild.timeline).first).to be_kind_of(CustomBuild)
244
355
  end
245
356
 
246
- it "should return instances of subclasses as well if a special view exists" do
247
- @db.save_document Build.new(:state => 'success', :time => '2008-01-01')
248
- @db.save_document CustomBuild.new(:state => 'success', :time => '2008-01-01', :server => 'Jenkins')
357
+ it 'should return instances of subclasses as well if a special view exists' do
358
+ @db.save_document Build.new(state: 'success', time: '2008-01-01')
359
+ @db.save_document CustomBuild.new(state: 'success', time: '2008-01-01', server: 'Jenkins')
249
360
  results = @db.view(Build.all)
250
361
  expect(results.map(&:class)).to eq([CustomBuild, Build])
251
362
  end
252
363
  end
253
364
 
254
- describe "list functions" do
365
+ describe 'list functions' do
255
366
  class Coworker
256
367
  include CouchPotato::Persistence
257
368
 
258
369
  property :name
259
370
 
260
- view :all_with_list, :key => :name, :list => :append_doe
261
- view :all, :key => :name
371
+ view :all_with_list, key: :name, list: :append_doe
372
+ view :all, key: :name
262
373
 
263
374
  list :append_doe, <<-JS
264
375
  function(head, req) {
@@ -273,14 +384,14 @@ describe 'views' do
273
384
  JS
274
385
  end
275
386
 
276
- it "should use the list function declared at class level" do
277
- @db.save! Coworker.new(:name => 'joe')
387
+ it 'should use the list function declared at class level' do
388
+ @db.save! Coworker.new(name: 'joe')
278
389
  expect(@db.view(Coworker.all_with_list).first.name).to eq('joe doe')
279
390
  end
280
391
 
281
- it "should use the list function passed at runtime" do
282
- @db.save! Coworker.new(:name => 'joe')
283
- expect(@db.view(Coworker.all(:list => :append_doe)).first.name).to eq('joe doe')
392
+ it 'should use the list function passed at runtime' do
393
+ @db.save! Coworker.new(name: 'joe')
394
+ expect(@db.view(Coworker.all(list: :append_doe)).first.name).to eq('joe doe')
284
395
  end
285
396
  end
286
397
 
@@ -291,7 +402,7 @@ describe 'views' do
291
402
  @db.view(Build.timeline)
292
403
  @db.destroy build
293
404
 
294
- expect(@db.view(Build.timeline(:stale => 'ok'))).to be_empty
405
+ expect(@db.view(Build.timeline(stale: 'ok'))).to be_empty
295
406
  end
296
407
  end
297
408
  end