httpadapter 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,20 +19,24 @@ require 'rack/response'
19
19
  require 'addressable/uri'
20
20
 
21
21
  module HTTPAdapter #:nodoc:
22
- class RackRequestAdapter
23
- def initialize(request, error_stream=STDERR)
24
- unless request.kind_of?(Rack::Request)
25
- raise TypeError, "Expected Rack::Request, got #{request.class}."
26
- end
27
- @request = request
28
- @error_stream = error_stream
22
+ class RackAdapter
23
+ include HTTPAdapter
24
+
25
+ def initialize(options={})
26
+ options = {
27
+ :error_stream => $stderr
28
+ }.merge(options)
29
+ @error_stream = options[:error_stream]
29
30
  end
30
31
 
31
- def to_ary
32
- method = @request.request_method.to_s.upcase
33
- uri = Addressable::URI.parse(@request.url.to_s).normalize.to_s
32
+ def convert_request_to_a(request_obj)
33
+ unless request_obj.kind_of?(Rack::Request)
34
+ raise TypeError, "Expected Rack::Request, got #{request_obj.class}."
35
+ end
36
+ method = request_obj.request_method.to_s.upcase
37
+ uri = Addressable::URI.parse(request_obj.url.to_s).normalize.to_s
34
38
  headers = []
35
- @request.env.each do |parameter, value|
39
+ request_obj.env.each do |parameter, value|
36
40
  next if parameter !~ /^HTTP_/
37
41
  # Ugh, lossy canonicalization again
38
42
  header = (parameter.gsub(/^HTTP_/, '').split('_').map do |chunk|
@@ -40,12 +44,12 @@ module HTTPAdapter #:nodoc:
40
44
  end).join('-')
41
45
  headers << [header, value]
42
46
  end
43
- return [method, uri, headers, @request.body]
47
+ return [method, uri, headers, request_obj.body]
44
48
  end
45
49
 
46
- def self.from_ary(array)
50
+ def convert_request_from_a(request_ary)
47
51
  # These contortions are really obnoxious; lossiness is bad!
48
- method, uri, headers, body = array
52
+ method, uri, headers, body = request_ary
49
53
  env = {}
50
54
  method = method.to_s.upcase
51
55
  uri = Addressable::URI.parse(uri)
@@ -88,26 +92,15 @@ module HTTPAdapter #:nodoc:
88
92
  return request
89
93
  end
90
94
 
91
- def self.transmit(request, connection=nil)
92
- raise NotImplementedError,
93
- 'No HTTP client implementation available to transmit a Rack::Request.'
94
- end
95
- end
96
-
97
- class RackResponseAdapter
98
- def initialize(response)
99
- unless response.kind_of?(Rack::Response)
100
- raise TypeError, "Expected Rack::Response, got #{response.class}."
95
+ def convert_response_to_a(response_obj)
96
+ unless response_obj.kind_of?(Rack::Response)
97
+ raise TypeError, "Expected Rack::Response, got #{response_obj.class}."
101
98
  end
102
- @response = response
103
- end
104
-
105
- def to_ary
106
- return @response.finish
99
+ return response_obj.finish
107
100
  end
108
101
 
109
- def self.from_ary(array)
110
- status, headers, body = array
102
+ def convert_response_from_a(request_ary)
103
+ status, headers, body = request_ary
111
104
  status = status.to_i
112
105
  body.each do |chunk|
113
106
  # Purely for strict type-checking
@@ -118,5 +111,10 @@ module HTTPAdapter #:nodoc:
118
111
  response = Rack::Response.new(body, status, Hash[headers])
119
112
  return response
120
113
  end
114
+
115
+ def fetch_resource(request_ary, connection=nil)
116
+ raise NotImplementedError,
117
+ 'No HTTP client implementation available to transmit a Rack::Request.'
118
+ end
121
119
  end
122
120
  end
@@ -19,27 +19,26 @@ require 'typhoeus/response'
19
19
  require 'addressable/uri'
20
20
 
21
21
  module HTTPAdapter #:nodoc:
22
- class TyphoeusRequestAdapter
23
- def initialize(request)
24
- unless request.kind_of?(Typhoeus::Request)
25
- raise TypeError, "Expected Typhoeus::Request, got #{request.class}."
26
- end
27
- @request = request
28
- end
22
+ class TyphoeusAdapter
23
+ include HTTPAdapter
29
24
 
