matthewford-couchrest 0.2.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 (90) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +93 -0
  3. data/Rakefile +66 -0
  4. data/THANKS.md +18 -0
  5. data/examples/model/example.rb +144 -0
  6. data/examples/word_count/markov +38 -0
  7. data/examples/word_count/views/books/chunked-map.js +3 -0
  8. data/examples/word_count/views/books/united-map.js +1 -0
  9. data/examples/word_count/views/markov/chain-map.js +6 -0
  10. data/examples/word_count/views/markov/chain-reduce.js +7 -0
  11. data/examples/word_count/views/word_count/count-map.js +6 -0
  12. data/examples/word_count/views/word_count/count-reduce.js +3 -0
  13. data/examples/word_count/word_count.rb +46 -0
  14. data/examples/word_count/word_count_query.rb +40 -0
  15. data/examples/word_count/word_count_views.rb +26 -0
  16. data/lib/couchrest.rb +189 -0
  17. data/lib/couchrest/commands/generate.rb +71 -0
  18. data/lib/couchrest/commands/push.rb +103 -0
  19. data/lib/couchrest/core/database.rb +316 -0
  20. data/lib/couchrest/core/design.rb +89 -0
  21. data/lib/couchrest/core/document.rb +96 -0
  22. data/lib/couchrest/core/response.rb +16 -0
  23. data/lib/couchrest/core/server.rb +88 -0
  24. data/lib/couchrest/core/view.rb +4 -0
  25. data/lib/couchrest/helper/pager.rb +103 -0
  26. data/lib/couchrest/helper/streamer.rb +44 -0
  27. data/lib/couchrest/mixins.rb +4 -0
  28. data/lib/couchrest/mixins/attachments.rb +31 -0
  29. data/lib/couchrest/mixins/callbacks.rb +483 -0
  30. data/lib/couchrest/mixins/design_doc.rb +64 -0
  31. data/lib/couchrest/mixins/document_queries.rb +48 -0
  32. data/lib/couchrest/mixins/extended_attachments.rb +68 -0
  33. data/lib/couchrest/mixins/extended_document_mixins.rb +6 -0
  34. data/lib/couchrest/mixins/properties.rb +125 -0
  35. data/lib/couchrest/mixins/validation.rb +234 -0
  36. data/lib/couchrest/mixins/views.rb +168 -0
  37. data/lib/couchrest/monkeypatches.rb +119 -0
  38. data/lib/couchrest/more/casted_model.rb +28 -0
  39. data/lib/couchrest/more/extended_document.rb +217 -0
  40. data/lib/couchrest/more/property.rb +40 -0
  41. data/lib/couchrest/support/blank.rb +42 -0
  42. data/lib/couchrest/support/class.rb +191 -0
  43. data/lib/couchrest/validation/auto_validate.rb +163 -0
  44. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  45. data/lib/couchrest/validation/validation_errors.rb +118 -0
  46. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  47. data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
  48. data/lib/couchrest/validation/validators/format_validator.rb +117 -0
  49. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  50. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  51. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  52. data/lib/couchrest/validation/validators/length_validator.rb +134 -0
  53. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  54. data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
  55. data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
  56. data/spec/couchrest/core/couchrest_spec.rb +201 -0
  57. data/spec/couchrest/core/database_spec.rb +745 -0
  58. data/spec/couchrest/core/design_spec.rb +131 -0
  59. data/spec/couchrest/core/document_spec.rb +311 -0
  60. data/spec/couchrest/core/server_spec.rb +35 -0
  61. data/spec/couchrest/helpers/pager_spec.rb +122 -0
  62. data/spec/couchrest/helpers/streamer_spec.rb +23 -0
  63. data/spec/couchrest/more/casted_extended_doc_spec.rb +40 -0
  64. data/spec/couchrest/more/casted_model_spec.rb +98 -0
  65. data/spec/couchrest/more/extended_doc_attachment_spec.rb +130 -0
  66. data/spec/couchrest/more/extended_doc_spec.rb +509 -0
  67. data/spec/couchrest/more/extended_doc_view_spec.rb +207 -0
  68. data/spec/couchrest/more/property_spec.rb +130 -0
  69. data/spec/couchrest/support/class_spec.rb +59 -0
  70. data/spec/fixtures/attachments/README +3 -0
  71. data/spec/fixtures/attachments/couchdb.png +0 -0
  72. data/spec/fixtures/attachments/test.html +11 -0
  73. data/spec/fixtures/more/article.rb +34 -0
  74. data/spec/fixtures/more/card.rb +20 -0
  75. data/spec/fixtures/more/course.rb +14 -0
  76. data/spec/fixtures/more/event.rb +6 -0
  77. data/spec/fixtures/more/invoice.rb +17 -0
  78. data/spec/fixtures/more/person.rb +8 -0
  79. data/spec/fixtures/more/question.rb +6 -0
  80. data/spec/fixtures/more/service.rb +12 -0
  81. data/spec/fixtures/views/lib.js +3 -0
  82. data/spec/fixtures/views/test_view/lib.js +3 -0
  83. data/spec/fixtures/views/test_view/only-map.js +4 -0
  84. data/spec/fixtures/views/test_view/test-map.js +3 -0
  85. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  86. data/spec/spec.opts +6 -0
  87. data/spec/spec_helper.rb +26 -0
  88. data/utils/remap.rb +27 -0
  89. data/utils/subset.rb +30 -0
  90. metadata +219 -0
