couch_view 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/readme.markdown ADDED
@@ -0,0 +1,307 @@
1
+ # CouchView
2
+
3
+ A decorator library for generating CouchDB view (map/reduce) functions for CouchRest::Model models.
4
+
5
+ ## Why?
6
+
7
+ CouchView makes your views modular, decouples them from your model, and also makes it easier to unit test them.
8
+
9
+ ## Requirements?
10
+
11
+ This gem integrates with the `couchrest_model` gem (version ~> 1.0.0)
12
+
13
+ ## Installation
14
+
15
+ Install it as a gem:
16
+
17
+ ```sh
18
+ $ gem install couch_view
19
+ ```
20
+
21
+ ## Your first view
22
+
23
+ Let's imagine that we want to create a map on our model that includes all of the document labels (human-readable ids):
24
+
25
+ We can start by simply calling the "map" method on our model, passing in the :label property:
26
+
27
+ ```ruby
28
+ class Article < CouchRest::Model::Base
29
+ include CouchView
30
+
31
+ property :label
32
+
33
+ map :label
34
+ end
35
+ ```
36
+
37
+ This will generate a javascript map function that looks like this:
38
+
39
+ ```javascript
40
+ function(doc){
41
+ if (doc['couchrest-type'] == 'Article')
42
+ emit(doc.label, null)
43
+ }
44
+ ```
45
+
46
+ We can query this map by using the "map_by_label!" method:
47
+
48
+ ```ruby
49
+ Article.map_by_label! #==> all of the articles, in order of label
50
+ ```
51
+
52
+ You can use any of the standard CouchDB query options on this view; see the section "Query Proxy" for more information.
53
+
54
+ We can also count all of the articles in the "by_label" map using the `count_by_label!` method:
55
+
56
+ ```ruby
57
+ Article.count_by_label! #==> 0, assuming we haven't created any articles
58
+ ```
59
+
60
+ Next, let's imagine that we'd like to create a view with a compound key: let's index all of the articles in the system by author and created_at:
61
+
62
+ ```ruby
63
+ class Article < CouchRest::Model::Base
64
+ include CouchView
65
+ property :author
66
+ timestamps!
67
+
68
+ map :author, :created_at
69
+ end
70
+ ```
71
+
72
+ And it's as simple as that. `CouchView` generated the following javascript map function:
73
+
74
+ ```javascript
75
+ function(doc){
76
+ if (doc['couchrest-type'] == 'Article')
77
+ emit([doc.author, doc.created_at], null)
78
+ }
79
+ ```
80
+
81
+ We can now iterate over the map using the `map_by_author_and_created_at!` method, and we can count it using the `count_by_author_and_created_at!` method.
82
+
83
+ ## Complex views
84
+
85
+ But what if you need to create a more complex view? `CouchView` is your friend here. Let's imagine that we want to index all of the articles by the number of comments they've received.
86
+
87
+ We'll start by creating a map class:
88
+
89
+ ```ruby
90
+ class ByCommentCount
91
+ include CouchView::Map
92
+
93
+ def map
94
+ <<-JS
95
+ function(doc){
96
+ if (#{conditions})
97
+ emit(doc.comments.length, null)
98
+ }
99
+ JS
100
+ end
101
+ end
102
+ ```
103
+
104
+ Notice that we call the "conditions" method inside of our map. This method is mixed into our class by the `CouchView::Map` module. You'll learn how this method allows us to decorate our views with conditions in the next section. For now, let's see what happens if we simply instantiate this class and call the "map" method on it:
105
+
106
+ ```ruby
107
+ ByCommentCount.new.map #==>
108
+ "
109
+ function(doc){
110
+ emit(doc.comments.length, null)
111
+ }
112
+ "
113
+ ```
114
+
115
+ Well that's not very interesting... However, what if we use this map in a model?
116
+
117
+ ```ruby
118
+ class Article < CouchRest::Model::Base
119
+ include CouchView
120
+ property :comments, [String]
121
+ map ByCommentCount
122
+ end
123
+ ```
124
+
125
+ Now, CouchView will generate a map function on our Article design document that will look like this:
126
+
127
+ ```javascript
128
+ function(doc){
129
+ if (doc['couchrest-type'] == 'Article')
130
+ emit(doc.comments.length, null)
131
+ }
132
+ ```
133
+
134
+ Similar to before, we can query this index with `map_by_comment_count!` and `count_by_comment_count!`.
135
+
136
+
137
+ ## Decorating maps with conditions
138
+
139
+ Next, let's imagine that sometimes we'd like to constrain our Article views to published articles, "web exclusive" articles, and a mixture of the two. Imagine our model was defined like this:
140
+
141
+ To start, let's define the following condition modules:
142
+
143
+ ```ruby
144
+ module WebExclusive
145
+ def conditions
146
+ "#{super} && doc.web_exclusive == true"
147
+ end
148
+ end
149
+
150
+ module Published
151
+ def conditions
152
+ "#{super} && doc.published == true"
153
+ end
154
+ end
155
+ ```
156
+
157
+ We can then add them into our map definitions thusly:
158
+
159
+ ```ruby
160
+ class Article < CouchRest::Model::Base
161
+ include CouchView
162
+
163
+ property :label
164
+ property :web_exclusive, TrueClass, :default => false
165
+ property :published, TrueClass, :default => false
166
+
167
+ map :label do
168
+ conditions WebExclusive, Published
169
+ end
170
+ end
171
+ ```
172
+
173
+ Now, we can constrain our `map_by_label` query proxy to consider only web exlusive articles, published articles, or both:
174
+
175
+ ```ruby
176
+ Article.map_by_label.published.get!
177
+ #==> published articles ordered by label
178
+
179
+ Article.map_by_label.web_exclusive.get!
180
+ #==> web exclusive articles ordered by label
181
+
182
+ Article.map_by_label.published.web_exclusive.get!
183
+ #==> published, web exclusive articles ordered by label
184
+ ```
185
+
186
+
187
+ ## Query Proxy
188
+
189
+ CouchView includes a simple query proxy system for building your map/reduce queries.
190
+
191
+ Given the following model:
192
+
193
+ ```ruby
194
+ class Article < CouchRest::Model::Base
195
+ include CouchView
196
+ map :label
197
+ end
198
+ ```
199
+
200
+ When you call `map_by_label`, you'll recieve a proxy for the query you want to run:
201
+
202
+ ```ruby
203
+ proxy = Article.map_by_label
204
+ ```
205
+
206
+ You can now begin modifying your proxy with the standard CouchDB query options. For example, suppose we'd like to limit our result to 10. We can call either the `limit` method, or the `limit!` method. The former will return a new proxy, and leave the original untouched. The latter will modify the proxy it was called on.
207
+
208
+ ```ruby
209
+ # generate a new proxy
210
+ new_proxy = proxy.limit 10
211
+
212
+ # or update the existing property
213
+ proxy.limit!(10)
214
+ ```
215
+
216
+ Here's the full list of CouchDB query parameters that `CouchView` supports:
217
+
218
+ ```ruby
219
+ limit
220
+ skip
221
+ startkey
222
+ endkey
223
+ startkey_docid
224
+ endkey_docid
225
+ stale
226
+ descending
227
+ group
228
+ group_level
229
+ reduce
230
+ include_docs
231
+ update_seq
232
+ ```
233
+
234
+ Your query will execute when you call `each` or `get!` on it:
235
+
236
+ ```ruby
237
+ Article.map_by_label.startkey!("a").endkey!("b").each do |articles|
238
+ # do something with the articles
239
+ end
240
+
241
+ # or...
242
+
243
+ articles = Article.map_by_label.startkey!("a").endkey!("b").get!
244
+ ```
245
+
246
+ You can also make your `map_by_label` and `count_by_label` return immediately by adding an `!` onto the end:
247
+
248
+ ```ruby
249
+ Article.map_by_label!
250
+ #==> [Article<#848283>,...]
251
+
252
+ Article.count_by_label!
253
+ #==> 5
254
+ ```
255
+
256
+ ## Arbitrary Reduce
257
+
258
+ You can add any arbitrary reduce onto your view by using the `reduce` class method. Just make sure to group it with a map by placing them both with a `view` block:
259
+
260
+ ```ruby
261
+ class Article < CouchRest::Model::Base
262
+ include CouchView
263
+
264
+ property :label
265
+
266
+ view do
267
+ map :label
268
+ reduce <<-JS
269
+ function(key, values){
270
+ return sum(values)
271
+ }
272
+ JS
273
+ end
274
+ end
275
+ ```
276
+
277
+ And now you can call it with:
278
+
279
+ ```ruby
280
+ Article.reduce_by_label.get!
281
+ ```
282
+
283
+ Note that you can still call `map_by_label` as well. You can't, however, call `count_by_label`.
284
+
285
+
286
+ ## Custom Names
287
+
288
+ As you've seen, `CouchView` will generate names for your views based on the properties being mapped (or based on the name of the `CouchView::Map` class passed to `map`).
289
+
290
+ You can override this default name by passing a name to the `view` method:
291
+
292
+ ```ruby
293
+ class Article < CouchRest::Model::Base
294
+ include CouchView
295
+
296
+ view :over_label do
297
+ map :label
298
+ end
299
+ end
300
+ ```
301
+
302
+ You can now call your view using the `map_over_label` and `count_over_label` methods:
303
+
304
+ ```ruby
305
+ Article.map_over_label!
306
+ Article.count_over_label!
307
+ ```
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: couch_view
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Matt Parker
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-15 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: couchrest
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - "="
27
+ - !ruby/object:Gem::Version
28
+ hash: 21
29
+ segments:
30
+ - 1
31
+ - 0
32
+ - 1
33
+ version: 1.0.1
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: couchrest_model
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 23
45
+ segments:
46
+ - 1
47
+ - 0
48
+ - 0
49
+ version: 1.0.0
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: cucumber
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ type: :development
65
+ version_requirements: *id003
66
+ - !ruby/object:Gem::Dependency
67
+ name: rspec
68
+ prerelease: false
69
+ requirement: &id004 !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 3
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ type: :development
79
+ version_requirements: *id004
80
+ - !ruby/object:Gem::Dependency
81
+ name: couchrest_model_config
82
+ prerelease: false
83
+ requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ type: :development
93
+ version_requirements: *id005
94
+ description: Modular, de-coupled views for CouchDB.
95
+ email: moonmaster9000@gmail.com
96
+ executables: []
97
+
98
+ extensions: []
99
+
100
+ extra_rdoc_files: []
101
+
102
+ files:
103
+ - lib/couch_view/array.rb
104
+ - lib/couch_view/config.rb
105
+ - lib/couch_view/couch_view.rb
106
+ - lib/couch_view/count_proxy.rb
107
+ - lib/couch_view/map.rb
108
+ - lib/couch_view/map_property.rb
109
+ - lib/couch_view/proxy.rb
110
+ - lib/couch_view/query_options.rb
111
+ - lib/couch_view.rb
112
+ - readme.markdown
113
+ - features/couch_view.array.feature
114
+ - features/couch_view.config.feature
115
+ - features/couch_view.count.proxy.feature
116
+ - features/couch_view.feature
117
+ - features/couch_view.map.feature
118
+ - features/couch_view.proxy.feature
119
+ - features/setup/env.rb
120
+ - features/setup/hooks.rb
121
+ - features/step_definitions/couch_view.array.rb
122
+ - features/step_definitions/couch_view.config.rb
123
+ - features/step_definitions/couch_view.count.proxy.rb
124
+ - features/step_definitions/couch_view.map.rb
125
+ - features/step_definitions/couch_view.proxy.rb
126
+ - features/step_definitions/couch_view.rb
127
+ homepage: http://github.com/moonmaster9000/couch_view
128
+ licenses: []
129
+
130
+ post_install_message:
131
+ rdoc_options: []
132
+
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ hash: 3
141
+ segments:
142
+ - 0
143
+ version: "0"
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ hash: 3
150
+ segments:
151
+ - 0
152
+ version: "0"
153
+ requirements: []
154
+
155
+ rubyforge_project:
156
+ rubygems_version: 1.8.5
157
+ signing_key:
158
+ specification_version: 3
159
+ summary: Powerful views for CouchRest::Model::Base
160
+ test_files:
161
+ - features/couch_view.array.feature
162
+ - features/couch_view.config.feature
163
+ - features/couch_view.count.proxy.feature
164
+ - features/couch_view.feature
165
+ - features/couch_view.map.feature
166
+ - features/couch_view.proxy.feature
167
+ - features/setup/env.rb
168
+ - features/setup/hooks.rb
169
+ - features/step_definitions/couch_view.array.rb
170
+ - features/step_definitions/couch_view.config.rb
171
+ - features/step_definitions/couch_view.count.proxy.rb
172
+ - features/step_definitions/couch_view.map.rb
173
+ - features/step_definitions/couch_view.proxy.rb
174
+ - features/step_definitions/couch_view.rb