30
- def to_ary
31
- method = @request.method.to_s.upcase
32
- uri = @request.url.to_str
25
+ def convert_request_to_a(request_obj)
26
+ unless request_obj.kind_of?(Typhoeus::Request)
27
+ raise TypeError,
28
+ "Expected Typhoeus::Request, got #{request_obj.class}."
29
+ end
30
+ method = request_obj.method.to_s.upcase
31
+ uri = request_obj.url.to_str
33
32
  headers = []
34
- @request.headers.each do |header, value|
33
+ request_obj.headers.each do |header, value|
35
34
  headers << [header, value]
36
35
  end
37
- body = @request.body || ""
36
+ body = request_obj.body || ""
38
37
  return [method, uri, headers, [body]]
39
38
  end
40
39
 
41
- def self.from_ary(array)
42
- method, uri, headers, body = array
40
+ def convert_request_from_a(request_ary)
41
+ method, uri, headers, body = request_ary
43
42
  method = method.to_s.downcase.to_sym
44
43
  uri = Addressable::URI.parse(uri)
45
44
  headers = Hash[headers]
@@ -59,41 +58,14 @@ module HTTPAdapter #:nodoc:
59
58
  return request
60
59
  end
61
60
 
62
- def self.transmit(request, connection=nil)
63
- method, uri, headers, body = request
64
- uri = Addressable::URI.parse(uri)
65
- typhoeus_request = self.from_ary([method, uri, headers, body])
66
- typhoeus_response = nil
67
- unless connection
68
- hydra = Typhoeus::Hydra.new
69
- connection = HTTPAdapter::Connection.new(
70
- uri.host, uri.inferred_port, hydra,
71
- :join => [:run, [], nil]
72
- )
73
- else
74
- http = nil
75
- end
76
- typhoeus_request.on_complete do |response|
77
- typhoeus_response = response
78
- end
79
- connection.connection.queue(typhoeus_request)
80
- connection.join
81
- return TyphoeusResponseAdapter.new(typhoeus_response).to_ary
82
- end
83
- end
84
-
85
- class TyphoeusResponseAdapter
86
- def initialize(response)
87
- unless response.kind_of?(Typhoeus::Response)
88
- raise TypeError, "Expected Typhoeus::Response, got #{response.class}."
61
+ def convert_response_to_a(response_obj)
62
+ unless response_obj.kind_of?(Typhoeus::Response)
63
+ raise TypeError,
64
+ "Expected Typhoeus::Response, got #{response_obj.class}."
89
65
  end
90
- @response = response
91
- end
92
-
93
- def to_ary
94
- status = @response.code.to_i
66
+ status = response_obj.code.to_i
95
67
  headers = []
96
- @response.headers_hash.each do |header, value|
68
+ response_obj.headers_hash.each do |header, value|
97
69
  # Eh? Seriously? This is NOT a header!
98
70
  next if header =~ /^HTTP\/\d\.\d \d{3} .+$/
99
71
  if value.kind_of?(Array)
@@ -105,12 +77,12 @@ module HTTPAdapter #:nodoc:
105
77
  headers << [header, value]
106
78
  end
107
79
  end
108
- body = @response.body || ""
80
+ body = response_obj.body || ""
109
81
  return [status, headers, [body]]
110
82
  end
111
83
 
112
- def self.from_ary(array)
113
- status, headers, body = array
84
+ def convert_response_from_a(request_ary)
85
+ status, headers, body = request_ary
114
86
  status = status.to_i
115
87
  merged_body = ""
116
88
  body.each do |chunk|
@@ -123,5 +95,29 @@ module HTTPAdapter #:nodoc:
123
95
  )
124
96
  return response
125
97
  end
98
+
99
+ def fetch_resource(request_ary, connection=nil)
100
+ method, uri, headers, body = request_ary
101
+ uri = Addressable::URI.parse(uri)
102
+ typhoeus_request = self.convert_request_from_a(
103
+ [method, uri, headers, body]
104
+ )
105
+ typhoeus_response = nil
106
+ unless connection
107
+ hydra = Typhoeus::Hydra.new
108
+ connection = HTTPAdapter::Connection.new(
109
+ uri.host, uri.inferred_port, hydra,
110
+ :join => [:run, [], nil]
111
+ )
112
+ else
113
+ http = nil
114
+ end
115
+ typhoeus_request.on_complete do |response|
116
+ typhoeus_response = response
117
+ end
118
+ connection.connection.queue(typhoeus_request)
119
+ connection.join
120
+ return self.convert_response_to_a(typhoeus_response)
121
+ end
126
122
  end
127
123
  end
