rufus-jig 0.1.23 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/CHANGELOG.txt +8 -0
- data/README.rdoc +41 -11
- data/Rakefile +37 -12
- data/TODO.txt +6 -3
- data/lib/rufus/jig/adapters/em.rb +21 -24
- data/lib/rufus/jig/adapters/net.rb +3 -4
- data/lib/rufus/jig/adapters/net_persistent.rb +26 -7
- data/lib/rufus/jig/adapters/patron.rb +25 -10
- data/lib/rufus/jig/couch.rb +199 -36
- data/lib/rufus/jig/http.rb +183 -90
- data/lib/rufus/jig/path.rb +4 -4
- data/lib/rufus/jig/version.rb +1 -1
- data/rufus-jig.gemspec +55 -34
- data/spec/couch/attachements_spec.rb +113 -0
- data/spec/couch/basic_auth_spec.rb +75 -0
- data/spec/couch/conditional_spec.rb +178 -0
- data/spec/couch/continuous.rb +97 -0
- data/spec/couch/couch_spec.rb +64 -0
- data/spec/couch/db_spec.rb +366 -0
- data/{test → spec/couch}/tweet.png +0 -0
- data/spec/couch/views_spec.rb +326 -0
- data/spec/couch_url.txt +2 -0
- data/spec/jig/basic_auth_spec.rb +51 -0
- data/spec/jig/conditional_spec.rb +76 -0
- data/spec/jig/delete_spec.rb +32 -0
- data/spec/jig/get_spec.rb +116 -0
- data/spec/jig/misc_spec.rb +120 -0
- data/spec/jig/new_spec.rb +95 -0
- data/spec/jig/parse_uri_spec.rb +139 -0
- data/spec/jig/post_spec.rb +79 -0
- data/spec/jig/prefix_spec.rb +51 -0
- data/spec/jig/put_spec.rb +68 -0
- data/spec/jig/timeout_spec.rb +94 -0
- data/{test → spec}/server.rb +14 -4
- data/spec/spec_helper.rb +61 -0
- data/spec/support/couch_helper.rb +14 -0
- data/spec/support/server_helper.rb +32 -0
- metadata +98 -43
- data/lib/rufus/jig/adapters/net_response.rb +0 -42
- data/test/base.rb +0 -53
- data/test/bm/bm0.rb +0 -49
- data/test/bm/bm1.rb +0 -43
- data/test/conc/put_vs_delete.rb +0 -28
- data/test/couch_base.rb +0 -52
- data/test/couch_url.txt +0 -1
- data/test/ct_0_couch.rb +0 -64
- data/test/ct_1_couchdb.rb +0 -204
- data/test/ct_2_couchdb_options.rb +0 -50
- data/test/ct_3_couchdb_views.rb +0 -106
- data/test/ct_4_attachments.rb +0 -126
- data/test/ct_5_couchdb_continuous.rb +0 -92
- data/test/cut_0_auth_couch.rb +0 -62
- data/test/test.rb +0 -28
- data/test/to.sh +0 -25
- data/test/tt_0_get_timeout.rb +0 -92
- data/test/ut_0_http_get.rb +0 -191
- data/test/ut_1_http_post.rb +0 -81
- data/test/ut_2_http_delete.rb +0 -42
- data/test/ut_3_http_put.rb +0 -105
- data/test/ut_4_http_prefix.rb +0 -50
- data/test/ut_5_http_misc.rb +0 -65
- data/test/ut_6_args.rb +0 -98
- data/test/ut_7_parse_uri.rb +0 -79
- data/test/ut_8_auth.rb +0 -37
data/lib/rufus/jig/couch.rb
CHANGED
@@ -22,6 +22,8 @@
|
|
22
22
|
# Made in Japan.
|
23
23
|
#++
|
24
24
|
|
25
|
+
require 'cgi'
|
26
|
+
|
25
27
|
require 'base64'
|
26
28
|
require 'socket'
|
27
29
|
# for #on_change
|
@@ -38,7 +40,7 @@ module Rufus::Jig
|
|
38
40
|
attr_reader :path
|
39
41
|
attr_reader :http
|
40
42
|
|
41
|
-
def initialize
|
43
|
+
def initialize(*args)
|
42
44
|
|
43
45
|
@http = Rufus::Jig::Http.new(*args)
|
44
46
|
|
@@ -55,7 +57,7 @@ module Rufus::Jig
|
|
55
57
|
@http.close
|
56
58
|
end
|
57
59
|
|
58
|
-
def put
|
60
|
+
def put(doc_or_path, opts={})
|
59
61
|
|
60
62
|
path, payload = if doc_or_path.is_a?(String)
|
61
63
|
[ doc_or_path, '' ]
|
@@ -84,19 +86,71 @@ module Rufus::Jig
|
|
84
86
|
nil
|
85
87
|
end
|
86
88
|
|
87
|
-
def get
|
89
|
+
def get(doc_or_path, opts={})
|
88
90
|
|
89
91
|
path = doc_or_path.is_a?(Hash) ? doc_or_path['_id'] : doc_or_path
|
90
92
|
path = adjust(path)
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
+
@http.get(path, opts)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns all the docs in the current database.
|
98
|
+
#
|
99
|
+
# c = Rufus::Jig::Couch.new('http://127.0.0.1:5984, 'my_db')
|
100
|
+
#
|
101
|
+
# docs = c.all
|
102
|
+
# docs = c.all(:include_docs => false)
|
103
|
+
# docs = c.all(:include_design_docs => false)
|
104
|
+
#
|
105
|
+
# docs = c.all(:skip => 10, :limit => 10)
|
106
|
+
#
|
107
|
+
# It understands (passes) all the options for CouchDB view API :
|
108
|
+
#
|
109
|
+
# http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options
|
110
|
+
#
|
111
|
+
def all(opts={})
|
112
|
+
|
113
|
+
opts = opts.dup
|
114
|
+
# don't touch the original
|
115
|
+
|
116
|
+
path = adjust('_all_docs')
|
117
|
+
|
118
|
+
opts[:include_docs] = true if opts[:include_docs].nil?
|
119
|
+
|
120
|
+
adjust_params(opts)
|
121
|
+
|
122
|
+
keys = opts.delete(:keys)
|
123
|
+
|
124
|
+
return [] if keys && keys.empty?
|
125
|
+
|
126
|
+
res = if keys
|
127
|
+
opts[:cache] = :with_body if opts[:cache].nil?
|
128
|
+
@http.post(path, { 'keys' => keys }, opts)
|
129
|
+
else
|
130
|
+
@http.get(path, opts)
|
94
131
|
end
|
95
132
|
|
96
|
-
|
133
|
+
rows = res['rows']
|
134
|
+
|
135
|
+
docs = if opts[:params][:include_docs]
|
136
|
+
rows.map { |row| row['doc'] }
|
137
|
+
else
|
138
|
+
rows.map { |row| { '_id' => row['id'], '_rev' => row['value']['rev'] } }
|
139
|
+
end
|
140
|
+
|
141
|
+
if opts[:include_design_docs] == false
|
142
|
+
docs = docs.reject { |doc| DESIGN_PATH_REGEX.match(doc['_id']) }
|
143
|
+
end
|
144
|
+
|
145
|
+
docs
|
146
|
+
end
|
147
|
+
|
148
|
+
def ids(opts={})
|
149
|
+
|
150
|
+
all(opts).collect { |row| row['_id'] }
|
97
151
|
end
|
98
152
|
|
99
|
-
def delete
|
153
|
+
def delete(doc_or_path, rev=nil)
|
100
154
|
|
101
155
|
doc, path = if rev
|
102
156
|
[ { '_id' => doc_or_path, '_rev' => rev }, doc_or_path ]
|
@@ -135,17 +189,9 @@ module Rufus::Jig
|
|
135
189
|
end
|
136
190
|
end
|
137
191
|
|
138
|
-
def post
|
139
|
-
|
140
|
-
path = adjust(path)
|
141
|
-
|
142
|
-
opts = { :content_type => :json }
|
143
|
-
|
144
|
-
if et = etag(path)
|
145
|
-
opts[:etag] = et
|
146
|
-
end
|
192
|
+
def post(path, doc, opts={})
|
147
193
|
|
148
|
-
@http.post(path, doc, opts)
|
194
|
+
@http.post(adjust(path), doc, opts.merge(:content_type => :json))
|
149
195
|
end
|
150
196
|
|
151
197
|
# Attaches a file to a couch document.
|
@@ -160,7 +206,7 @@ module Rufus::Jig
|
|
160
206
|
# doc, 'my_picture', data,
|
161
207
|
# :content_type => 'image/jpeg')
|
162
208
|
#
|
163
|
-
def attach
|
209
|
+
def attach(doc_id, doc_rev, attachment_name, data, opts=nil)
|
164
210
|
|
165
211
|
if opts.nil?
|
166
212
|
opts = data
|
@@ -183,23 +229,27 @@ module Rufus::Jig
|
|
183
229
|
path = adjust("#{doc_id}/#{attachment_name}?rev=#{doc_rev}")
|
184
230
|
|
185
231
|
if @http.variant == :patron
|
186
|
-
|
187
|
-
# patron, as of 0.4.5 has difficulties when PUTting
|
232
|
+
|
233
|
+
# patron, as of 0.4.5 (~> 0.4.10), has difficulties when PUTting
|
234
|
+
# attachements
|
188
235
|
# this is a fallback to net/http
|
189
|
-
|
236
|
+
|
190
237
|
require 'net/http'
|
238
|
+
|
191
239
|
http = Net::HTTP.new(@http.host, @http.port)
|
240
|
+
|
192
241
|
req = Net::HTTP::Put.new(path)
|
193
242
|
req['User-Agent'] =
|
194
|
-
"rufus-jig #{Rufus::Jig::VERSION} (patron 0.4.
|
243
|
+
"rufus-jig #{Rufus::Jig::VERSION} (patron 0.4.x fallback to net/http)"
|
195
244
|
req['Content-Type'] =
|
196
245
|
opts[:content_type]
|
246
|
+
req['Accept'] =
|
247
|
+
'application/json'
|
197
248
|
req.body = data
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
return nil
|
249
|
+
|
250
|
+
res = Rufus::Jig::HttpResponse.new(http.start { |h| h.request(req) })
|
251
|
+
|
252
|
+
return @http.send(:respond, :put, path, nil, opts, nil, res)
|
203
253
|
end
|
204
254
|
|
205
255
|
@http.put(path, data, opts)
|
@@ -213,7 +263,7 @@ module Rufus::Jig
|
|
213
263
|
#
|
214
264
|
# couch.detach(doc, 'my_picture')
|
215
265
|
#
|
216
|
-
def detach
|
266
|
+
def detach(doc_id, doc_rev, attachment_name=nil)
|
217
267
|
|
218
268
|
if attachment_name.nil?
|
219
269
|
attachment_name = doc_rev
|
@@ -244,7 +294,7 @@ module Rufus::Jig
|
|
244
294
|
# Note : doc inclusion (third parameter to the block) only works with
|
245
295
|
# CouchDB >= 0.11.
|
246
296
|
#
|
247
|
-
def on_change
|
297
|
+
def on_change(opts={}, &block)
|
248
298
|
|
249
299
|
query = {
|
250
300
|
'feed' => 'continuous',
|
@@ -299,6 +349,8 @@ module Rufus::Jig
|
|
299
349
|
on_change(opts, &block) if opts[:reconnect]
|
300
350
|
end
|
301
351
|
|
352
|
+
DESIGN_PATH_REGEX = /^\_design\//
|
353
|
+
|
302
354
|
# A development method. Removes all the design documents in this couch
|
303
355
|
# database.
|
304
356
|
#
|
@@ -309,14 +361,111 @@ module Rufus::Jig
|
|
309
361
|
|
310
362
|
docs = get('_all_docs')['rows']
|
311
363
|
|
312
|
-
views = docs.select { |d| d['id'] && d['id']
|
364
|
+
views = docs.select { |d| d['id'] && DESIGN_PATH_REGEX.match(d['id']) }
|
313
365
|
|
314
366
|
views.each { |v| delete(v['id'], v['value']['rev']) }
|
315
367
|
end
|
316
368
|
|
369
|
+
# Queries a view.
|
370
|
+
#
|
371
|
+
# res = couch.query('_design/my_test/_view/my_view')
|
372
|
+
# #
|
373
|
+
# # [ {"id"=>"c3", "key"=>"capuccino", "value"=>nil},
|
374
|
+
# # {"id"=>"c0", "key"=>"espresso", "value"=>nil},
|
375
|
+
# # {"id"=>"c2", "key"=>"macchiato", "value"=>nil},
|
376
|
+
# # {"id"=>"c4", "key"=>"macchiato", "value"=>nil},
|
377
|
+
# # {"id"=>"c1", "key"=>"ristretto", "value"=>nil} ]
|
378
|
+
#
|
379
|
+
# # or simply :
|
380
|
+
#
|
381
|
+
# res = couch.query('my_test:my_view')
|
382
|
+
#
|
383
|
+
# Accepts the usual couch parameters : limit, skip, descending, keys,
|
384
|
+
# startkey, endkey, ...
|
385
|
+
#
|
386
|
+
def query(path, opts={})
|
387
|
+
|
388
|
+
opts = opts.dup
|
389
|
+
# don't touch the original
|
390
|
+
|
391
|
+
raw = opts.delete(:raw)
|
392
|
+
|
393
|
+
path = if DESIGN_PATH_REGEX.match(path)
|
394
|
+
path
|
395
|
+
else
|
396
|
+
doc_id, view = path.split(':')
|
397
|
+
path = "_design/#{doc_id}/_view/#{view}"
|
398
|
+
end
|
399
|
+
|
400
|
+
path = adjust(path)
|
401
|
+
|
402
|
+
adjust_params(opts)
|
403
|
+
|
404
|
+
keys = opts.delete(:keys)
|
405
|
+
|
406
|
+
res = if keys
|
407
|
+
opts[:cache] = :with_body if opts[:cache].nil?
|
408
|
+
@http.post(path, { 'keys' => keys }, opts)
|
409
|
+
else
|
410
|
+
@http.get(path, opts)
|
411
|
+
end
|
412
|
+
|
413
|
+
return nil if res == true
|
414
|
+
# POST and the view doesn't exist
|
415
|
+
|
416
|
+
return res if raw
|
417
|
+
|
418
|
+
res.nil? ? res : res['rows']
|
419
|
+
end
|
420
|
+
|
421
|
+
# A shortcut for
|
422
|
+
#
|
423
|
+
# query(path, :include_docs => true).collect { |row| row['doc'] }
|
424
|
+
#
|
425
|
+
def query_for_docs(path, opts={})
|
426
|
+
|
427
|
+
res = query(path, opts.merge(:include_docs => true))
|
428
|
+
|
429
|
+
if res.nil?
|
430
|
+
nil
|
431
|
+
elsif opts[:raw]
|
432
|
+
res
|
433
|
+
else
|
434
|
+
res.collect { |row| row['doc'] }.uniq
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
# Creates or updates docs in bulk (could even delete).
|
439
|
+
#
|
440
|
+
# http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API#Modify_Multiple_Documents_With_a_Single_Request
|
441
|
+
#
|
442
|
+
def bulk_put(docs, opts={})
|
443
|
+
|
444
|
+
res = @http.post(adjust('_bulk_docs'), { 'docs' => docs })
|
445
|
+
|
446
|
+
opts[:raw] ?
|
447
|
+
res :
|
448
|
+
res.collect { |row| { '_id' => row['id'], '_rev' => row['rev'] } }
|
449
|
+
end
|
450
|
+
|
451
|
+
# Given an array of documents (at least { '_id' => x, '_rev' => y },
|
452
|
+
# deletes them.
|
453
|
+
#
|
454
|
+
def bulk_delete(docs, opts={})
|
455
|
+
|
456
|
+
docs = docs.inject([]) { |a, doc|
|
457
|
+
a << {
|
458
|
+
'_id' => doc['_id'], '_rev' => doc['_rev'], '_deleted' => true
|
459
|
+
} if doc
|
460
|
+
a
|
461
|
+
}
|
462
|
+
|
463
|
+
bulk_put(docs, opts)
|
464
|
+
end
|
465
|
+
|
317
466
|
protected
|
318
467
|
|
319
|
-
def adjust
|
468
|
+
def adjust(path)
|
320
469
|
|
321
470
|
case path
|
322
471
|
when '.' then @path
|
@@ -325,13 +474,27 @@ module Rufus::Jig
|
|
325
474
|
end
|
326
475
|
end
|
327
476
|
|
328
|
-
|
329
|
-
|
330
|
-
|
477
|
+
COUCH_PARAMS = %w[
|
478
|
+
key startkey endkey descending group group_level limit skip include_docs
|
479
|
+
].collect { |k| k.to_sym }
|
480
|
+
|
481
|
+
COUCH_KEYS = [ :key, :startkey, :endkey ]
|
482
|
+
|
483
|
+
def adjust_params(opts)
|
484
|
+
|
485
|
+
opts[:params] = opts.keys.inject({}) { |h, k|
|
331
486
|
|
332
|
-
|
487
|
+
if COUCH_PARAMS.include?(k)
|
488
|
+
v = opts.delete(k)
|
489
|
+
if COUCH_KEYS.include?(k)
|
490
|
+
h[k] = CGI.escape(Rufus::Json.encode(v))
|
491
|
+
elsif v != nil
|
492
|
+
h[k] = v
|
493
|
+
end
|
494
|
+
end
|
333
495
|
|
334
|
-
|
496
|
+
h
|
497
|
+
}
|
335
498
|
end
|
336
499
|
end
|
337
500
|
end
|
data/lib/rufus/jig/http.rb
CHANGED
@@ -22,7 +22,6 @@
|
|
22
22
|
# Made in Japan.
|
23
23
|
#++
|
24
24
|
|
25
|
-
require 'ostruct'
|
26
25
|
|
27
26
|
require 'rufus/lru' # gem install rufus-lru
|
28
27
|
|
@@ -34,7 +33,7 @@ module Rufus::Jig
|
|
34
33
|
# The classical helper method, does a full copy of the given object.
|
35
34
|
# Thanks Marshal.
|
36
35
|
#
|
37
|
-
def self.marshal_copy
|
36
|
+
def self.marshal_copy(o)
|
38
37
|
|
39
38
|
Marshal.load(Marshal.dump(o))
|
40
39
|
end
|
@@ -45,7 +44,7 @@ module Rufus::Jig
|
|
45
44
|
|
46
45
|
attr_reader :status
|
47
46
|
|
48
|
-
def initialize
|
47
|
+
def initialize(status, message)
|
49
48
|
|
50
49
|
@status = status
|
51
50
|
super(message)
|
@@ -56,56 +55,129 @@ module Rufus::Jig
|
|
56
55
|
#
|
57
56
|
class TimeoutError < RuntimeError
|
58
57
|
|
59
|
-
def initialize
|
58
|
+
def initialize(message=nil)
|
60
59
|
|
61
60
|
super(message || 'timed out')
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
64
|
+
#
|
65
|
+
# a Rufus::Jig wrapper for the server response.
|
66
|
+
#
|
65
67
|
class HttpResponse
|
66
68
|
|
67
69
|
attr_reader :status, :headers, :body
|
68
70
|
attr_reader :original
|
71
|
+
|
72
|
+
def initialize(res)
|
73
|
+
|
74
|
+
net_http_init(res)
|
75
|
+
end
|
76
|
+
|
77
|
+
def etag
|
78
|
+
|
79
|
+
headers['ETag'] || headers['Etag'] || headers['etag']
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
# (leveraged by the patron adapter as well)
|
85
|
+
#
|
86
|
+
def net_http_init(net_http_response)
|
87
|
+
|
88
|
+
@original = net_http_response
|
89
|
+
@status = net_http_response.code.to_i
|
90
|
+
@body = net_http_response.body
|
91
|
+
@headers = {}
|
92
|
+
net_http_response.each { |k, v|
|
93
|
+
@headers[k.split('-').collect { |s| s.capitalize }.join('-')] = v
|
94
|
+
}
|
95
|
+
end
|
69
96
|
end
|
70
97
|
|
71
|
-
|
98
|
+
#
|
99
|
+
# Rufus::Jig.parse_uri returns instances of this class.
|
100
|
+
#
|
101
|
+
class Uri
|
102
|
+
|
103
|
+
attr_accessor :scheme
|
104
|
+
attr_accessor :username, :password
|
105
|
+
attr_accessor :host, :port
|
106
|
+
attr_accessor :path, :query, :fragment
|
107
|
+
|
108
|
+
def initialize(sc, us, ps, ho, po, pa, qu, fr)
|
109
|
+
|
110
|
+
@scheme = sc
|
111
|
+
@username = us
|
112
|
+
@password = ps
|
113
|
+
@host = ho
|
114
|
+
@port = po
|
115
|
+
@path = pa
|
116
|
+
@query = qu
|
117
|
+
@fragment = fr
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_s
|
121
|
+
|
122
|
+
tail = tail_to_s
|
123
|
+
|
124
|
+
return tail unless @host
|
125
|
+
|
126
|
+
up = ''
|
127
|
+
up = "#{@username}:#{password}@" if @username
|
128
|
+
|
129
|
+
"#{@scheme}://#{up}#{@host}:#{@port}#{tail}"
|
130
|
+
end
|
131
|
+
|
132
|
+
def tail_to_s
|
133
|
+
|
134
|
+
tail = @path
|
135
|
+
tail = "#{tail}?#{@query}" if @query
|
136
|
+
tail = "#{tail}##{@fragment}" if @fragment
|
137
|
+
|
138
|
+
tail
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
URI_REGEX = /(https?):\/\/([^@]+:[^@]+@)?([^\/]+)(.*)?$/
|
143
|
+
PATH_REGEX = /([^\?#]*)(\?[^#]+)?(#[^#]+)?$/
|
72
144
|
|
73
145
|
# The current URI lib is not UTF-8 friendly, so this is a workaround.
|
74
146
|
# Temporary hopefully.
|
75
147
|
#
|
76
|
-
def self.parse_uri
|
148
|
+
def self.parse_uri(s)
|
77
149
|
|
78
150
|
m = URI_REGEX.match(s)
|
79
151
|
|
80
|
-
scheme, uname, pass, host, port,
|
152
|
+
scheme, uname, pass, host, port, tail = if m
|
81
153
|
|
82
154
|
ho, po = m[3].split(':')
|
83
155
|
po = (po || 80).to_i
|
84
156
|
|
85
|
-
query = m[5] ? m[5][1..-1] : nil
|
86
|
-
|
87
157
|
un, pa = m[2] ? m[2][0..-2].split(':') : [ nil, nil ]
|
88
158
|
|
89
|
-
[ m[1], un, pa, ho, po, m[4]
|
159
|
+
[ m[1], un, pa, ho, po, m[4] ]
|
90
160
|
|
91
161
|
else
|
92
162
|
|
93
|
-
|
94
|
-
|
95
|
-
[ nil, nil, nil, nil, nil, pa, qu ]
|
163
|
+
[ nil, nil, nil, nil, nil, s ]
|
96
164
|
end
|
97
165
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
166
|
+
m = PATH_REGEX.match(tail)
|
167
|
+
|
168
|
+
path, query, fragment = [ m[1], m[2], m[3] ]
|
169
|
+
|
170
|
+
port = 443 if scheme == 'https' && port == 80
|
171
|
+
query = query[1..-1] if query
|
172
|
+
fragment = fragment[1..-1] if fragment
|
173
|
+
|
174
|
+
Uri.new(scheme, uname, pass, host, port, path, query, fragment)
|
103
175
|
end
|
104
176
|
|
105
177
|
# The current URI lib is not UTF-8 friendly, so this is a workaround.
|
106
178
|
# Temporary hopefully.
|
107
179
|
#
|
108
|
-
def self.parse_host
|
180
|
+
def self.parse_host(s)
|
109
181
|
|
110
182
|
u = parse_uri(s)
|
111
183
|
|
@@ -140,9 +212,9 @@ module Rufus::Jig
|
|
140
212
|
# path, it is stored in @_path (and not used).
|
141
213
|
# Rufus::Jig::Couch uses it though.
|
142
214
|
#
|
143
|
-
attr_accessor :_path, :_query
|
215
|
+
attr_accessor :_path, :_query, :_fragment
|
144
216
|
|
145
|
-
def initialize
|
217
|
+
def initialize(*args)
|
146
218
|
|
147
219
|
@options = args.last.is_a?(Hash) ? args.pop.dup : {}
|
148
220
|
|
@@ -162,13 +234,19 @@ module Rufus::Jig
|
|
162
234
|
@options[:basic_auth] ||= [ u.username, u.password ] if u.username
|
163
235
|
|
164
236
|
if args[1]
|
165
|
-
|
237
|
+
uu = Rufus::Jig.parse_uri(args[1])
|
238
|
+
@_path = uu.path
|
239
|
+
@_query = uu.query
|
240
|
+
@_fragment = uu.fragment
|
166
241
|
else
|
167
242
|
@_path = u.path
|
168
243
|
@_query = u.query
|
244
|
+
@_fragment = u.fragment
|
169
245
|
end
|
170
246
|
end
|
171
247
|
|
248
|
+
@_path ||= ''
|
249
|
+
|
172
250
|
@cache = LruHash.new((@options[:cache_size] || 35).to_i)
|
173
251
|
|
174
252
|
if pf = @options[:prefix]
|
@@ -180,99 +258,79 @@ module Rufus::Jig
|
|
180
258
|
|
181
259
|
def uri
|
182
260
|
|
183
|
-
|
261
|
+
Uri.new(@scheme, nil, nil, @host, @port, nil, nil, nil)
|
184
262
|
end
|
185
263
|
|
186
|
-
def get
|
264
|
+
def get(path, opts={})
|
187
265
|
|
188
266
|
request(:get, path, nil, opts)
|
189
267
|
end
|
190
268
|
|
191
|
-
def post
|
269
|
+
def post(path, data, opts={})
|
192
270
|
|
193
271
|
request(:post, path, data, opts)
|
194
272
|
end
|
195
273
|
|
196
|
-
def put
|
274
|
+
def put(path, data, opts={})
|
197
275
|
|
198
276
|
request(:put, path, data, opts)
|
199
277
|
end
|
200
278
|
|
201
|
-
def delete
|
279
|
+
def delete(path, opts={})
|
202
280
|
|
203
281
|
request(:delete, path, nil, opts)
|
204
282
|
end
|
205
283
|
|
206
284
|
protected
|
207
285
|
|
208
|
-
def
|
209
|
-
|
210
|
-
if et = opts[:etag]
|
211
|
-
|
212
|
-
cached = @cache[path]
|
213
|
-
|
214
|
-
if cached && cached.first != et
|
215
|
-
#
|
216
|
-
# cached version is perhaps stale
|
217
|
-
#
|
218
|
-
cached = nil
|
219
|
-
opts.delete(:etag)
|
220
|
-
end
|
221
|
-
|
222
|
-
cached
|
223
|
-
|
224
|
-
else
|
225
|
-
|
226
|
-
nil
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
def request (method, path, data, opts={})
|
286
|
+
def request(method, path, data, opts={})
|
231
287
|
|
232
|
-
|
288
|
+
data_hash = data.hash
|
233
289
|
|
234
290
|
path = add_prefix(path, opts)
|
235
291
|
path = add_params(path, opts)
|
236
292
|
|
237
293
|
path = '/' if path == ''
|
238
294
|
|
239
|
-
cached = from_cache(path, opts)
|
240
|
-
opts.delete(:etag) if (not cached) || method != :get
|
295
|
+
cached = from_cache(method, path, data_hash, opts)
|
241
296
|
|
242
297
|
opts = rehash_options(opts)
|
243
298
|
data = repack_data(data, opts)
|
244
299
|
|
245
|
-
|
300
|
+
res = do_request(method, path, data, opts)
|
246
301
|
|
247
|
-
|
302
|
+
respond(method, path, data_hash, opts, cached, res)
|
303
|
+
end
|
248
304
|
|
249
|
-
|
305
|
+
def respond(method, path, data_hash, opts, cached, res)
|
250
306
|
|
251
|
-
|
252
|
-
return nil if method == :get && r.status == 404
|
253
|
-
return true if [ 404, 409 ].include?(r.status)
|
307
|
+
@last_response = res
|
254
308
|
|
255
|
-
|
256
|
-
|
257
|
-
end
|
309
|
+
raw = opts[:raw]
|
310
|
+
raw == false ? false : raw || @options[:raw]
|
258
311
|
|
259
|
-
|
312
|
+
unless raw
|
260
313
|
|
261
|
-
|
314
|
+
return Rufus::Jig.marshal_copy(cached.last) if res.status == 304
|
315
|
+
return nil if method == :get && res.status == 404
|
316
|
+
return true if [ 404, 409 ].include?(res.status)
|
262
317
|
|
263
|
-
|
264
|
-
|
318
|
+
if res.status >= 400 && res.status < 600
|
319
|
+
#File.open('error.html', 'wb') { |f| f.puts(res.body) }
|
320
|
+
raise @error_class.new(res.status, res.body)
|
321
|
+
end
|
322
|
+
end
|
265
323
|
|
266
|
-
|
324
|
+
b = decode_body(res, opts)
|
267
325
|
|
268
|
-
|
326
|
+
do_cache(method, path, data_hash, opts, res, b)
|
269
327
|
|
270
|
-
raw
|
328
|
+
raw ? res : b
|
271
329
|
end
|
272
330
|
|
273
331
|
# Should work with GET and POST/PUT options
|
274
332
|
#
|
275
|
-
def rehash_options
|
333
|
+
def rehash_options(opts)
|
276
334
|
|
277
335
|
opts['Accept'] ||= (opts.delete(:accept) || 'application/json')
|
278
336
|
opts['Accept'] = 'application/json' if opts['Accept'] == :json
|
@@ -291,7 +349,7 @@ module Rufus::Jig
|
|
291
349
|
opts
|
292
350
|
end
|
293
351
|
|
294
|
-
def add_prefix
|
352
|
+
def add_prefix(path, opts)
|
295
353
|
|
296
354
|
host = Rufus::Jig.parse_host(path)
|
297
355
|
|
@@ -306,7 +364,7 @@ module Rufus::Jig
|
|
306
364
|
Path.join(*elts)
|
307
365
|
end
|
308
366
|
|
309
|
-
def add_params
|
367
|
+
def add_params(path, opts)
|
310
368
|
|
311
369
|
if params = opts[:params]
|
312
370
|
|
@@ -322,42 +380,77 @@ module Rufus::Jig
|
|
322
380
|
path
|
323
381
|
end
|
324
382
|
|
325
|
-
|
383
|
+
APP_JSON_REGEX = /^application\/json/
|
326
384
|
|
327
|
-
|
385
|
+
def repack_data(data, opts)
|
328
386
|
|
387
|
+
return nil if data == nil
|
388
|
+
return data if data.is_a?(String)
|
329
389
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
Rufus::Json.encode(data)
|
390
|
+
ct = opts['Content-Type'] || ''
|
391
|
+
|
392
|
+
if APP_JSON_REGEX.match(ct)
|
393
|
+
return Rufus::Json.encode(data)
|
394
|
+
end
|
395
|
+
if ct == '' && (data.is_a?(Array) || data.is_a?(Hash))
|
396
|
+
opts['Content-Type'] = 'application/json'
|
397
|
+
return Rufus::Json.encode(data)
|
398
|
+
end
|
399
|
+
|
400
|
+
data.to_s
|
401
|
+
end
|
402
|
+
|
403
|
+
def from_cache(method, path, data_hash, opts)
|
404
|
+
|
405
|
+
path = if method == :post && opts[:cache] == :with_body
|
406
|
+
"#{path}//#{data_hash}"
|
334
407
|
else
|
335
|
-
|
408
|
+
path
|
336
409
|
end
|
337
410
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
411
|
+
etag = opts[:etag]
|
412
|
+
cached = @cache[path]
|
413
|
+
|
414
|
+
if etag && cached && cached.first != etag
|
415
|
+
# cached version is probably stale
|
416
|
+
cached = nil
|
417
|
+
opts.delete(:etag)
|
418
|
+
end
|
419
|
+
if ( ! etag) && cached
|
420
|
+
opts[:etag] = cached.first
|
421
|
+
end
|
342
422
|
|
343
|
-
|
423
|
+
cached
|
344
424
|
end
|
345
425
|
|
346
|
-
def do_cache
|
426
|
+
def do_cache(method, path, data_hash, opts, response, body)
|
427
|
+
|
428
|
+
path = if method == :post && opts[:cache] == :with_body
|
429
|
+
"#{path}//#{data_hash}"
|
430
|
+
else
|
431
|
+
path
|
432
|
+
end
|
347
433
|
|
348
|
-
|
434
|
+
etag = response.etag
|
435
|
+
|
436
|
+
if etag.nil? || opts[:cache] == false || method == :delete
|
437
|
+
@cache.delete(path)
|
438
|
+
return
|
439
|
+
end
|
440
|
+
if method != :get && ( ! opts[:cache])
|
349
441
|
@cache.delete(path)
|
350
|
-
|
351
|
-
@cache[path] = [ et, Rufus::Jig.marshal_copy(body) ]
|
442
|
+
return
|
352
443
|
end
|
444
|
+
|
445
|
+
@cache[path] = [ etag, Rufus::Jig.marshal_copy(body) ]
|
353
446
|
end
|
354
447
|
|
355
|
-
def decode_body
|
448
|
+
def decode_body(response, opts)
|
356
449
|
|
357
450
|
b = response.body
|
358
451
|
ct = response.headers['Content-Type']
|
359
452
|
|
360
|
-
if opts[:force_json] || (ct &&
|
453
|
+
if opts[:force_json] || (ct && APP_JSON_REGEX.match(ct))
|
361
454
|
Rufus::Json.decode(b)
|
362
455
|
else
|
363
456
|
b
|