rufus-jig 0.1.21 → 0.1.22
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +9 -1
- data/README.rdoc +12 -7
- data/TODO.txt +5 -3
- data/lib/rufus/jig/adapters/em.rb +19 -5
- data/lib/rufus/jig/adapters/net.rb +15 -23
- data/lib/rufus/jig/adapters/net_persistent.rb +101 -0
- data/lib/rufus/jig/adapters/net_response.rb +42 -0
- data/lib/rufus/jig/adapters/patron.rb +14 -9
- data/lib/rufus/jig/couch.rb +31 -2
- data/lib/rufus/jig/http.rb +59 -74
- data/lib/rufus/jig/version.rb +1 -1
- data/rufus-jig.gemspec +10 -3
- data/test/base.rb +10 -3
- data/test/bm/bm0.rb +49 -0
- data/test/bm/bm1.rb +43 -0
- data/test/couch_base.rb +16 -5
- data/test/couch_url.txt +1 -0
- data/test/ct_0_couch.rb +5 -5
- data/test/ct_1_couchdb.rb +6 -6
- data/test/ct_2_couchdb_options.rb +2 -6
- data/test/ct_3_couchdb_views.rb +2 -2
- data/test/ct_4_attachments.rb +2 -2
- data/test/ct_5_couchdb_continuous.rb +5 -3
- data/test/cut_0_auth_couch.rb +62 -0
- data/test/server.rb +51 -0
- data/test/test.rb +1 -1
- data/test/ut_0_http_get.rb +2 -2
- data/test/ut_6_args.rb +68 -59
- data/test/ut_7_parse_uri.rb +29 -2
- data/test/ut_8_auth.rb +37 -0
- metadata +10 -3
data/CHANGELOG.txt
CHANGED
@@ -2,7 +2,15 @@
|
|
2
2
|
= rufus-jig CHANGELOG.txt
|
3
3
|
|
4
4
|
|
5
|
-
== rufus-jig - 0.1.
|
5
|
+
== rufus-jig - 0.1.22 released 2010/09/30
|
6
|
+
|
7
|
+
- close methods are back
|
8
|
+
- basic authentication added
|
9
|
+
- net-http-persistent integration
|
10
|
+
- Rufus::Jig.parse_uri(s), uri now has scheme
|
11
|
+
|
12
|
+
|
13
|
+
== rufus-jig - 0.1.21 released 2010/09/21
|
6
14
|
|
7
15
|
- fixed port.to_i minor issue
|
8
16
|
- caching is only for GET !
|
data/README.rdoc
CHANGED
@@ -11,6 +11,11 @@ This library also contains some CouchDB helpers.
|
|
11
11
|
To select a HTTP transport layer for rufus-jig, just make sure you have loaded
|
12
12
|
the library before loading rufus-jig.
|
13
13
|
|
14
|
+
For net-http-persistent:
|
15
|
+
|
16
|
+
require 'net/http/persistent'
|
17
|
+
require 'rufus/jig'
|
18
|
+
|
14
19
|
For Patron:
|
15
20
|
|
16
21
|
require 'patron'
|
@@ -33,7 +38,7 @@ Let's say we have an hypothetical document server.
|
|
33
38
|
|
34
39
|
getting...
|
35
40
|
|
36
|
-
require '
|
41
|
+
require 'net/http/persistent'
|
37
42
|
require 'yajl'
|
38
43
|
require 'rufus/jig'
|
39
44
|
|
@@ -49,8 +54,6 @@ getting...
|
|
49
54
|
p [ r.status, r.body ]
|
50
55
|
#
|
51
56
|
# => [ 200, "letter:\n\nfour birds" ]
|
52
|
-
#
|
53
|
-
# else returns a Response object (currently the one of Patron)
|
54
57
|
|
55
58
|
j = @h.get('/document', :content_type => 'application/json')
|
56
59
|
j = @h.get('/document', :content_type => :json)
|
@@ -77,7 +80,7 @@ The class Rufus::Jig::Couch provides a get/put/delete trio that is couch-oriente
|
|
77
80
|
|
78
81
|
put and delete return nil in case of success and true (conflict) or and exception else.
|
79
82
|
|
80
|
-
require '
|
83
|
+
require 'net/http/persistent'
|
81
84
|
require 'yajl'
|
82
85
|
require 'rufus/jig'
|
83
86
|
|
@@ -108,9 +111,6 @@ put and delete return nil in case of success and true (conflict) or and exceptio
|
|
108
111
|
|
109
112
|
c.detach('coffee0', '1-x-newrevision-whatever', 'picture')
|
110
113
|
|
111
|
-
...
|
112
|
-
|
113
|
-
Warning : Patron, as of 0.4.5 is timing out when PUTting images.
|
114
114
|
|
115
115
|
=== on_change
|
116
116
|
|
@@ -155,6 +155,11 @@ To test the em-http-request HTTP transport, make sure the sinatra server is runn
|
|
155
155
|
ruby test/test.rb -- --em
|
156
156
|
|
157
157
|
|
158
|
+
== known issues
|
159
|
+
|
160
|
+
- the EM based client cannot deal with non-ASCII paths
|
161
|
+
|
162
|
+
|
158
163
|
== mailing list
|
159
164
|
|
160
165
|
On the rufus-ruby list :
|
data/TODO.txt
CHANGED
@@ -15,16 +15,18 @@
|
|
15
15
|
[x] don't abuse exceptions ! (especially with couch)
|
16
16
|
[o] couch.attach
|
17
17
|
[o] couch.detach ?
|
18
|
+
[o] net http persistent
|
19
|
+
http://seattlerb.rubyforge.org/net-http-persistent/Net/HTTP/Persistent.html
|
20
|
+
[o] HttpResponse : DRY net and netp
|
21
|
+
[x] patron : http can steal each other's patron session (inside the same thread)
|
18
22
|
|
19
23
|
[ ] HEAD
|
20
24
|
[ ] redirections ? (Patron seem to understand them)
|
21
|
-
[ ] patron : http can steal each other's patron session (inside the same thread)
|
22
25
|
[ ] why not a :force_json ?
|
23
26
|
|
24
27
|
[ ] make HttpCore detect do_request
|
25
28
|
|
26
29
|
[ ] timeout per request ?
|
27
30
|
|
28
|
-
[ ]
|
29
|
-
http://seattlerb.rubyforge.org/net-http-persistent/Net/HTTP/Persistent.html
|
31
|
+
[ ] netp : close() impl
|
30
32
|
|
@@ -48,9 +48,11 @@ end
|
|
48
48
|
|
49
49
|
class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
50
50
|
|
51
|
-
|
51
|
+
require 'uri'
|
52
52
|
|
53
|
-
|
53
|
+
def initialize( *args )
|
54
|
+
|
55
|
+
super( *args )
|
54
56
|
|
55
57
|
@options[:user_agent] ||= "#{self.class} #{Rufus::Jig::VERSION} (em)"
|
56
58
|
end
|
@@ -59,9 +61,14 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
|
59
61
|
:em
|
60
62
|
end
|
61
63
|
|
64
|
+
def close
|
65
|
+
|
66
|
+
# nothing to do
|
67
|
+
end
|
68
|
+
|
62
69
|
protected
|
63
70
|
|
64
|
-
def do_request
|
71
|
+
def do_request( method, path, data, opts )
|
65
72
|
|
66
73
|
args = {}
|
67
74
|
|
@@ -75,14 +82,19 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
|
75
82
|
args[:timeout] = 5.0 # like Patron
|
76
83
|
end
|
77
84
|
|
85
|
+
if auth = @options[:basic_auth]
|
86
|
+
args[:head].merge!( 'authorization' => auth )
|
87
|
+
end
|
88
|
+
|
78
89
|
em_response( em_request( path ).send( method, args ) )
|
79
90
|
end
|
80
91
|
|
81
92
|
def em_request( uri = '/' )
|
93
|
+
|
82
94
|
uri = Rufus::Jig.parse_uri( uri )
|
83
95
|
uri = URI::HTTP.build(
|
84
|
-
:host =>
|
85
|
-
:port =>
|
96
|
+
:host => uri.host || @host,
|
97
|
+
:port => uri.port || @port,
|
86
98
|
:path => uri.path,
|
87
99
|
:query => uri.query
|
88
100
|
)
|
@@ -91,6 +103,7 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
|
91
103
|
end
|
92
104
|
|
93
105
|
def em_response( em_client )
|
106
|
+
|
94
107
|
th = Thread.current
|
95
108
|
|
96
109
|
timedout = false
|
@@ -116,6 +129,7 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
|
116
129
|
end
|
117
130
|
|
118
131
|
def request_headers( options )
|
132
|
+
|
119
133
|
headers = { 'user-agent' => @options[:user_agent] }
|
120
134
|
|
121
135
|
%w[ Accept If-None-Match Content-Type ].each do |k|
|
@@ -22,39 +22,27 @@
|
|
22
22
|
# Made in Japan.
|
23
23
|
#++
|
24
24
|
|
25
|
-
require 'thread'
|
26
25
|
require 'net/http'
|
26
|
+
require 'rufus/jig/adapters/net_response'
|
27
27
|
|
28
28
|
|
29
|
-
class Rufus::Jig::HttpResponse
|
30
|
-
|
31
|
-
def initialize (nh_res)
|
32
|
-
|
33
|
-
@original = nh_res
|
34
|
-
@status = nh_res.code.to_i
|
35
|
-
@body = nh_res.body
|
36
|
-
@headers = {}
|
37
|
-
nh_res.each { |k, v|
|
38
|
-
@headers[k.split('-').collect { |s| s.capitalize }.join('-')] = v
|
39
|
-
}
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
29
|
class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
44
30
|
|
45
|
-
def initialize (
|
31
|
+
def initialize (*args)
|
46
32
|
|
47
|
-
super(
|
33
|
+
super(*args)
|
48
34
|
|
49
35
|
@options[:user_agent] ||= "#{self.class} #{Rufus::Jig::VERSION} (net/http)"
|
50
|
-
|
51
|
-
#@mutex = Mutex.new
|
52
36
|
end
|
53
37
|
|
54
38
|
def variant
|
55
39
|
:net
|
56
40
|
end
|
57
41
|
|
42
|
+
def close
|
43
|
+
# nothing to do
|
44
|
+
end
|
45
|
+
|
58
46
|
protected
|
59
47
|
|
60
48
|
def get_http (opts)
|
@@ -76,8 +64,6 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
|
76
64
|
|
77
65
|
def do_request (method, path, data, opts)
|
78
66
|
|
79
|
-
#@mutex.synchronize do
|
80
|
-
|
81
67
|
path = '/' if path == ''
|
82
68
|
|
83
69
|
req = eval("Net::HTTP::#{method.to_s.capitalize}").new(path)
|
@@ -85,14 +71,20 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
|
85
71
|
req['User-Agent'] = @options[:user_agent]
|
86
72
|
opts.each { |k, v| req[k] = v if k.is_a?(String) }
|
87
73
|
|
74
|
+
if auth = @options[:basic_auth]
|
75
|
+
req.basic_auth(*auth)
|
76
|
+
end
|
77
|
+
|
88
78
|
req.body = data ? data : ''
|
89
79
|
|
90
80
|
begin
|
91
|
-
|
81
|
+
http = get_http(opts)
|
82
|
+
Rufus::Jig::HttpResponse.new(http.start { |h| h.request(req) })
|
92
83
|
rescue Timeout::Error => te
|
93
84
|
raise Rufus::Jig::TimeoutError
|
85
|
+
ensure
|
86
|
+
http.finish rescue nil
|
94
87
|
end
|
95
|
-
#end
|
96
88
|
end
|
97
89
|
end
|
98
90
|
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009-2010, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
require 'rufus/jig/adapters/net_response'
|
27
|
+
|
28
|
+
|
29
|
+
#
|
30
|
+
# Re-opening Net::HTTP::Persistent to add a shutdown_in_all_threads method.
|
31
|
+
#
|
32
|
+
class Net::HTTP::Persistent
|
33
|
+
|
34
|
+
# Shuts down this instance's connection in all the threads.
|
35
|
+
#
|
36
|
+
# (to avoid too many open files issues).
|
37
|
+
#
|
38
|
+
def shutdown_in_all_threads
|
39
|
+
|
40
|
+
Thread.list.each do |t|
|
41
|
+
|
42
|
+
if cons = t[@connection_key]
|
43
|
+
cons.each { |_, connection| connection.finish rescue IOError }
|
44
|
+
end
|
45
|
+
|
46
|
+
t[@connection_key] = nil
|
47
|
+
t[@request_key] = nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
54
|
+
|
55
|
+
def initialize (*args)
|
56
|
+
|
57
|
+
super(*args)
|
58
|
+
|
59
|
+
@options[:user_agent] ||=
|
60
|
+
"#{self.class} #{Rufus::Jig::VERSION} (net/http/persistent)"
|
61
|
+
|
62
|
+
@http = Net::HTTP::Persistent.new
|
63
|
+
end
|
64
|
+
|
65
|
+
def variant
|
66
|
+
:net_persistent
|
67
|
+
end
|
68
|
+
|
69
|
+
# Closes the connection
|
70
|
+
#
|
71
|
+
def close
|
72
|
+
|
73
|
+
#@http.shutdown
|
74
|
+
@http.shutdown_in_all_threads
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def do_request (method, path, data, opts)
|
80
|
+
|
81
|
+
path = '/' if path == ''
|
82
|
+
|
83
|
+
req = eval("Net::HTTP::#{method.to_s.capitalize}").new(path)
|
84
|
+
|
85
|
+
req['User-Agent'] = @options[:user_agent]
|
86
|
+
opts.each { |k, v| req[k] = v if k.is_a?(String) }
|
87
|
+
|
88
|
+
if auth = @options[:basic_auth]
|
89
|
+
req.basic_auth(*auth)
|
90
|
+
end
|
91
|
+
|
92
|
+
req.body = data ? data : ''
|
93
|
+
|
94
|
+
begin
|
95
|
+
Rufus::Jig::HttpResponse.new(@http.request(uri, req))
|
96
|
+
rescue Timeout::Error => te
|
97
|
+
raise Rufus::Jig::TimeoutError
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009-2010, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
#
|
27
|
+
# a Rufus::Jig wrapper for the server response.
|
28
|
+
#
|
29
|
+
class Rufus::Jig::HttpResponse
|
30
|
+
|
31
|
+
def initialize (nh_res)
|
32
|
+
|
33
|
+
@original = nh_res
|
34
|
+
@status = nh_res.code.to_i
|
35
|
+
@body = nh_res.body
|
36
|
+
@headers = {}
|
37
|
+
nh_res.each { |k, v|
|
38
|
+
@headers[k.split('-').collect { |s| s.capitalize }.join('-')] = v
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -36,23 +36,22 @@ end
|
|
36
36
|
|
37
37
|
class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
38
38
|
|
39
|
-
def initialize (
|
39
|
+
def initialize (*args)
|
40
40
|
|
41
|
-
super(
|
42
|
-
end
|
43
|
-
|
44
|
-
def close
|
41
|
+
super(*args)
|
45
42
|
|
46
|
-
|
47
|
-
# session can get collected as garbage
|
48
|
-
|
49
|
-
#Thread.current[key] = nil
|
43
|
+
@options[:user_agent] ||= "#{self.class} #{Rufus::Jig::VERSION} (patron)"
|
50
44
|
end
|
51
45
|
|
52
46
|
def variant
|
53
47
|
:patron
|
54
48
|
end
|
55
49
|
|
50
|
+
def close
|
51
|
+
|
52
|
+
# nothing to do
|
53
|
+
end
|
54
|
+
|
56
55
|
protected
|
57
56
|
|
58
57
|
def get_patron (opts)
|
@@ -72,6 +71,12 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
|
|
72
71
|
@options[:user_agent] ||
|
73
72
|
[ self.class, Rufus::Jig::VERSION, '(patron)' ].join(' ')
|
74
73
|
|
74
|
+
if auth = @options[:basic_auth]
|
75
|
+
patron.auth_type = :basic
|
76
|
+
patron.username = auth[0]
|
77
|
+
patron.password = auth[1]
|
78
|
+
end
|
79
|
+
|
75
80
|
patron
|
76
81
|
end
|
77
82
|
|
data/lib/rufus/jig/couch.rb
CHANGED
@@ -22,6 +22,7 @@
|
|
22
22
|
# Made in Japan.
|
23
23
|
#++
|
24
24
|
|
25
|
+
require 'base64'
|
25
26
|
require 'socket'
|
26
27
|
# for #on_change
|
27
28
|
|
@@ -39,9 +40,9 @@ module Rufus::Jig
|
|
39
40
|
|
40
41
|
def initialize (*args)
|
41
42
|
|
42
|
-
@http
|
43
|
+
@http = Rufus::Jig::Http.new(*args)
|
43
44
|
|
44
|
-
@path
|
45
|
+
@path = @http._path || '/'
|
45
46
|
end
|
46
47
|
|
47
48
|
def name
|
@@ -248,15 +249,43 @@ module Rufus::Jig
|
|
248
249
|
query = {
|
249
250
|
'feed' => 'continuous',
|
250
251
|
'heartbeat' => opts[:heartbeat] || 20_000 }
|
252
|
+
#'since' => 0 } # that's already the default
|
251
253
|
query['include_docs'] = true if block.arity > 2
|
252
254
|
query = query.map { |k, v| "#{k}=#{v}" }.join('&')
|
253
255
|
|
254
256
|
socket = TCPSocket.open(@http.host, @http.port)
|
255
257
|
|
258
|
+
auth = @http.options[:basic_auth]
|
259
|
+
|
260
|
+
if auth
|
261
|
+
auth = Base64.encode64(auth.join(':')).strip
|
262
|
+
auth = "Authorization: Basic #{auth}\r\n"
|
263
|
+
else
|
264
|
+
auth = ''
|
265
|
+
end
|
266
|
+
|
256
267
|
socket.print("GET /#{path}/_changes?#{query} HTTP/1.1\r\n")
|
257
268
|
socket.print("User-Agent: rufus-jig #{Rufus::Jig::VERSION}\r\n")
|
269
|
+
#socket.print("Accept: application/json;charset=UTF-8\r\n")
|
270
|
+
socket.print(auth)
|
258
271
|
socket.print("\r\n")
|
259
272
|
|
273
|
+
# consider reply
|
274
|
+
|
275
|
+
answer = socket.gets.strip
|
276
|
+
status = answer.match(/^HTTP\/.+ (\d{3}) /)[1].to_i
|
277
|
+
|
278
|
+
raise Rufus::Jig::HttpError.new(status, answer) if status != 200
|
279
|
+
|
280
|
+
# discard headers
|
281
|
+
|
282
|
+
loop do
|
283
|
+
data = socket.gets
|
284
|
+
break if data.nil? || data == "\r\n"
|
285
|
+
end
|
286
|
+
|
287
|
+
# the on_change loop
|
288
|
+
|
260
289
|
loop do
|
261
290
|
data = socket.gets
|
262
291
|
break if data.nil?
|