@@ -12,8 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'httpadapter'
16
-
17
15
  module HTTPAdapter #:nodoc:
18
16
  class Connection
19
17
  def initialize(host, port, connection, options={})
@@ -16,9 +16,9 @@
16
16
  unless defined? HTTPAdapter::VERSION
17
17
  module HTTPAdapter #:nodoc:
18
18
  module VERSION #:nodoc:
19
- MAJOR = 0
20
- MINOR = 2
21
- TINY = 1
19
+ MAJOR = 1
20
+ MINOR = 0
21
+ TINY = 0
22
22
 
23
23
  STRING = [MAJOR, MINOR, TINY].join('.')
24
24
  end
@@ -0,0 +1,170 @@
1
+ # Copyright (C) 2010 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ shared_examples_for 'adapter type-checking example' do
16
+ before do
17
+ @request = ['GET', '/', [], ['']]
18
+ @response = [200, [], ['']]
19
+ end
20
+
21
+ describe 'when attempting to specialize a request' do
22
+ it 'should raise an error for converting from an invalid tuple' do
23
+ (lambda do
24
+ @adapter.specialize_request(42)
25
+ end).should raise_error(TypeError)
26
+ end
27
+
28
+ it 'should raise an error for converting from an invalid tuple' do
29
+ (lambda do
30
+ @adapter.specialize_request([42])
31
+ end).should raise_error(TypeError)
32
+ end
33
+
34
+ it 'should raise an error for converting from an invalid tuple' do
35
+ (lambda do
36
+ @adapter.specialize_request([42, 42, 42, 42])
37
+ end).should raise_error(TypeError)
38
+ end
39
+
40
+ it 'should raise an error for converting from an invalid tuple' do
41
+ (lambda do
42
+ @adapter.specialize_request(['GET', 42, [], ['']])
43
+ end).should raise_error(TypeError)
44
+ end
45
+
46
+ it 'should raise an error for converting from an invalid tuple' do
47
+ (lambda do
48
+ @adapter.specialize_request(['GET', '/', 42, ['']])
49
+ end).should raise_error(TypeError)
50
+ end
51
+
52
+ it 'should raise an error for converting from an invalid tuple' do
53
+ (lambda do
54
+ @adapter.specialize_request(['GET', '/', [42], ['']])
55
+ end).should raise_error(TypeError)
56
+ end
57
+
58
+ it 'should raise an error for converting from an invalid tuple' do
59
+ (lambda do
60
+ @adapter.specialize_request(['GET', '/', [[42, 'value']], ['']])
61
+ end).should raise_error(TypeError)
62
+ end
63
+
64
+ it 'should raise an error for converting from an invalid tuple' do
65
+ (lambda do
66
+ @adapter.specialize_request(['GET', '/', [['X', 42]], ['']])
67
+ end).should raise_error(TypeError)
68
+ end
69
+
70
+ it 'should raise an error for converting from an invalid tuple' do
71
+ (lambda do
72
+ @adapter.specialize_request(['GET', '/', [], 42])
73
+ end).should raise_error(TypeError)
74
+ end
75
+
76
+ it 'should raise an error for converting from an invalid tuple' do
77
+ (lambda do
78
+ # Note that the body value here should be [''], not ''.
79
+ @adapter.specialize_request(['GET', '/', [], ''])
80
+ end).should raise_error(TypeError)
81
+ end
82
+ end
83
+
84
+ describe 'when attempting to specialize a response' do
85
+ it 'should raise an error for converting from an invalid tuple' do
86
+ (lambda do
87
+ @adapter.specialize_response(42)
88
+ end).should raise_error(TypeError)
89
+ end
90
+
91
+ it 'should raise an error for converting from an invalid tuple' do
92
+ (lambda do
93
+ @adapter.specialize_response([42])
94
+ end).should raise_error(TypeError)
95
+ end
96
+
97
+ it 'should raise an error for converting from an invalid tuple' do
98
+ (lambda do
99
+ @adapter.specialize_response([42, 42, 42])
100
+ end).should raise_error(TypeError)
101
+ end
102
+
103
+ it 'should raise an error for converting from an invalid tuple' do
104
+ (lambda do
105
+ @adapter.specialize_response([Object.new, [], ['']])
106
+ end).should raise_error(TypeError)
107
+ end
108
+
109
+ it 'should raise an error for converting from an invalid tuple' do
110
+ (lambda do
111
+ @adapter.specialize_response(['', 42, ['']])
112
+ end).should raise_error(TypeError)
113
+ end
114
+
115
+ it 'should raise an error for converting from an invalid tuple' do
116
+ (lambda do
117
+ @adapter.specialize_response([200, [42], ['']])
118
+ end).should raise_error(TypeError)
119
+ end
120
+
121
+ it 'should raise an error for converting from an invalid tuple' do
122
+ (lambda do
123
+ @adapter.specialize_response([200, [[42, 'value']], ['']])
124
+ end).should raise_error(TypeError)
125
+ end
126
+
127
+ it 'should raise an error for converting from an invalid tuple' do
128
+ (lambda do
129
+ @adapter.specialize_response([200, [['X', 42]], ['']])
130
+ end).should raise_error(TypeError)
131
+ end
132
+
133
+ it 'should raise an error for converting from an invalid tuple' do
134
+ (lambda do
135
+ @adapter.specialize_response([200, [], 42])
136
+ end).should raise_error(TypeError)
137
+ end
138
+
139
+ it 'should raise an error for converting from an invalid tuple' do
140
+ (lambda do
141
+ # Note that the body value here should be [''], not ''.
142
+ @adapter.specialize_response([200, [], ''])
143
+ end).should raise_error(TypeError)
144
+ end
145
+ end
146
+
147
+ it 'should raise an error for invalid adapt request calls' do
148
+ (lambda do
149
+ @adapter.adapt_request(42)
150
+ end).should raise_error(TypeError)
151
+ end
152
+
153
+ it 'should raise an error for invalid adapt response calls' do
154
+ (lambda do
155
+ @adapter.adapt_response(42)
156
+ end).should raise_error(TypeError)
157
+ end
158
+
159
+ it 'should raise an error for invalid transmission calls' do
160
+ (lambda do
161
+ @adapter.transmit(42)
162
+ end).should raise_error(TypeError)
163
+ end
164
+
165
+ it 'should raise an error for invalid transmission calls' do
166
+ (lambda do
167
+ @adapter.transmit(@request, 42)
168
+ end).should raise_error(TypeError)
169
+ end
170
+ end
@@ -0,0 +1,81 @@
1
+ # Copyright (C) 2010 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'spec_helper'
16
+ require 'spec/httpadapter/adapter_type_checking_spec'
17
+
18
+ require 'httpadapter/adapters/mock'
19
+
20
+ describe HTTPAdapter::MockAdapter do
21
+ describe 'with no response mocked' do
22
+ before do
23
+ @adapter = HTTPAdapter::MockAdapter.create do |request_ary, connection|
24
+ method, uri, headers, body = request_ary
25
+ headers.should be_any { |k, v| k.downcase == 'user-agent' }
26
+ end
27
+ end
28
+
29
+ it_should_behave_like 'adapter type-checking example'
30
+
31
+ describe 'when transmitting a request' do
32
+ it 'should have an expectation failure' do
33
+ (lambda do
34
+ @adapter.transmit(['GET', 'http://www.google.com/', [], ['']])
35
+ end).should raise_error(Spec::Expectations::ExpectationNotMetError)
36
+ end
37
+
38
+ it 'should meet all expectations' do
39
+ response = @adapter.transmit([
40
+ 'GET',
41
+ 'http://www.google.com/',
42
+ [['User-Agent', 'Mock Agent']],
43
+ ['']
44
+ ])
45
+ status, headers, body = response
46
+ status.should == 200
47
+ end
48
+ end
49
+ end
50
+
51
+ describe 'with a mocked response' do
52
+ before do
53
+ @adapter = HTTPAdapter::MockAdapter.create do |request_ary, connection|
54
+ method, uri, headers, body = request_ary
55
+ headers.should be_any { |k, v| k.downcase == 'user-agent' }
56
+ [400, [], ['']]
57
+ end
58
+ end
59
+
60
+ it_should_behave_like 'adapter type-checking example'
61
+
62
+ describe 'when transmitting a request' do
63
+ it 'should have an expectation failure' do
64
+ (lambda do
65
+ @adapter.transmit(['GET', 'http://www.google.com/', [], ['']])
66
+ end).should raise_error(Spec::Expectations::ExpectationNotMetError)
67
+ end
68
+
69
+ it 'should meet all expectations' do
70
+ response = @adapter.transmit([
71
+ 'GET',
72
+ 'http://www.google.com/',
73
+ [['User-Agent', 'Mock Agent']],
74
+ ['']
75
+ ])
76
+ status, headers, body = response
77
+ status.should == 400
78
+ end
79
+ end
80
+ end
81
+ end