couch-db 0.11.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,410 +1,413 @@
1
- require 'couch/db/version'
2
- require 'net/http'
3
- require 'json'
4
- require 'objspace'
5
- require 'openssl'
6
-
7
- class Hash
8
- def include_symbol_or_string?(param)
9
- if param.is_a? Symbol or param.is_a? String
10
- include? param.to_sym or include? param.to_s
11
- else
12
- false
13
- end
14
- end
15
- end
16
-
17
- module Couch
18
- module BasicRequest
19
- def create_postfix(query_params, default='')
20
- if query_params
21
- params_a = []
22
- query_params.each do |key, value|
23
- params_a << "#{key}=#{value}"
24
- end
25
- postfix = "?#{params_a.join('&')}"
26
- else
27
- postfix = default
28
- end
29
- postfix
30
- end
31
-
32
- module Get
33
- # Returns parsed doc from database
34
- def get_doc(database, id)
35
- res = get("/#{database}/#{CGI.escape(id)}")
36
- JSON.parse(res.body)
37
- end
38
-
39
- def get_attachment_str(db, id, attachment)
40
- uri = URI::encode "/#{db}/#{id}/#{attachment}"
41
- get(uri).body
42
- end
43
- end
44
-
45
- module Head
46
- # Returns revision for given document
47
- def get_rev(database, id)
48
- res = head("/#{database}/#{CGI.escape(id)}")
49
- if res.code == '200'
50
- res['etag'].gsub(/^"|"$/, '')
51
- else
52
- nil
53
- end
54
- end
55
- end
56
- end
57
-
58
- # Bulk requests; use methods from Couch::BasicRequest
59
- module BulkRequest
60
- module Get
61
- # Returns an array of the full documents for given database, possibly filtered with given parameters.
62
- # We recommend you use all_docs instead.
63
- #
64
- # Note that the 'include_docs' parameter must be set to true for this.
65
- def get_all_docs(database, params)
66
- # unless params.include_symbol_or_string? :include_docs
67
- # params.merge!({:include_docs => true})
68
- # end
69
- postfix = create_postfix(params)
70
- uri = URI::encode "/#{database}/_all_docs#{postfix}"
71
- res = get(uri)
72
- JSON.parse(res.body)['rows']
73
- end
74
-
75
-
76
- # If a block is given, performs the block for each +limit+-sized slice of _all_docs.
77
- # If no block is given, returns all docs by appending +limit+-sized slices of _all_docs.
78
- #
79
- # This method assumes your docs don't have the high-value Unicode character \ufff0. If it does, then behaviour is undefined. The reason why we use the startkey parameter instead of skip is that startkey is faster.
80
- def all_docs(db, limit=750, opts={}, &block)
81
- all_docs = []
82
- start_key = nil
83
- loop do
84
- opts = opts.merge({limit: limit})
85
- if start_key
86
- opts[:startkey]=start_key
87
- end
88
- docs = (lambda { |options| get_all_docs(db, options) }).call(opts)
89
- if docs.length <= 0
90
- break
91
- else
92
- if block
93
- block.call(docs)
94
- else
95
- all_docs < docs
96
- end
97
- start_key ="\"#{docs.last['_id']}\\ufff0\""
98
- end
99
- end
100
- all_docs.flatten
101
- end
102
-
103
- # Returns an array of all rows for given view.
104
- #
105
- # We recommend you use rows_for_view instead.
106
- def get_rows_for_view(database, design_doc, view, query_params=nil)
107
- postfix = create_postfix(query_params)
108
- uri = URI::encode "/#{database}/_design/#{design_doc}/_view/#{view}#{postfix}"
109
- res = get(uri)
110
- JSON.parse(res.body.force_encoding('utf-8'))['rows']
111
- end
112
-
113
- # If a block is given, performs the block for each +limit+-sized slice of rows for the given view.
114
- # If no block is given, returns all rows by appending +limit+-sized slices of the given view.
115
- def rows_for_view(db, design_doc, view, limit=500, opts={}, &block)
116
- get_all_views(lambda { |options| get_rows_for_view(db, design_doc, view, options) }, limit, opts, block)
117
- end
118
-
119
-
120
- # Returns an array of all ids in the database
121
- def get_all_ids(database, params)
122
- ids=[]
123
- postfix = create_postfix(params)
124
-
125
- uri = URI::encode "/#{database}/_all_docs#{postfix}"
126
- res = get(uri)
127
- result = JSON.parse(res.body)
128
- result['rows'].each do |row|
129
- if row['error']
130
- puts "#{row['key']}: #{row['error']}"
131
- puts "#{row['reason']}"
132
- else
133
- ids << row['id']
134
- end
135
- end
136
- ids
137
- end
138
-
139
- # Returns an array of all ids in the database
140
- def all_ids(db, limit=500, opts={}, &block)
141
- all_docs = []
142
- start_key = nil
143
- loop do
144
- opts = opts.merge({limit: limit})
145
- if start_key
146
- opts[:startkey]=start_key
147
- end
148
- docs = (lambda { |options| get_all_ids(db, options) }).call(opts)
149
- if docs.length <= 0
150
- break
151
- else
152
- if block
153
- block.call(docs)
154
- else
155
- all_docs < docs
156
- end
157
- start_key ="\"#{docs.last}\\ufff0\""
158
- end
159
- end
160
- all_docs.flatten
161
- end
162
-
163
- # Returns an array of the full documents for given view, possibly filtered with given parameters. Note that the 'include_docs' parameter must be set to true for this.
164
- #
165
- # Also consider using `docs_for_view`
166
- def get_docs_for_view(db, design_doc, view, params={})
167
- params.merge!({:include_docs => true})
168
- rows = get_rows_for_view(db, design_doc, view, params)
169
- docs = []
170
- rows.each do |row|
171
- docs << row['doc']
172
- end
173
- docs
174
- end
175
-
176
- # If a block is given, performs the block for each +limit+-sized slice of documents for the given view.
177
- # If no block is given, returns all docs by appending +limit+-sized slices of the given view.
178
- def docs_for_view(db, design_doc, view, limit=750, opts={}, &block)
179
- get_all_views(lambda { |options| get_docs_for_view(db, design_doc, view, options) }, limit, opts, block)
180
- end
181
-
182
- private
183
-
184
- def get_all_views(next_results, limit, opts, block)
185
- all = []
186
- offset = 0
187
- loop do
188
- opts = opts.merge({
189
- limit: limit,
190
- skip: offset,
191
- })
192
- docs = next_results.call(opts)
193
- if docs.length <= 0
194
- break
195
- else
196
- if block
197
- block.call(docs)
198
- else
199
- all < docs
200
- end
201
- offset += limit
202
- end
203
- end
204
- all.flatten
205
- end
206
-
207
- end
208
-
209
- module Delete
210
- def bulk_delete(database, docs)
211
- docs.each do |doc|
212
- doc[:_deleted]=true
213
- end
214
- json = {:docs => docs}.to_json
215
- post("/#{database}/_bulk_docs", json)
216
- end
217
- end
218
-
219
- module Post
220
- # Flushes the given hashes to CouchDB
221
- def post_bulk(database, docs)
222
- body = {:docs => docs}.to_json #.force_encoding('utf-8')
223
- post("/#{database}/_bulk_docs", body)
224
- end
225
-
226
- def post_bulk_throttled(db, docs, &block)
227
- # puts "Flushing #{docs.length} docs"
228
- bulk = []
229
- docs.each do |doc|
230
- bulk << doc
231
- if bulk.to_json.bytesize/1024/1024 > options[:flush_size_mb] or bulk.length >= options[:max_array_length]
232
- handle_bulk_flush(bulk, db, block)
233
- end
234
- end
235
- if bulk.length > 0
236
- handle_bulk_flush(bulk, db, block)
237
- end
238
- end
239
-
240
-
241
- def post_bulk_if_big_enough(db, docs)
242
- flush = (docs.to_json.bytesize / 1024 >= (options[:flush_size_mb]*1024) or docs.length >= options[:max_array_length])
243
- if flush
244
- post_bulk_throttled(db, docs)
245
- docs.clear
246
- end
247
- flush
248
- end
249
-
250
- private
251
-
252
- def handle_bulk_flush(bulk, db, block)
253
- res = post_bulk(db, bulk)
254
- error_count=0
255
- if res.body
256
- begin
257
- JSON.parse(res.body).each do |d|
258
- error_count+=1 if d['error']
259
- end
260
- end
261
- end
262
- if error_count > 0
263
- puts "Bulk request completed with #{error_count} errors"
264
- end
265
- if block
266
- block.call(res)
267
- end
268
- bulk.clear
269
- end
270
- end
271
- end
272
-
273
- class Server
274
- attr_accessor :options
275
-
276
- def initialize(url, options)
277
- if url.is_a? String
278
- url = URI(url)
279
- end
280
- @couch_url = url
281
- @options = options
282
- @options[:couch_url] = @couch_url
283
- @options[:use_ssl] ||= true
284
- @options[:max_array_length] ||= 250
285
- @options[:flush_size_mb] ||= 10
286
- @options[:open_timeout] ||= 5*30
287
- @options[:read_timeout] ||= 5*30
288
- @options[:fail_silent] ||= false
289
- end
290
-
291
- def delete(uri)
292
- Request.new(Net::HTTP::Delete.new(uri), nil,
293
- @options
294
- ).perform
295
- end
296
-
297
- def new_delete(uri)
298
- Request.new(Net::HTTP::Delete.new(uri)).couch_url(@couch_url)
299
- end
300
-
301
- def head(uri)
302
- Request.new(Net::HTTP::Head.new(uri), nil,
303
- @options
304
- ).perform
305
- end
306
-
307
- def new_head(uri)
308
- Request.new(Net::HTTP::Head.new(uri)).couch_url(@couch_url)
309
- end
310
-
311
- def get(uri)
312
- Request.new(
313
- Net::HTTP::Get.new(uri), nil,
314
- @options
315
- ).perform
316
- end
317
-
318
- def new_get(uri)
319
- Request.new(Net::HTTP::Get.new(uri)).couch_url(@couch_url)
320
- end
321
-
322
- def put(uri, json)
323
- Request.new(Net::HTTP::Put.new(uri), json,
324
- @options
325
- ).perform
326
- end
327
-
328
- def new_put(uri)
329
- Request.new(Net::HTTP::Put.new(uri)).couch_url(@couch_url)
330
- end
331
-
332
- def post(uri, json)
333
- Request.new(Net::HTTP::Post.new(uri), json,
334
- @options
335
- ).perform
336
- end
337
-
338
- def new_post(uri)
339
- Request.new(Net::HTTP::Post.new(uri)).couch_url(@couch_url)
340
- end
341
-
342
- class Request
343
- def initialize(req, json=nil, opts={open_timeout: 5*30, read_timeout: 5*30, fail_silent: false})
344
- @req=req
345
- @json = json
346
- @options = opts
347
- end
348
-
349
- def json(json)
350
- @json = json
351
- self
352
- end
353
-
354
- def couch_url(couch_url)
355
- @options.merge!({couch_url: couch_url})
356
- self
357
- end
358
-
359
- def open_timeout(open_timeout)
360
- @options.merge!({open_timeout: open_timeout})
361
- self
362
- end
363
-
364
- def read_timeout(read_timeout)
365
- @options.merge!({read_timeout: read_timeout})
366
- self
367
- end
368
-
369
- def fail_silent(fail_silent)
370
- @options.merge!({fail_silent: fail_silent})
371
- self
372
- end
373
-
374
- def perform
375
- @req.basic_auth @options[:name], @options[:password]
376
-
377
- if @json
378
- @req['Content-Type'] = 'application/json;charset=utf-8'
379
- @req.body = @json
380
- end
381
-
382
- res = Net::HTTP.start(@options[:couch_url].host, @options[:couch_url].port,
383
- :use_ssl => @options[:couch_url].scheme =='https') do |http|
384
- http.open_timeout = @options[:open_timeout]
385
- http.read_timeout = @options[:read_timeout]
386
- http.request(@req)
387
- end
388
-
389
- unless @options[:fail_silent] or res.kind_of?(Net::HTTPSuccess)
390
- # puts "CouchDb responsed with error code #{res.code}"
391
- handle_error(@req, res)
392
- end
393
- res
394
- end
395
-
396
- def handle_error(req, res)
397
- raise RuntimeError.new("#{res.code}:#{res.message}\nMETHOD:#{req.method}\nURI:#{req.path}\n#{res.body}")
398
- end
399
- end
400
-
401
- include BasicRequest
402
- include BasicRequest::Head
403
- include BasicRequest::Get
404
- include BulkRequest::Get
405
- include BulkRequest::Delete
406
- include BulkRequest::Post
407
-
408
- private
409
- end
410
- end
1
+ require 'couch/db/version'
2
+ require 'net/http'
3
+ require 'json'
4
+ require 'objspace'
5
+ require 'openssl'
6
+
7
+ class Hash
8
+ def include_symbol_or_string?(param)
9
+ if param.is_a? Symbol or param.is_a? String
10
+ include? param.to_sym or include? param.to_s
11
+ else
12
+ false
13
+ end
14
+ end
15
+ end
16
+
17
+ module Couch
18
+ module BasicRequest
19
+ def create_postfix(query_params, default='')
20
+ if query_params
21
+ params_a = []
22
+ query_params.each do |key, value|
23
+ params_a << "#{key}=#{value}"
24
+ end
25
+ postfix = "?#{params_a.join('&')}"
26
+ else
27
+ postfix = default
28
+ end
29
+ postfix
30
+ end
31
+
32
+ module Get
33
+ # Returns parsed doc from database
34
+ def get_doc(database, id, params={})
35
+ res = get("/#{database}/#{CGI.escape(id)}#{create_postfix(params)}")
36
+ JSON.parse(res.body)
37
+ end
38
+
39
+ def get_attachment_str(db, id, attachment)
40
+ uri = URI::encode "/#{db}/#{id}/#{attachment}"
41
+ get(uri).body
42
+ end
43
+ end
44
+
45
+ module Head
46
+ # Returns revision for given document
47
+ def get_rev(database, id)
48
+ res = head("/#{database}/#{CGI.escape(id)}")
49
+ if res.code == '200'
50
+ res['etag'].gsub(/^"|"$/, '')
51
+ else
52
+ nil
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # Bulk requests; use methods from Couch::BasicRequest
59
+ module BulkRequest
60
+ module Get
61
+ # Returns an array of the full documents for given database, possibly filtered with given parameters.
62
+ # We recommend you use all_docs instead.
63
+ #
64
+ # Note that the 'include_docs' parameter must be set to true for this.
65
+ def get_all_docs(database, params)
66
+ # unless params.include_symbol_or_string? :include_docs
67
+ # params.merge!({:include_docs => true})
68
+ # end
69
+ postfix = create_postfix(params)
70
+ uri = URI::encode "/#{database}/_all_docs#{postfix}"
71
+ res = get(uri)
72
+ JSON.parse(res.body)['rows']
73
+ end
74
+
75
+
76
+ # If a block is given, performs the block for each +limit+-sized slice of _all_docs.
77
+ # If no block is given, returns all docs by appending +limit+-sized slices of _all_docs.
78
+ #
79
+ # This method assumes your docs don't have the high-value Unicode character \ufff0. If it does, then behaviour is undefined. The reason why we use the startkey parameter instead of skip is that startkey is faster.
80
+ def all_docs(db, limit=750, opts={}, &block)
81
+ all_docs = []
82
+ start_key = nil
83
+ loop do
84
+ opts = opts.merge({limit: limit})
85
+ if start_key
86
+ opts[:startkey]=start_key
87
+ end
88
+ docs = (lambda { |options| get_all_docs(db, options) }).call(opts)
89
+ if docs.length <= 0
90
+ break
91
+ else
92
+ if block
93
+ block.call(docs)
94
+ else
95
+ all_docs < docs
96
+ end
97
+ start_key ="\"#{docs.last['_id']}\\ufff0\""
98
+ end
99
+ end
100
+ all_docs.flatten
101
+ end
102
+
103
+ # Returns an array of all rows for given view.
104
+ #
105
+ # We recommend you use rows_for_view instead.
106
+ def get_rows_for_view(database, design_doc, view, query_params=nil)
107
+ postfix = create_postfix(query_params)
108
+ uri = URI::encode "/#{database}/_design/#{design_doc}/_view/#{view}#{postfix}"
109
+ res = get(uri)
110
+ JSON.parse(res.body.force_encoding('utf-8'))['rows']
111
+ end
112
+
113
+ # If a block is given, performs the block for each +limit+-sized slice of rows for the given view.
114
+ # If no block is given, returns all rows by appending +limit+-sized slices of the given view.
115
+ def rows_for_view(db, design_doc, view, limit=500, opts={}, &block)
116
+ get_all_views(lambda { |options| get_rows_for_view(db, design_doc, view, options) }, limit, opts, block)
117
+ end
118
+
119
+
120
+ # Returns an array of all ids in the database
121
+ def get_all_ids(database, params)
122
+ ids=[]
123
+ postfix = create_postfix(params)
124
+
125
+ uri = URI::encode "/#{database}/_all_docs#{postfix}"
126
+ res = get(uri)
127
+ result = JSON.parse(res.body)
128
+ result['rows'].each do |row|
129
+ if row['error']
130
+ puts "#{row['key']}: #{row['error']}"
131
+ puts "#{row['reason']}"
132
+ else
133
+ ids << row['id']
134
+ end
135
+ end
136
+ ids
137
+ end
138
+
139
+ # Returns an array of all ids in the database
140
+ def all_ids(db, limit=500, opts={}, &block)
141
+ all_docs = []
142
+ start_key = nil
143
+ loop do
144
+ opts = opts.merge({limit: limit})
145
+ if start_key
146
+ opts[:startkey]=start_key
147
+ end
148
+ docs = (lambda { |options| get_all_ids(db, options) }).call(opts)
149
+ if docs.length <= 0
150
+ break
151
+ else
152
+ if block
153
+ block.call(docs)
154
+ else
155
+ all_docs < docs
156
+ end
157
+ start_key ="\"#{docs.last}\\ufff0\""
158
+ end
159
+ end
160
+ all_docs.flatten
161
+ end
162
+
163
+ # Returns an array of the full documents for given view, possibly filtered with given parameters. Note that the 'include_docs' parameter must be set to true for this.
164
+ #
165
+ # Also consider using `docs_for_view`
166
+ def get_docs_for_view(db, design_doc, view, params={})
167
+ params.merge!({:include_docs => true})
168
+ rows = get_rows_for_view(db, design_doc, view, params)
169
+ docs = []
170
+ rows.each do |row|
171
+ docs << row['doc']
172
+ end
173
+ docs
174
+ end
175
+
176
+ # If a block is given, performs the block for each +limit+-sized slice of documents for the given view.
177
+ # If no block is given, returns all docs by appending +limit+-sized slices of the given view.
178
+ def docs_for_view(db, design_doc, view, limit=750, opts={}, &block)
179
+ get_all_views(lambda { |options| get_docs_for_view(db, design_doc, view, options) }, limit, opts, block)
180
+ end
181
+
182
+ private
183
+
184
+ def get_all_views(next_results, limit, opts, block)
185
+ all = []
186
+ offset = 0
187
+ loop do
188
+ opts = opts.merge({
189
+ limit: limit,
190
+ skip: offset,
191
+ })
192
+ docs = next_results.call(opts)
193
+ if docs.length <= 0
194
+ break
195
+ else
196
+ if block
197
+ block.call(docs)
198
+ else
199
+ all < docs
200
+ end
201
+ offset += limit
202
+ end
203
+ end
204
+ all.flatten
205
+ end
206
+
207
+ end
208
+
209
+ module Delete
210
+ def bulk_delete(database, docs)
211
+ docs.each do |doc|
212
+ doc[:_deleted]=true
213
+ end
214
+ json = {:docs => docs}.to_json
215
+ post("/#{database}/_bulk_docs", json)
216
+ end
217
+ end
218
+
219
+ module Post
220
+ # Flushes the given hashes to CouchDB
221
+ def post_bulk(database, docs)
222
+ body = {:docs => docs}.to_json #.force_encoding('utf-8')
223
+ post("/#{database}/_bulk_docs", body)
224
+ end
225
+
226
+ def post_bulk_throttled(db, docs, &block)
227
+ # puts "Flushing #{docs.length} docs"
228
+ bulk = []
229
+ docs.each do |doc|
230
+ bulk << doc
231
+ if bulk.to_json.bytesize/1024/1024 > options[:flush_size_mb] or bulk.length >= options[:max_array_length]
232
+ handle_bulk_flush(bulk, db, block)
233
+ end
234
+ end
235
+ if bulk.length > 0
236
+ handle_bulk_flush(bulk, db, block)
237
+ end
238
+ end
239
+
240
+
241
+ def post_bulk_if_big_enough(db, docs)
242
+ flush = (docs.to_json.bytesize / 1024 >= (options[:flush_size_mb]*1024) or docs.length >= options[:max_array_length])
243
+ if flush
244
+ post_bulk_throttled(db, docs)
245
+ docs.clear
246
+ end
247
+ flush
248
+ end
249
+
250
+ private
251
+
252
+ def handle_bulk_flush(bulk, db, block)
253
+ res = post_bulk(db, bulk)
254
+ error_count=0
255
+ if res.body
256
+ begin
257
+ JSON.parse(res.body).each do |d|
258
+ error_count+=1 if d['error']
259
+ end
260
+ end
261
+ end
262
+ if error_count > 0
263
+ puts "Bulk request completed with #{error_count} errors"
264
+ end
265
+ if block
266
+ block.call(res)
267
+ end
268
+ bulk.clear
269
+ end
270
+ end
271
+ end
272
+
273
+ class Server
274
+ attr_accessor :options
275
+
276
+ def initialize(url, options)
277
+ if url.is_a? String
278
+ url = URI(url)
279
+ end
280
+ @couch_url = url
281
+ @options = options
282
+ @options[:couch_url] = @couch_url
283
+ @options[:use_ssl] ||= true
284
+ @options[:max_array_length] ||= 250
285
+ @options[:flush_size_mb] ||= 10
286
+ @options[:open_timeout] ||= 5*30
287
+ @options[:read_timeout] ||= 5*30
288
+ @options[:fail_silent] ||= false
289
+ end
290
+
291
+ def delete(uri)
292
+ Request.new(Net::HTTP::Delete.new(uri), nil,
293
+ @options
294
+ ).perform
295
+ end
296
+
297
+ def new_delete(uri)
298
+ Request.new(Net::HTTP::Delete.new(uri)).couch_url(@couch_url)
299
+ end
300
+
301
+ def head(uri)
302
+ Request.new(Net::HTTP::Head.new(uri), nil,
303
+ @options
304
+ ).perform
305
+ end
306
+
307
+ def new_head(uri)
308
+ Request.new(Net::HTTP::Head.new(uri)).couch_url(@couch_url)
309
+ end
310
+
311
+ def get(uri)
312
+ Request.new(
313
+ Net::HTTP::Get.new(uri), nil,
314
+ @options
315
+ ).perform
316
+ end
317
+
318
+ def new_get(uri)
319
+ Request.new(Net::HTTP::Get.new(uri)).couch_url(@couch_url)
320
+ end
321
+
322
+ def put(uri, json)
323
+ Request.new(Net::HTTP::Put.new(uri), json,
324
+ @options
325
+ ).perform
326
+ end
327
+
328
+ def new_put(uri)
329
+ Request.new(Net::HTTP::Put.new(uri)).couch_url(@couch_url)
330
+ end
331
+
332
+ def post(uri, json)
333
+ Request.new(Net::HTTP::Post.new(uri), json,
334
+ @options
335
+ ).perform
336
+ end
337
+
338
+ def new_post(uri)
339
+ Request.new(Net::HTTP::Post.new(uri)).couch_url(@couch_url)
340
+ end
341
+
342
+ class Request
343
+ def initialize(req, json=nil, opts={open_timeout: 5*30, read_timeout: 5*30, fail_silent: false})
344
+ @req=req
345
+ @json = json
346
+ @options = opts
347
+ end
348
+
349
+ def json(json)
350
+ @json = json
351
+ self
352
+ end
353
+
354
+ def couch_url(couch_url)
355
+ @options.merge!({couch_url: couch_url})
356
+ self
357
+ end
358
+
359
+ def open_timeout(open_timeout)
360
+ @options.merge!({open_timeout: open_timeout})
361
+ self
362
+ end
363
+
364
+ def read_timeout(read_timeout)
365
+ @options.merge!({read_timeout: read_timeout})
366
+ self
367
+ end
368
+
369
+ def fail_silent(fail_silent)
370
+ @options.merge!({fail_silent: fail_silent})
371
+ self
372
+ end
373
+
374
+ def perform
375
+ @req.basic_auth @options[:name], @options[:password]
376
+
377
+ if @json
378
+ @req['Content-Type'] = 'application/json;charset=utf-8'
379
+ @req.body = @json
380
+ end
381
+
382
+ res = Net::HTTP.start(
383
+ @options[:couch_url].host,
384
+ @options[:couch_url].port,
385
+ {:use_ssl => @options[:couch_url].scheme =='https'}
386
+ ) do |http|
387
+ http.open_timeout = @options[:open_timeout]
388
+ http.read_timeout = @options[:read_timeout]
389
+ http.request(@req)
390
+ end
391
+
392
+ unless @options[:fail_silent] or res.kind_of?(Net::HTTPSuccess)
393
+ # puts "CouchDb responsed with error code #{res.code}"
394
+ handle_error(@req, res)
395
+ end
396
+ res
397
+ end
398
+
399
+ def handle_error(req, res)
400
+ raise RuntimeError.new("#{res.code}:#{res.message}\nMETHOD:#{req.method}\nURI:#{req.path}\n#{res.body}")
401
+ end
402
+ end
403
+
404
+ include BasicRequest
405
+ include BasicRequest::Head
406
+ include BasicRequest::Get
407
+ include BulkRequest::Get
408
+ include BulkRequest::Delete
409
+ include BulkRequest::Post
410
+
411
+ private
412
+ end
413
+ end