couch_view 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+