couch_view 0.0.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.
@@ -0,0 +1,28 @@
1
+ Feature: All subsets of an array
2
+
3
+
4
+ Scenario: Generating all subsets of an array
5
+
6
+ Given an array of 3 elements:
7
+ """
8
+ @array = [1,2,3]
9
+ """
10
+
11
+ When I call the "all_combinations" method on it:
12
+ """
13
+ @result = @array.all_combinations
14
+ """
15
+
16
+ Then I should receive all of the subsets of my array:
17
+ """
18
+ @result.should == [
19
+ [],
20
+ [1],
21
+ [2],
22
+ [3],
23
+ [1,2],
24
+ [1,3],
25
+ [2,3],
26
+ [1,2,3]
27
+ ]
28
+ """
@@ -0,0 +1,396 @@
1
+ Feature: CouchView::Config
2
+
3
+ A `CouchView::Config` consists of the following data:
4
+ * a CouchView::Map
5
+ * the conditions (if any) with which to extend that map
6
+ * the names of the views
7
+ * the reduce for the view
8
+ * the base name of the view
9
+
10
+ When you instantiate a CouchView::Config, you must provide both the model and the properties to map over:
11
+
12
+ class Article < CouchRest::Model::Base; end
13
+
14
+ config = CouchView::Config.new Article
15
+ config.map :prop1, :prop2
16
+ config.view_names #==> ["by_prop1_and_prop2"]
17
+ config.base_view_name #==> "by_prop1_and_prop2"
18
+ config.model #==> Article
19
+
20
+ You could, alternatively, provide it with a model and a CouchView::Map class:
21
+
22
+ class ById; include CouchView::Map; end
23
+ config = CouchView::Config.new Article
24
+ config.map ById
25
+ config.view_names #==> ["by_id"]
26
+ config.model #==> Article
27
+
28
+ By default, a `CouchView::Config` will assume a reduce of `_count`:
29
+
30
+ config.reduce #==> "_count"
31
+
32
+ You can override this by supplying your own reduce:
33
+
34
+ config.reduce "function(key, values){}"
35
+ config.reduce #==> "function(key, values){}"
36
+
37
+ The base name of your view is generated by the parameters passed to the "map" method.
38
+
39
+ If you want to override that, you can pass a new name to the "base_view_name" method:
40
+
41
+ config.base_view_name "funny"
42
+
43
+ Lastly, you can provide your `CouchView::Config` with conditions:
44
+
45
+ module Published
46
+ def conditions
47
+ "#{super} && doc.published == true"
48
+ end
49
+ end
50
+
51
+ config.conditions Published
52
+ config.conditions #==> [Published]
53
+
54
+ You could also add conditions by passing a block to the map method:
55
+
56
+ config.map :label do
57
+ conditions Published
58
+ end
59
+
60
+ You can now ask your `Config` to return a hash containing all of the views (with their corresponding map/reduce) that you should setup on your model:
61
+
62
+ class Article < CouchRest::Model::Base
63
+ property :label
64
+ property :published, TrueClass, :default => false
65
+ end
66
+
67
+ config = CouchView::Config.new Article
68
+ config.map :label
69
+
70
+ module Published
71
+ def conditions
72
+ "#{super} && doc.published == true"
73
+ end
74
+ end
75
+
76
+ config.conditions Published
77
+
78
+ config.views #==>
79
+ {
80
+ "by_label" => {
81
+ "map" => "function....",
82
+ "reduce" => "_count"
83
+ },
84
+ "by_label_published" => {
85
+ "map" => "function....",
86
+ "reduce" => "_count"
87
+ }
88
+ }
89
+
90
+
91
+ @db
92
+ Scenario: Generating a name based on the properties passed in to map over
93
+
94
+ Given the following model:
95
+ """
96
+ class Article < CouchRest::Model::Base
97
+ include CouchView
98
+ property :label
99
+ property :name
100
+ end
101
+ """
102
+
103
+ When I create a CouchView::Config to map over Article labels:
104
+ """
105
+ @config = CouchView::Config.new Article
106
+ @config.map :label
107
+ """
108
+
109
+ Then the config should report "by_label" as a view name:
110
+ """
111
+ @config.view_names.should == ["by_label"]
112
+ """
113
+
114
+ When I create a CouchView::Config to map over Article labels and names:
115
+ """
116
+ @config = CouchView::Config.new Article
117
+ @config.map :label, :name
118
+ """
119
+
120
+ Then the config should report "by_label" as a view name:
121
+ """
122
+ @config.view_names.should == ["by_label_and_name"]
123
+ """
124
+
125
+ When I create a CouchView::Map `ById`:
126
+ """
127
+ class ById
128
+ include CouchView::Map
129
+ end
130
+ """
131
+
132
+ And I create a CouchView::Config to map over article documents `ById`:
133
+ """
134
+ @config = CouchView::Config.new Article
135
+ @config.map ById
136
+ """
137
+
138
+ Then the config should report "by_id" as a view name:
139
+ """
140
+ @config.view_names.should == ["by_id"]
141
+ """
142
+
143
+
144
+ @db
145
+ Scenario: Setting a custom reduce
146
+
147
+ Given the following model:
148
+ """
149
+ class Article < CouchRest::Model::Base
150
+ include CouchView
151
+ property :label
152
+ property :name
153
+ end
154
+ """
155
+
156
+ When I create a CouchView::Config to map over Article labels:
157
+ """
158
+ @config = CouchView::Config.new Article
159
+ @config.map :label
160
+ """
161
+
162
+ Then the config should default the "reduce" to "_count":
163
+ """
164
+ @config.reduce.should == "_count"
165
+ """
166
+
167
+ When I change the "reduce" to a javascript function:
168
+ """
169
+ @config.reduce "function(key, values){}"
170
+ """
171
+
172
+ Then the config should return that function for the "reduce":
173
+ """
174
+ @config.reduce.should == "function(key, values){}"
175
+ """
176
+
177
+
178
+ @db
179
+ Scenario: Setting conditions on the view
180
+
181
+ Given the following model:
182
+ """
183
+ class Article < CouchRest::Model::Base
184
+ include CouchView
185
+ property :label
186
+ property :name
187
+ end
188
+ """
189
+
190
+ And the following conditions:
191
+ """
192
+ module Published
193
+ def conditions
194
+ "#{super} && doc.published == true"
195
+ end
196
+ end
197
+
198
+ module Visible
199
+ def conditions
200
+ "#{super} && doc.visible == true"
201
+ end
202
+ end
203
+ """
204
+
205
+ When I create a CouchView::Config to map over Article labels:
206
+ """
207
+ @config = CouchView::Config.new Article
208
+ @config.map :label
209
+ """
210
+
211
+ And I add the Published and Visible conditions to it:
212
+ """
213
+ @config.conditions Published, Visible
214
+ """
215
+
216
+ Then the conditions should be Published and Visible:
217
+ """
218
+ @config.conditions.should == [Published, Visible]
219
+ """
220
+
221
+ And the view names should include views for published, visible, and published/visible documents:
222
+ """
223
+ @config.view_names.sort.should == ["by_label", "by_label_published", "by_label_published_visible", "by_label_visible"]
224
+ """
225
+
226
+
227
+ @db @focus
228
+ Scenario: Creating conditions by passing a block to the map
229
+
230
+ Given the following model:
231
+ """
232
+ class Article < CouchRest::Model::Base; end
233
+ """
234
+
235
+ And the following conditions:
236
+ """
237
+ module Published
238
+ def conditions
239
+ "#{super} && doc.published == true"
240
+ end
241
+ end
242
+
243
+ module Visible
244
+ def conditions
245
+ "#{super} && doc.visible == true"
246
+ end
247
+ end
248
+ """
249
+
250
+ When I create a view config and specify conditions in a block on the `map` method:
251
+ """
252
+ @config = CouchView::Config.new Article
253
+ @config.map :label do
254
+ conditions Published, Visible
255
+ end
256
+ """
257
+
258
+ Then the conditions method on my config should return the conditions specified:
259
+ """
260
+ @config.conditions.should == [Published, Visible]
261
+ """
262
+
263
+ @db
264
+ Scenario: Getting all the views
265
+
266
+ Given the following model:
267
+ """
268
+ class Article < CouchRest::Model::Base
269
+ include CouchView
270
+ property :label
271
+ property :name
272
+ end
273
+ """
274
+
275
+ And the following conditions:
276
+ """
277
+ module Published
278
+ def conditions
279
+ "#{super} && doc.published == true"
280
+ end
281
+ end
282
+
283
+ module Visible
284
+ def conditions
285
+ "#{super} && doc.visible == true"
286
+ end
287
+ end
288
+ """
289
+
290
+ When I create a CouchView::Config to map over Article labels:
291
+ """
292
+ @config = CouchView::Config.new Article
293
+ @config.map :label
294
+ """
295
+
296
+ And I add the Published and Visible conditions to it:
297
+ """
298
+ @config.conditions Published, Visible
299
+ """
300
+
301
+ Then the views should include a "by_label" view:
302
+ """
303
+ @config.views[:by_label][:map].should ==
304
+ "
305
+ function(doc){
306
+ if (doc['couchrest-type'] == 'Article')
307
+ emit(doc.label, null)
308
+ }
309
+ "
310
+ @config.views[:by_label][:reduce].should == "_count"
311
+ """
312
+
313
+ And the views should include a "by_label_published" view:
314
+ """
315
+ @config.views[:by_label_published][:map].should ==
316
+ "
317
+ function(doc){
318
+ if (doc['couchrest-type'] == 'Article' && doc.published == true)
319
+ emit(doc.label, null)
320
+ }
321
+ "
322
+ @config.views[:by_label_published][:reduce].should == "_count"
323
+ """
324
+
325
+ And the views should include a "by_label_visible" view:
326
+ """
327
+ @config.views[:by_label_visible][:map].should ==
328
+ "
329
+ function(doc){
330
+ if (doc['couchrest-type'] == 'Article' && doc.visible == true)
331
+ emit(doc.label, null)
332
+ }
333
+ "
334
+ @config.views[:by_label_visible][:reduce].should == "_count"
335
+ """
336
+
337
+ And the views should include a "by_label_published_visible" view:
338
+ """
339
+ @config.views[:by_label_published_visible][:map].should ==
340
+ "
341
+ function(doc){
342
+ if (doc['couchrest-type'] == 'Article' && doc.published == true && doc.visible == true)
343
+ emit(doc.label, null)
344
+ }
345
+ "
346
+ @config.views[:by_label_published_visible][:reduce].should == "_count"
347
+ """
348
+
349
+
350
+ @db @focus
351
+ Scenario: Giving your view a custom base name
352
+
353
+ Given the following model:
354
+ """
355
+ class Article < CouchRest::Model::Base
356
+ include CouchView
357
+ property :label
358
+ property :name
359
+ end
360
+ """
361
+
362
+ And the following conditions:
363
+ """
364
+ module Published
365
+ def conditions
366
+ "#{super} && doc.published == true"
367
+ end
368
+ end
369
+
370
+ module Visible
371
+ def conditions
372
+ "#{super} && doc.visible == true"
373
+ end
374
+ end
375
+ """
376
+
377
+ When I create a CouchView::Config to map over Article labels:
378
+ """
379
+ @config = CouchView::Config.new Article
380
+ @config.map :label
381
+ """
382
+
383
+ And I give it a base name of "funny":
384
+ """
385
+ @config.base_view_name "funny"
386
+ """
387
+
388
+ And I add the Published and Visible conditions to it:
389
+ """
390
+ @config.conditions Published, Visible
391
+ """
392
+
393
+ Then the view names should include "funny" views for published, visible, and published/visible documents:
394
+ """
395
+ @config.view_names.sort.should == ["funny", "funny_published", "funny_published_visible", "funny_visible"]
396
+ """
@@ -0,0 +1,284 @@
1
+ Feature: CouchView::Count::Proxy
2
+
3
+ A `CouchView::Count::Proxy` is like a regular `CouchView::Proxy`, except that it defaults the CouchDB "reduce" option to "true".
4
+
5
+ proxy = CouchView::Count::Proxy SomeModel, :some_view
6
+ proxy._options #==> { "reduce" => true }
7
+
8
+ Another noticable difference: you can't call ".each" on the proxy unless you've set the "group" option to "true":
9
+
10
+ proxy = CouchView::Count::Proxy SomeModel, :some_view
11
+
12
+ proxy.each {...} #==> raises an exception "You can't call 'each' on a count proxy that doesn't set 'group' to 'true'."
13
+
14
+ If you've set "group" to true, then when you call ".each" on it, you will iterate over the key/value pairs in the response:
15
+
16
+ proxy.group(true).each do |key, value|
17
+ ...
18
+ end
19
+
20
+ If you'd rather work with the raw (hash) response on a group level query, simply use the "get!" method:
21
+
22
+ proxy.get!
23
+
24
+ If you're not running a group level query, then "get!" will return the actual count (`Fixnum`):
25
+
26
+ proxy = CouchView::Count::Proxy SomeModel, :some_view
27
+ proxy.get! #==> 0, or some other positive integer
28
+
29
+
30
+ @db
31
+ Scenario: Options default to "reduce" => true
32
+
33
+ Given an Article model with a view "by_id":
34
+ """
35
+ class Article < CouchRest::Model::Base
36
+ view_by :id
37
+ end
38
+ """
39
+
40
+ When I instantiate a new CouchView::Map::Proxy with "Article" and ":by_id":
41
+ """
42
+ @proxy = CouchView::Count::Proxy.new Article, :by_id
43
+ """
44
+
45
+ Then the options on the proxy should default to "reduce" => true
46
+ """
47
+ @proxy._options.should == {:reduce => true}
48
+ """
49
+
50
+
51
+ @db
52
+ Scenario: ".each" should iterate over the key/value pairs of the 'rows' result:
53
+
54
+ Given the following map definition:
55
+ """
56
+ class ByLabel
57
+ include CouchView::Map
58
+
59
+ def map
60
+ "
61
+ function(doc){
62
+ if (#{conditions})
63
+ emit(doc.label, null)
64
+ }
65
+ "
66
+ end
67
+ end
68
+ """
69
+
70
+ And an Article model that maps ByLabel:
71
+ """
72
+ class Article < CouchRest::Model::Base
73
+ include CouchView
74
+ map ByLabel
75
+ end
76
+ """
77
+
78
+ And several articles:
79
+ """
80
+ Article.create :label => "fun-article"
81
+ Article.create :label => "awesome-article"
82
+ Article.create :label => "awesome-article"
83
+ """
84
+
85
+ When I instantiate a new CouchView::Map::Proxy with "Article" and ":by_id":
86
+ """
87
+ @proxy = CouchView::Count::Proxy.new Article, :by_by_label
88
+ """
89
+
90
+ And I set the CouchDB "group" query option to "true":
91
+ """
92
+ @proxy.group! true
93
+ """
94
+
95
+ Then the ".each" method should iterate over the keys and values in the 'rows' array in the query response:
96
+ """
97
+ @results = []
98
+ @proxy.each {|label, count| @results << {label => count}}
99
+ @results.first.should == {"awesome-article" => 2}
100
+ @results.last.should == {"fun-article" => 1}
101
+ @results.count.should == 2
102
+ """
103
+
104
+
105
+ @db @focus
106
+ Scenario: ".get!" should return 0 when the reduce returns an empty 'rows' response:
107
+
108
+ Given the following map definition:
109
+ """
110
+ class ByLabel
111
+ include CouchView::Map
112
+
113
+ def map
114
+ "
115
+ function(doc){
116
+ if (#{conditions})
117
+ emit(doc.label, null)
118
+ }
119
+ "
120
+ end
121
+ end
122
+ """
123
+
124
+ And an Article model that maps ByLabel:
125
+ """
126
+ class Article < CouchRest::Model::Base
127
+ include CouchView
128
+ map ByLabel
129
+ end
130
+ """
131
+
132
+ And there are no articles:
133
+ """
134
+ Article.all.count.should == 0
135
+ """
136
+
137
+ When I instantiate a new CouchView::Map::Proxy with "Article" and ":by_id":
138
+ """
139
+ @proxy = CouchView::Count::Proxy.new Article, :by_by_label
140
+ """
141
+
142
+ Then the ".get!" method should return 0:
143
+ """
144
+ @proxy.get!.should == 0
145
+ """
146
+
147
+
148
+ @db @focus
149
+ Scenario: ".get!" should return the 'value' of the first row of the 'rows' reduce response:
150
+
151
+ Given the following map definition:
152
+ """
153
+ class ById
154
+ include CouchView::Map
155
+ end
156
+ """
157
+
158
+ And an Article model that maps ByLabel:
159
+ """
160
+ class Article < CouchRest::Model::Base
161
+ include CouchView
162
+ map ById
163
+ end
164
+ """
165
+
166
+ And there are 2 articles:
167
+ """
168
+ 2.times { Article.create }
169
+ """
170
+
171
+ When I instantiate a new CouchView::Map::Proxy with "Article" and ":by_by_id":
172
+ """
173
+ @proxy = CouchView::Count::Proxy.new Article, :by_by_id
174
+ """
175
+
176
+ Then the ".get!" method should return 2:
177
+ """
178
+ @proxy.get!.should == 2
179
+ """
180
+
181
+
182
+
183
+ @db @focus
184
+ Scenario: ".get!" should return the raw JSON (hash) response from CouchDB for a group query:
185
+
186
+ Given the following map definition:
187
+ """
188
+ class ByLabel
189
+ include CouchView::Map
190
+
191
+ def map
192
+ "
193
+ function(doc){
194
+ if (#{conditions})
195
+ emit(doc.label, null)
196
+ }
197
+ "
198
+ end
199
+ end
200
+ """
201
+
202
+ And an Article model that maps ByLabel:
203
+ """
204
+ class Article < CouchRest::Model::Base
205
+ include CouchView
206
+ map ByLabel
207
+ end
208
+ """
209
+
210
+ And several articles:
211
+ """
212
+ Article.create :label => "fun-article"
213
+ Article.create :label => "awesome-article"
214
+ Article.create :label => "awesome-article"
215
+ """
216
+
217
+ When I instantiate a new CouchView::Map::Proxy with "Article" and ":by_id":
218
+ """
219
+ @proxy = CouchView::Count::Proxy.new Article, :by_by_label
220
+ """
221
+
222
+ And I set the CouchDB "group" query option to "true":
223
+ """
224
+ @proxy.group! true
225
+ """
226
+
227
+ Then the ".get!" method should return the raw JSON response from CouchDB as a hash:
228
+ """
229
+ @proxy.get!.should == {
230
+ "rows" => [
231
+ { "key" => "awesome-article", "value" => 2 },
232
+ { "key" => "fun-article", "value" => 1 }
233
+ ]
234
+ }
235
+ """
236
+
237
+
238
+ @db
239
+ Scenario: You can't call ".each" on a count proxy unless the CouchDB "group" option is set to true
240
+
241
+ Given the following map definition:
242
+ """
243
+ class ByLabel
244
+ include CouchView::Map
245
+
246
+ def map
247
+ "
248
+ function(doc){
249
+ if (#{conditions})
250
+ emit(doc.label, null)
251
+ }
252
+ "
253
+ end
254
+ end
255
+ """
256
+
257
+ And an Article model that maps ByLabel:
258
+ """
259
+ class Article < CouchRest::Model::Base
260
+ include CouchView
261
+ map ByLabel
262
+ end
263
+ """
264
+
265
+ When I instantiate a new CouchView::Map::Proxy with "Article" and ":by_label":
266
+ """
267
+ @proxy = CouchView::Count::Proxy.new Article, :by_by_label
268
+ """
269
+
270
+ Then ".each" should raise an exception:
271
+ """
272
+ proc { @proxy.each {|k,v|} }.should raise_exception("You can't call 'each' on a count proxy that doesn't set 'group' to 'true'.")
273
+ """
274
+
275
+ When I set the CouchDB "group" query option to "true":
276
+ """
277
+ @proxy.group!(true)
278
+ """
279
+
280
+ Then ".each" should not raise an exception:
281
+ """
282
+ proc { @proxy.each {|k,v|} }.should_not raise_exception
283
+ """
284
+