@@ -0,0 +1,103 @@
1
+ module CouchRest
2
+
3
+ module Commands
4
+
5
+ module Push
6
+
7
+ def self.run(options)
8
+ directory = options[:directory]
9
+ database = options[:trailing_args].first
10
+
11
+ fm = CouchRest::FileManager.new(database)
12
+ fm.loud = options[:loud]
13
+
14
+ if options[:loud]
15
+ puts "Pushing views from directory #{directory} to database #{fm.db}"
16
+ end
17
+
18
+ fm.push_views(directory)
19
+ end
20
+
21
+ def self.help
22
+ helpstring = <<-GEN
23
+
24
+ == Pushing views with Couchview ==
25
+
26
+ Usage: couchview push directory dbname
27
+
28
+ Couchview expects a specific filesystem layout for your CouchDB views (see
29
+ example below). It also supports advanced features like inlining of library
30
+ code (so you can keep DRY) as well as avoiding unnecessary document
31
+ modification.
32
+
33
+ Couchview also solves a problem with CouchDB's view API, which only provides
34
+ access to the final reduce side of any views which have both a map and a
35
+ reduce function defined. The intermediate map results are often useful for
36
+ development and production. CouchDB is smart enough to reuse map indexes for
37
+ functions duplicated across views within the same design document.
38
+
39
+ For views with a reduce function defined, Couchview creates both a reduce view
40
+ and a map-only view, so that you can browse and query the map side as well as
41
+ the reduction, with no performance penalty.
42
+
43
+ == Example ==
44
+
45
+ couchview push foo-project/bar-views baz-database
46
+
47
+ This will push the views defined in foo-project/bar-views into a database
48
+ called baz-database. Couchview expects the views to be defined in files with
49
+ names like:
50
+
51
+ foo-project/bar-views/my-design/viewname-map.js
52
+ foo-project/bar-views/my-design/viewname-reduce.js
53
+ foo-project/bar-views/my-design/noreduce-map.js
54
+
55
+ Pushed to => http://127.0.0.1:5984/baz-database/_design/my-design
56
+
57
+ And the design document:
58
+ {
59
+ "views" : {
60
+ "viewname-map" : {
61
+ "map" : "### contents of view-name-map.js ###"
62
+ },
63
+ "viewname-reduce" : {
64
+ "map" : "### contents of view-name-map.js ###",
65
+ "reduce" : "### contents of view-name-reduce.js ###"
66
+ },
67
+ "noreduce-map" : {
68
+ "map" : "### contents of noreduce-map.js ###"
69
+ }
70
+ }
71
+ }
72
+
73
+ Couchview will create a design document for each subdirectory of the views
74
+ directory specified on the command line.
75
+
76
+ == Library Inlining ==
77
+
78
+ Couchview can optionally inline library code into your views so you only have
79
+ to maintain it in one place. It looks for any files named lib.* in your
80
+ design-doc directory (for doc specific libs) and in the parent views directory
81
+ (for project global libs). These libraries are only inserted into views which
82
+ include the text
83
+
84
+ // !include lib
85
+
86
+ or
87
+
88
+ # !include lib
89
+
90
+ Couchview is a result of scratching my own itch. I'd be happy to make it more
91
+ general, so please contact me at jchris@grabb.it if you'd like to see anything
92
+ added or changed.
93
+
94
+ GEN
95
+ helpstring.gsub(/^ /, '')
96
+ end
97
+
98
+ end
99
+
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,316 @@
1
+ require 'cgi'
2
+ require "base64"
3
+
4
+ module CouchRest
5
+ class Database
6
+ attr_reader :server, :host, :name, :root, :uri
7
+ attr_accessor :bulk_save_cache_limit
8
+
9
+ # Create a CouchRest::Database adapter for the supplied CouchRest::Server
10
+ # and database name.
11
+ #
12
+ # ==== Parameters
13
+ # server<CouchRest::Server>:: database host
14
+ # name<String>:: database name
15
+ #
16
+ def initialize(server, name)
17
+ @name = name
18
+ @server = server
19
+ @host = server.uri
20
+ @uri = @root = "#{host}/#{name}"
21
+ @streamer = Streamer.new(self)
22
+ @bulk_save_cache = []
23
+ @bulk_save_cache_limit = 500 # must be smaller than the uuid count
24
+ end
25
+
26
+ # returns the database's uri
27
+ def to_s
28
+ @uri
29
+ end
30
+
31
+ # GET the database info from CouchDB
32
+ def info
33
+ CouchRest.get @uri
34
+ end
35
+
36
+ # Query the <tt>_all_docs</tt> view. Accepts all the same arguments as view.
37
+ def documents(params = {})
38
+ keys = params.delete(:keys)
39
+ url = CouchRest.paramify_url "#{@uri}/_all_docs", params
40
+ if keys
41
+ CouchRest.post(url, {:keys => keys})
42
+ else
43
+ CouchRest.get url
44
+ end
45
+ end
46
+
47
+ # POST a temporary view function to CouchDB for querying. This is not
48
+ # recommended, as you don't get any performance benefit from CouchDB's
49
+ # materialized views. Can be quite slow on large databases.
50
+ def slow_view(funcs, params = {})
51
+ keys = params.delete(:keys)
52
+ funcs = funcs.merge({:keys => keys}) if keys
53
+ url = CouchRest.paramify_url "#{@uri}/_temp_view", params
54
+ JSON.parse(RestClient.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
55
+ end
56
+
57
+ # backwards compatibility is a plus
58
+ alias :temp_view :slow_view
59
+
60
+ # Query a CouchDB view as defined by a <tt>_design</tt> document. Accepts
61
+ # paramaters as described in http://wiki.apache.org/couchdb/HttpViewApi
62
+ def view(name, params = {}, &block)
63
+ keys = params.delete(:keys)
64
+ name = name.split('/') # I think this will always be length == 2, but maybe not...
65
+ dname = name.shift
66
+ vname = name.join('/')
67
+ url = CouchRest.paramify_url "#{@uri}/_design/#{dname}/_view/#{vname}", params
68
+ if keys
69
+ CouchRest.post(url, {:keys => keys})
70
+ else
71
+ if block_given?
72
+ @streamer.view("_design/#{dname}/_view/#{vname}", params, &block)
73
+ else
74
+ CouchRest.get url
75
+ end
76
+ end
77
+ end
78
+
79
+ # GET a document from CouchDB, by id. Returns a Ruby Hash.
80
+ def get(id)
81
+ slug = escape_docid(id)
82
+ hash = CouchRest.get("#{@uri}/#{slug}")
83
+ doc = if /^_design/ =~ hash["_id"]
84
+ Design.new(hash)
85
+ else
86
+ Document.new(hash)
87
+ end
88
+ doc.database = self
89
+ doc
90
+ end
91
+
92
+ # GET an attachment directly from CouchDB
93
+ def fetch_attachment(doc, name)
94
+ # slug = escape_docid(docid)
95
+ # name = CGI.escape(name)
96
+ uri = uri_for_attachment(doc, name)
97
+ RestClient.get uri
98
+ # "#{@uri}/#{slug}/#{name}"
99
+ end
100
+
101
+ # PUT an attachment directly to CouchDB
102
+ def put_attachment(doc, name, file, options = {})
103
+ docid = escape_docid(doc['_id'])
104
+ name = CGI.escape(name)
105
+ uri = uri_for_attachment(doc, name)
106
+ JSON.parse(RestClient.put(uri, file, options))
107
+ end
108
+
109
+ # DELETE an attachment directly from CouchDB
110
+ def delete_attachment doc, name
111
+ uri = uri_for_attachment(doc, name)
112
+ # this needs a rev
113
+ JSON.parse(RestClient.delete(uri))
114
+ end
115
+
116
+ # Save a document to CouchDB. This will use the <tt>_id</tt> field from
117
+ # the document as the id for PUT, or request a new UUID from CouchDB, if
118
+ # no <tt>_id</tt> is present on the document. IDs are attached to
119
+ # documents on the client side because POST has the curious property of
120
+ # being automatically retried by proxies in the event of network
121
+ # segmentation and lost responses.
122
+ #
123
+ # If <tt>bulk</tt> is true (false by default) the document is cached for bulk-saving later.
124
+ # Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save.
125
+ def save_doc(doc, bulk = false)
126
+ if doc['_attachments']
127
+ doc['_attachments'] = encode_attachments(doc['_attachments'])
128
+ end
129
+ if bulk
130
+ @bulk_save_cache << doc
131
+ return bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit
132
+ return {"ok" => true} # Compatibility with Document#save
133
+ elsif !bulk && @bulk_save_cache.length > 0
134
+ bulk_save
135
+ end
136
+ result = if doc['_id']
137
+ slug = escape_docid(doc['_id'])
138
+ CouchRest.put "#{@uri}/#{slug}", doc
139
+ else
140
+ begin
141
+ slug = doc['_id'] = @server.next_uuid
142
+ CouchRest.put "#{@uri}/#{slug}", doc
143
+ rescue #old version of couchdb
144
+ CouchRest.post @uri, doc
145
+ end
146
+ end
147
+ if result['ok']
148
+ doc['_id'] = result['id']
149
+ doc['_rev'] = result['rev']
150
+ doc.database = self if doc.respond_to?(:database=)
151
+ end
152
+ result
153
+ end
154
+
155
+ ### DEPRECATION NOTICE
156
+ def save(doc, bulk=false)
157
+ puts "CouchRest::Database's save method is being deprecated, please use save_doc instead"
158
+ save_doc(doc, bulk)
159
+ end
160
+
161
+
162
+ # POST an array of documents to CouchDB. If any of the documents are
163
+ # missing ids, supply one from the uuid cache.
164
+ #
165
+ # If called with no arguments, bulk saves the cache of documents to be bulk saved.
166
+ def bulk_save(docs = nil, use_uuids = true)
167
+ if docs.nil?
168
+ docs = @bulk_save_cache
169
+ @bulk_save_cache = []
170
+ end
171
+ if (use_uuids)
172
+ ids, noids = docs.partition{|d|d['_id']}
173
+ uuid_count = [noids.length, @server.uuid_batch_count].max
174
+ noids.each do |doc|
175
+ nextid = @server.next_uuid(uuid_count) rescue nil
176
+ doc['_id'] = nextid if nextid
177
+ end
178
+ end
179
+ CouchRest.post "#{@uri}/_bulk_docs", {:docs => docs}
180
+ end
181
+ alias :bulk_delete :bulk_save
182
+
183
+ # DELETE the document from CouchDB that has the given <tt>_id</tt> and
184
+ # <tt>_rev</tt>.
185
+ #
186
+ # If <tt>bulk</tt> is true (false by default) the deletion is recorded for bulk-saving (bulk-deletion :) later.
187
+ # Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save.
188
+ def delete_doc(doc, bulk = false)
189
+ raise ArgumentError, "_id and _rev required for deleting" unless doc['_id'] && doc['_rev']
190
+ if bulk
191
+ @bulk_save_cache << { '_id' => doc['_id'], '_rev' => doc['_rev'], '_deleted' => true }
192
+ return bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit
193
+ return { "ok" => true } # Mimic the non-deferred version
194
+ end
195
+ slug = escape_docid(doc['_id'])
196
+ CouchRest.delete "#{@uri}/#{slug}?rev=#{doc['_rev']}"
197
+ end
198
+
199
+ ### DEPRECATION NOTICE
200
+ def delete(doc, bulk=false)
201
+ puts "CouchRest::Database's delete method is being deprecated, please use delete_doc instead"
202
+ delete_doc(doc, bulk)
203
+ end
204
+
205
+ # COPY an existing document to a new id. If the destination id currently exists, a rev must be provided.
206
+ # <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
207
+ # hash with a '_rev' key
208
+ def copy_doc(doc, dest)
209
+ raise ArgumentError, "_id is required for copying" unless doc['_id']
210
+ slug = escape_docid(doc['_id'])
211
+ destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
212
+ "#{dest['_id']}?rev=#{dest['_rev']}"
213
+ else
214
+ dest
215
+ end
216
+ CouchRest.copy "#{@uri}/#{slug}", destination
217
+ end
218
+
219
+ ### DEPRECATION NOTICE
220
+ def copy(doc, dest)
221
+ puts "CouchRest::Database's copy method is being deprecated, please use copy_doc instead"
222
+ copy_doc(doc, dest)
223
+ end
224
+
225
+ # MOVE an existing document to a new id. If the destination id currently exists, a rev must be provided.
226
+ # <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
227
+ # hash with a '_rev' key
228
+ def move_doc(doc, dest)
229
+ raise ArgumentError, "_id and _rev are required for moving" unless doc['_id'] && doc['_rev']
230
+ slug = escape_docid(doc['_id'])
231
+ destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
232
+ "#{dest['_id']}?rev=#{dest['_rev']}"
233
+ else
234
+ dest
235
+ end
236
+ CouchRest.move "#{@uri}/#{slug}?rev=#{doc['_rev']}", destination
237
+ end
238
+
239
+ ### DEPRECATION NOTICE
240
+ def move(doc, dest)
241
+ puts "CouchRest::Database's move method is being deprecated, please use move_doc instead"
242
+ move_doc(doc, dest)
243
+ end
244
+
245
+ # Compact the database, removing old document revisions and optimizing space use.
246
+ def compact!
247
+ CouchRest.post "#{@uri}/_compact"
248
+ end
249
+
250
+ # Create the database
251
+ def create!
252
+ bool = server.create_db(@name) rescue false
253
+ bool && true
254
+ end
255
+
256
+ # Delete and re create the database
257
+ def recreate!
258
+ delete!
259
+ create!
260
+ rescue RestClient::ResourceNotFound
261
+ ensure
262
+ create!
263
+ end
264
+
265
+ # Replicates via "pulling" from another database to this database. Makes no attempt to deal with conflicts.
266
+ def replicate_from other_db
267
+ raise ArgumentError, "must provide a CouchReset::Database" unless other_db.kind_of?(CouchRest::Database)
268
+ CouchRest.post "#{@host}/_replicate", :source => other_db.root, :target => name
269
+ end
270
+
271
+ # Replicates via "pushing" to another database. Makes no attempt to deal with conflicts.
272
+ def replicate_to other_db
273
+ raise ArgumentError, "must provide a CouchReset::Database" unless other_db.kind_of?(CouchRest::Database)
274
+ CouchRest.post "#{@host}/_replicate", :target => other_db.root, :source => name
275
+ end
276
+
277
+ # DELETE the database itself. This is not undoable and could be rather
278
+ # catastrophic. Use with care!
279
+ def delete!
280
+ CouchRest.delete @uri
281
+ end
282
+
283
+ private
284
+
285
+ def uri_for_attachment(doc, name)
286
+ if doc.is_a?(String)
287
+ puts "CouchRest::Database#fetch_attachment will eventually require a doc as the first argument, not a doc.id"
288
+ docid = doc
289
+ rev = nil
290
+ else
291
+ docid = doc['_id']
292
+ rev = doc['_rev']
293
+ end
294
+ docid = escape_docid(docid)
295
+ name = CGI.escape(name)
296
+ rev = "?rev=#{doc['_rev']}" if rev
297
+ "#{@root}/#{docid}/#{name}#{rev}"
298
+ end
299
+
300
+ def escape_docid id
301
+ /^_design\/(.*)/ =~ id ? "_design/#{CGI.escape($1)}" : CGI.escape(id)
302
+ end
303
+
304
+ def encode_attachments(attachments)
305
+ attachments.each do |k,v|
306
+ next if v['stub']
307
+ v['data'] = base64(v['data'])
308
+ end
309
+ attachments
310
+ end
311
+
312
+ def base64(data)
313
+ Base64.encode64(data).gsub(/\s/,'')
314
+ end
315
+ end
316
+ end
@@ -0,0 +1,89 @@
1
+ module CouchRest
2
+ class Design < Document
3
+ def view_by *keys
4
+ opts = keys.pop if keys.last.is_a?(Hash)
5
+ opts ||= {}
6
+ self['views'] ||= {}
7
+ method_name = "by_#{keys.join('_and_')}"
8
+
9
+ if opts[:map]
10
+ view = {}
11
+ view['map'] = opts.delete(:map)
12
+ if opts[:reduce]
13
+ view['reduce'] = opts.delete(:reduce)
14
+ opts[:reduce] = false
15
+ end
16
+ self['views'][method_name] = view
17
+ else
18
+ doc_keys = keys.collect{|k|"doc['#{k}']"} # this is where :require => 'doc.x == true' would show up
19
+ key_emit = doc_keys.length == 1 ? "#{doc_keys.first}" : "[#{doc_keys.join(', ')}]"
20
+ guards = opts.delete(:guards) || []
21
+ guards.concat doc_keys
22
+ map_function = <<-JAVASCRIPT
23
+ function(doc) {
24
+ if (#{guards.join(' && ')}) {
25
+ emit(#{key_emit}, null);
26
+ }
27
+ }
28
+ JAVASCRIPT
29
+ self['views'][method_name] = {
30
+ 'map' => map_function
31
+ }
32
+ end
33
+ self['views'][method_name]['couchrest-defaults'] = opts unless opts.empty?
34
+ method_name
35
+ end
36
+
37
+ # Dispatches to any named view.
38
+ def view view_name, query={}, &block
39
+ view_name = view_name.to_s
40
+ view_slug = "#{name}/#{view_name}"
41
+ defaults = (self['views'][view_name] && self['views'][view_name]["couchrest-defaults"]) || {}
42
+ fetch_view(view_slug, defaults.merge(query), &block)
43
+ end
44
+
45
+ def name
46
+ id.sub('_design/','') if id
47
+ end
48
+
49
+ def name= newname
50
+ self['_id'] = "_design/#{newname}"
51
+ end
52
+
53
+ def save
54
+ raise ArgumentError, "_design docs require a name" unless name && name.length > 0
55
+ super
56
+ end
57
+
58
+ private
59
+
60
+ # returns stored defaults if the there is a view named this in the design doc
61
+ def has_view?(view)
62
+ view = view.to_s
63
+ self['views'][view] &&
64
+ (self['views'][view]["couchrest-defaults"]||{})
65
+ end
66
+
67
+ # def fetch_view_with_docs name, opts, raw=false, &block
68
+ # if raw
69
+ # fetch_view name, opts, &block
70
+ # else
71
+ # begin
72
+ # view = fetch_view name, opts.merge({:include_docs => true}), &block
73
+ # view['rows'].collect{|r|new(r['doc'])} if view['rows']
74
+ # rescue
75
+ # # fallback for old versions of couchdb that don't
76
+ # # have include_docs support
77
+ # view = fetch_view name, opts, &block
78
+ # view['rows'].collect{|r|new(database.get(r['id']))} if view['rows']
79
+ # end
80
+ # end
81
+ # end
82
+
83
+ def fetch_view view_name, opts, &block
84
+ database.view(view_name, opts, &block)
85
+ end
86
+
87
+ end
88
+
89
+ end