rufus-jig 0.1.23 → 1.0.0
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.
- 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
|