activeresource 2.3.18 → 3.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activeresource might be problematic. Click here for more details.
- data/CHANGELOG +1 -29
- data/README +7 -7
- data/examples/simple.rb +15 -0
- data/lib/active_resource.rb +17 -17
- data/lib/active_resource/base.rb +320 -66
- data/lib/active_resource/connection.rb +100 -100
- data/lib/active_resource/custom_methods.rb +33 -36
- data/lib/active_resource/exceptions.rb +4 -1
- data/lib/active_resource/formats.rb +4 -4
- data/lib/active_resource/formats/json_format.rb +2 -0
- data/lib/active_resource/formats/xml_format.rb +2 -0
- data/lib/active_resource/http_mock.rb +12 -103
- data/lib/active_resource/observing.rb +21 -0
- data/lib/active_resource/railtie.rb +17 -0
- data/lib/active_resource/railties/subscriber.rb +15 -0
- data/lib/active_resource/schema.rb +55 -0
- data/lib/active_resource/validations.rb +66 -215
- data/lib/active_resource/version.rb +3 -3
- metadata +29 -43
- data/Rakefile +0 -137
- data/lib/activeresource.rb +0 -2
- data/test/abstract_unit.rb +0 -21
- data/test/authorization_test.rb +0 -122
- data/test/base/custom_methods_test.rb +0 -100
- data/test/base/equality_test.rb +0 -52
- data/test/base/load_test.rb +0 -161
- data/test/base_errors_test.rb +0 -98
- data/test/base_test.rb +0 -1087
- data/test/connection_test.rb +0 -238
- data/test/fixtures/beast.rb +0 -14
- data/test/fixtures/customer.rb +0 -3
- data/test/fixtures/person.rb +0 -3
- data/test/fixtures/proxy.rb +0 -4
- data/test/fixtures/street_address.rb +0 -4
- data/test/format_test.rb +0 -112
- data/test/http_mock_test.rb +0 -155
- data/test/setter_trap.rb +0 -26
@@ -1,75 +1,10 @@
|
|
1
|
+
require 'active_support/core_ext/benchmark'
|
1
2
|
require 'net/https'
|
2
3
|
require 'date'
|
3
4
|
require 'time'
|
4
5
|
require 'uri'
|
5
|
-
require 'benchmark'
|
6
6
|
|
7
7
|
module ActiveResource
|
8
|
-
class ConnectionError < StandardError # :nodoc:
|
9
|
-
attr_reader :response
|
10
|
-
|
11
|
-
def initialize(response, message = nil)
|
12
|
-
@response = response
|
13
|
-
@message = message
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_s
|
17
|
-
"Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# Raised when a Timeout::Error occurs.
|
22
|
-
class TimeoutError < ConnectionError
|
23
|
-
def initialize(message)
|
24
|
-
@message = message
|
25
|
-
end
|
26
|
-
def to_s; @message ;end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Raised when a OpenSSL::SSL::SSLError occurs.
|
30
|
-
class SSLError < ConnectionError
|
31
|
-
def initialize(message)
|
32
|
-
@message = message
|
33
|
-
end
|
34
|
-
def to_s; @message ;end
|
35
|
-
end
|
36
|
-
|
37
|
-
# 3xx Redirection
|
38
|
-
class Redirection < ConnectionError # :nodoc:
|
39
|
-
def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
|
40
|
-
end
|
41
|
-
|
42
|
-
# 4xx Client Error
|
43
|
-
class ClientError < ConnectionError; end # :nodoc:
|
44
|
-
|
45
|
-
# 400 Bad Request
|
46
|
-
class BadRequest < ClientError; end # :nodoc
|
47
|
-
|
48
|
-
# 401 Unauthorized
|
49
|
-
class UnauthorizedAccess < ClientError; end # :nodoc
|
50
|
-
|
51
|
-
# 403 Forbidden
|
52
|
-
class ForbiddenAccess < ClientError; end # :nodoc
|
53
|
-
|
54
|
-
# 404 Not Found
|
55
|
-
class ResourceNotFound < ClientError; end # :nodoc:
|
56
|
-
|
57
|
-
# 409 Conflict
|
58
|
-
class ResourceConflict < ClientError; end # :nodoc:
|
59
|
-
|
60
|
-
# 410 Gone
|
61
|
-
class ResourceGone < ClientError; end # :nodoc:
|
62
|
-
|
63
|
-
# 5xx Server Error
|
64
|
-
class ServerError < ConnectionError; end # :nodoc:
|
65
|
-
|
66
|
-
# 405 Method Not Allowed
|
67
|
-
class MethodNotAllowed < ClientError # :nodoc:
|
68
|
-
def allowed_methods
|
69
|
-
@response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
8
|
# Class to handle connections to remote web services.
|
74
9
|
# This class is used by ActiveResource::Base to interface with REST
|
75
10
|
# services.
|
@@ -82,7 +17,7 @@ module ActiveResource
|
|
82
17
|
:head => 'Accept'
|
83
18
|
}
|
84
19
|
|
85
|
-
attr_reader :site, :user, :password, :timeout, :proxy, :ssl_options
|
20
|
+
attr_reader :site, :user, :password, :auth_type, :timeout, :proxy, :ssl_options
|
86
21
|
attr_accessor :format
|
87
22
|
|
88
23
|
class << self
|
@@ -93,36 +28,42 @@ module ActiveResource
|
|
93
28
|
|
94
29
|
# The +site+ parameter is required and will set the +site+
|
95
30
|
# attribute to the URI for the remote resource service.
|
96
|
-
def initialize(site, format = ActiveResource::Formats
|
31
|
+
def initialize(site, format = ActiveResource::Formats::XmlFormat)
|
97
32
|
raise ArgumentError, 'Missing site URI' unless site
|
98
33
|
@user = @password = nil
|
34
|
+
@uri_parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
99
35
|
self.site = site
|
100
36
|
self.format = format
|
101
37
|
end
|
102
38
|
|
103
39
|
# Set URI for remote service.
|
104
40
|
def site=(site)
|
105
|
-
@site = site.is_a?(URI) ? site :
|
106
|
-
@user =
|
107
|
-
@password =
|
41
|
+
@site = site.is_a?(URI) ? site : @uri_parser.parse(site)
|
42
|
+
@user = @uri_parser.unescape(@site.user) if @site.user
|
43
|
+
@password = @uri_parser.unescape(@site.password) if @site.password
|
108
44
|
end
|
109
45
|
|
110
46
|
# Set the proxy for remote service.
|
111
47
|
def proxy=(proxy)
|
112
|
-
@proxy = proxy.is_a?(URI) ? proxy :
|
48
|
+
@proxy = proxy.is_a?(URI) ? proxy : @uri_parser.parse(proxy)
|
113
49
|
end
|
114
50
|
|
115
|
-
#
|
51
|
+
# Sets the user for remote service.
|
116
52
|
def user=(user)
|
117
53
|
@user = user
|
118
54
|
end
|
119
55
|
|
120
|
-
#
|
56
|
+
# Sets the password for remote service.
|
121
57
|
def password=(password)
|
122
58
|
@password = password
|
123
59
|
end
|
124
60
|
|
125
|
-
#
|
61
|
+
# Sets the auth type for remote service.
|
62
|
+
def auth_type=(auth_type)
|
63
|
+
@auth_type = legitimize_auth_type(auth_type)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Sets the number of seconds after which HTTP requests to the remote service should time out.
|
126
67
|
def timeout=(timeout)
|
127
68
|
@timeout = timeout
|
128
69
|
end
|
@@ -132,44 +73,44 @@ module ActiveResource
|
|
132
73
|
@ssl_options = opts
|
133
74
|
end
|
134
75
|
|
135
|
-
#
|
76
|
+
# Executes a GET request.
|
136
77
|
# Used to get (find) resources.
|
137
78
|
def get(path, headers = {})
|
138
|
-
format.decode(request(:get, path, build_request_headers(headers, :get)).body)
|
79
|
+
with_auth { format.decode(request(:get, path, build_request_headers(headers, :get, self.site.merge(path))).body) }
|
139
80
|
end
|
140
81
|
|
141
|
-
#
|
82
|
+
# Executes a DELETE request (see HTTP protocol documentation if unfamiliar).
|
142
83
|
# Used to delete resources.
|
143
84
|
def delete(path, headers = {})
|
144
|
-
request(:delete, path, build_request_headers(headers, :delete))
|
85
|
+
with_auth { request(:delete, path, build_request_headers(headers, :delete, self.site.merge(path))) }
|
145
86
|
end
|
146
87
|
|
147
|
-
#
|
88
|
+
# Executes a PUT request (see HTTP protocol documentation if unfamiliar).
|
148
89
|
# Used to update resources.
|
149
90
|
def put(path, body = '', headers = {})
|
150
|
-
request(:put, path, body.to_s, build_request_headers(headers, :put))
|
91
|
+
with_auth { request(:put, path, body.to_s, build_request_headers(headers, :put, self.site.merge(path))) }
|
151
92
|
end
|
152
93
|
|
153
|
-
#
|
94
|
+
# Executes a POST request.
|
154
95
|
# Used to create new resources.
|
155
96
|
def post(path, body = '', headers = {})
|
156
|
-
request(:post, path, body.to_s, build_request_headers(headers, :post))
|
97
|
+
with_auth { request(:post, path, body.to_s, build_request_headers(headers, :post, self.site.merge(path))) }
|
157
98
|
end
|
158
99
|
|
159
|
-
#
|
100
|
+
# Executes a HEAD request.
|
160
101
|
# Used to obtain meta-information about resources, such as whether they exist and their size (via response headers).
|
161
102
|
def head(path, headers = {})
|
162
|
-
request(:head, path, build_request_headers(headers, :head))
|
103
|
+
with_auth { request(:head, path, build_request_headers(headers, :head, self.site.merge(path))) }
|
163
104
|
end
|
164
105
|
|
165
|
-
|
166
106
|
private
|
167
|
-
# Makes request to remote service.
|
107
|
+
# Makes a request to the remote service.
|
168
108
|
def request(method, path, *arguments)
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
109
|
+
result = ActiveSupport::Notifications.instrument("active_resource.request") do |payload|
|
110
|
+
payload[:method] = method
|
111
|
+
payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}"
|
112
|
+
payload[:result] = http.send(method, path, *arguments)
|
113
|
+
end
|
173
114
|
handle_response(result)
|
174
115
|
rescue Timeout::Error => e
|
175
116
|
raise TimeoutError.new(e.message)
|
@@ -177,7 +118,7 @@ module ActiveResource
|
|
177
118
|
raise SSLError.new(e.message)
|
178
119
|
end
|
179
120
|
|
180
|
-
# Handles response and error codes from remote service.
|
121
|
+
# Handles response and error codes from the remote service.
|
181
122
|
def handle_response(response)
|
182
123
|
case response.code.to_i
|
183
124
|
when 301,302
|
@@ -209,7 +150,7 @@ module ActiveResource
|
|
209
150
|
end
|
210
151
|
end
|
211
152
|
|
212
|
-
# Creates new Net::HTTP instance for communication with
|
153
|
+
# Creates new Net::HTTP instance for communication with the
|
213
154
|
# remote service and resources.
|
214
155
|
def http
|
215
156
|
configure_http(new_http)
|
@@ -263,21 +204,80 @@ module ActiveResource
|
|
263
204
|
end
|
264
205
|
|
265
206
|
# Builds headers for request to remote service.
|
266
|
-
def build_request_headers(headers, http_method
|
267
|
-
authorization_header.update(default_header).update(http_format_header(http_method)).update(headers)
|
207
|
+
def build_request_headers(headers, http_method, uri)
|
208
|
+
authorization_header(http_method, uri).update(default_header).update(http_format_header(http_method)).update(headers)
|
209
|
+
end
|
210
|
+
|
211
|
+
def response_auth_header
|
212
|
+
@response_auth_header ||= ""
|
213
|
+
end
|
214
|
+
|
215
|
+
def with_auth
|
216
|
+
retried ||= false
|
217
|
+
yield
|
218
|
+
rescue UnauthorizedAccess => e
|
219
|
+
raise if retried || auth_type != :digest
|
220
|
+
@response_auth_header = e.response['WWW-Authenticate']
|
221
|
+
retried = true
|
222
|
+
retry
|
223
|
+
end
|
224
|
+
|
225
|
+
def authorization_header(http_method, uri)
|
226
|
+
if @user || @password
|
227
|
+
if auth_type == :digest
|
228
|
+
{ 'Authorization' => digest_auth_header(http_method, uri) }
|
229
|
+
else
|
230
|
+
{ 'Authorization' => 'Basic ' + ["#{@user}:#{@password}"].pack('m').delete("\r\n") }
|
231
|
+
end
|
232
|
+
else
|
233
|
+
{}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def digest_auth_header(http_method, uri)
|
238
|
+
params = extract_params_from_response
|
239
|
+
|
240
|
+
ha1 = Digest::MD5.hexdigest("#{@user}:#{params['realm']}:#{@password}")
|
241
|
+
ha2 = Digest::MD5.hexdigest("#{http_method.to_s.upcase}:#{uri.path}")
|
242
|
+
|
243
|
+
params.merge!('cnonce' => client_nonce)
|
244
|
+
request_digest = Digest::MD5.hexdigest([ha1, params['nonce'], "0", params['cnonce'], params['qop'], ha2].join(":"))
|
245
|
+
"Digest #{auth_attributes_for(uri, request_digest, params)}"
|
246
|
+
end
|
247
|
+
|
248
|
+
def client_nonce
|
249
|
+
Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535)))
|
250
|
+
end
|
251
|
+
|
252
|
+
def extract_params_from_response
|
253
|
+
params = {}
|
254
|
+
if response_auth_header =~ /^(\w+) (.*)/
|
255
|
+
$2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
|
256
|
+
end
|
257
|
+
params
|
268
258
|
end
|
269
259
|
|
270
|
-
|
271
|
-
|
272
|
-
|
260
|
+
def auth_attributes_for(uri, request_digest, params)
|
261
|
+
[
|
262
|
+
%Q(username="#{@user}"),
|
263
|
+
%Q(realm="#{params['realm']}"),
|
264
|
+
%Q(qop="#{params['qop']}"),
|
265
|
+
%Q(uri="#{uri.path}"),
|
266
|
+
%Q(nonce="#{params['nonce']}"),
|
267
|
+
%Q(nc="0"),
|
268
|
+
%Q(cnonce="#{params['cnonce']}"),
|
269
|
+
%Q(opaque="#{params['opaque']}"),
|
270
|
+
%Q(response="#{request_digest}")].join(", ")
|
273
271
|
end
|
274
272
|
|
275
273
|
def http_format_header(http_method)
|
276
274
|
{HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type}
|
277
275
|
end
|
278
276
|
|
279
|
-
def
|
280
|
-
|
277
|
+
def legitimize_auth_type(auth_type)
|
278
|
+
return :basic if auth_type.nil?
|
279
|
+
auth_type = auth_type.to_sym
|
280
|
+
[:basic, :digest].include?(auth_type) ? auth_type : :basic
|
281
281
|
end
|
282
282
|
end
|
283
283
|
end
|
@@ -31,47 +31,44 @@ module ActiveResource
|
|
31
31
|
# # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
|
32
32
|
#
|
33
33
|
module CustomMethods
|
34
|
-
|
35
|
-
base.class_eval do
|
36
|
-
extend ActiveResource::CustomMethods::ClassMethods
|
37
|
-
include ActiveResource::CustomMethods::InstanceMethods
|
34
|
+
extend ActiveSupport::Concern
|
38
35
|
|
39
|
-
|
40
|
-
|
36
|
+
included do
|
37
|
+
class << self
|
38
|
+
alias :orig_delete :delete
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
40
|
+
# Invokes a GET to a given custom REST method. For example:
|
41
|
+
#
|
42
|
+
# Person.get(:active) # GET /people/active.xml
|
43
|
+
# # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
|
44
|
+
#
|
45
|
+
# Person.get(:active, :awesome => true) # GET /people/active.xml?awesome=true
|
46
|
+
# # => [{:id => 1, :name => 'Ryan'}]
|
47
|
+
#
|
48
|
+
# Note: the objects returned from this method are not automatically converted
|
49
|
+
# into ActiveResource::Base instances - they are ordinary Hashes. If you are expecting
|
50
|
+
# ActiveResource::Base instances, use the <tt>find</tt> class method with the
|
51
|
+
# <tt>:from</tt> option. For example:
|
52
|
+
#
|
53
|
+
# Person.find(:all, :from => :active)
|
54
|
+
def get(custom_method_name, options = {})
|
55
|
+
connection.get(custom_method_collection_url(custom_method_name, options), headers)
|
56
|
+
end
|
59
57
|
|
60
|
-
|
61
|
-
|
62
|
-
|
58
|
+
def post(custom_method_name, options = {}, body = '')
|
59
|
+
connection.post(custom_method_collection_url(custom_method_name, options), body, headers)
|
60
|
+
end
|
63
61
|
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
def put(custom_method_name, options = {}, body = '')
|
63
|
+
connection.put(custom_method_collection_url(custom_method_name, options), body, headers)
|
64
|
+
end
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
66
|
+
def delete(custom_method_name, options = {})
|
67
|
+
# Need to jump through some hoops to retain the original class 'delete' method
|
68
|
+
if custom_method_name.is_a?(Symbol)
|
69
|
+
connection.delete(custom_method_collection_url(custom_method_name, options), headers)
|
70
|
+
else
|
71
|
+
orig_delete(custom_method_name, options)
|
75
72
|
end
|
76
73
|
end
|
77
74
|
end
|
@@ -8,7 +8,10 @@ module ActiveResource
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def to_s
|
11
|
-
|
11
|
+
message = "Failed."
|
12
|
+
message << " Response code = #{response.code}." if response.respond_to?(:code)
|
13
|
+
message << " Response message = #{response.message}." if response.respond_to?(:message)
|
14
|
+
message
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module ActiveResource
|
2
2
|
module Formats
|
3
|
+
autoload :XmlFormat, 'active_resource/formats/xml_format'
|
4
|
+
autoload :JsonFormat, 'active_resource/formats/json_format'
|
5
|
+
|
3
6
|
# Lookup the format class from a mime type reference symbol. Example:
|
4
7
|
#
|
5
8
|
# ActiveResource::Formats[:xml] # => ActiveResource::Formats::XmlFormat
|
6
9
|
# ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat
|
7
10
|
def self.[](mime_type_reference)
|
8
|
-
ActiveResource::Formats.const_get(mime_type_reference.to_s
|
11
|
+
ActiveResource::Formats.const_get(ActiveSupport::Inflector.camelize(mime_type_reference.to_s) + "Format")
|
9
12
|
end
|
10
13
|
end
|
11
14
|
end
|
12
|
-
|
13
|
-
require 'active_resource/formats/xml_format'
|
14
|
-
require 'active_resource/formats/json_format'
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'active_support/core_ext/kernel/reporting'
|
2
2
|
|
3
3
|
module ActiveResource
|
4
4
|
class InvalidRequestError < StandardError; end #:nodoc:
|
@@ -29,8 +29,7 @@ module ActiveResource
|
|
29
29
|
#
|
30
30
|
# In order for a mock to deliver its content, the incoming request must match by the <tt>http_method</tt>,
|
31
31
|
# +path+ and <tt>request_headers</tt>. If no match is found an InvalidRequestError exception
|
32
|
-
# will be raised
|
33
|
-
# pairs have been recorded so you can create a new mock for that request.
|
32
|
+
# will be raised letting you know you need to create a new mock for that request.
|
34
33
|
#
|
35
34
|
# ==== Example
|
36
35
|
# def setup
|
@@ -58,7 +57,7 @@ module ActiveResource
|
|
58
57
|
# def post(path, request_headers = {}, body = nil, status = 200, response_headers = {})
|
59
58
|
# @responses[Request.new(:post, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
|
60
59
|
# end
|
61
|
-
module_eval <<-EOE, __FILE__, __LINE__
|
60
|
+
module_eval <<-EOE, __FILE__, __LINE__
|
62
61
|
def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {})
|
63
62
|
@responses << [Request.new(:#{method}, path, nil, request_headers), Response.new(body || "", status, response_headers)]
|
64
63
|
end
|
@@ -98,79 +97,10 @@ module ActiveResource
|
|
98
97
|
@@responses ||= []
|
99
98
|
end
|
100
99
|
|
101
|
-
# Accepts a block which declares a set of requests and responses for the HttpMock to respond to
|
102
|
-
#
|
103
|
-
|
104
|
-
|
105
|
-
#
|
106
|
-
# === Example
|
107
|
-
#
|
108
|
-
# @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
|
109
|
-
# ActiveResource::HttpMock.respond_to do |mock|
|
110
|
-
# mock.post "/people.xml", {}, @matz, 201, "Location" => "/people/1.xml"
|
111
|
-
# mock.get "/people/1.xml", {}, @matz
|
112
|
-
# mock.put "/people/1.xml", {}, nil, 204
|
113
|
-
# mock.delete "/people/1.xml", {}, nil, 200
|
114
|
-
# end
|
115
|
-
#
|
116
|
-
# Alternatively, accepts a hash of <tt>{Request => Response}</tt> pairs allowing you to generate
|
117
|
-
# these the following format:
|
118
|
-
#
|
119
|
-
# ActiveResource::Request.new(method, path, body, request_headers)
|
120
|
-
# ActiveResource::Response.new(body, status, response_headers)
|
121
|
-
#
|
122
|
-
# === Example
|
123
|
-
#
|
124
|
-
# Request.new(:#{method}, path, nil, request_headers)
|
125
|
-
#
|
126
|
-
# @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
|
127
|
-
#
|
128
|
-
# create_matz = ActiveResource::Request.new(:post, '/people.xml', @matz, {})
|
129
|
-
# created_response = ActiveResource::Response.new("", 201, {"Location" => "/people/1.xml"})
|
130
|
-
# get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil)
|
131
|
-
# ok_response = ActiveResource::Response.new("", 200, {})
|
132
|
-
#
|
133
|
-
# pairs = {create_matz => created_response, get_matz => ok_response}
|
134
|
-
#
|
135
|
-
# ActiveResource::HttpMock.respond_to(pairs)
|
136
|
-
#
|
137
|
-
# Note, by default, every time you call +respond_to+, any previous request and response pairs stored
|
138
|
-
# in HttpMock will be deleted giving you a clean slate to work on.
|
139
|
-
#
|
140
|
-
# If you want to override this behaviour, pass in +false+ as the last argument to +respond_to+
|
141
|
-
#
|
142
|
-
# === Example
|
143
|
-
#
|
144
|
-
# ActiveResource::HttpMock.respond_to do |mock|
|
145
|
-
# mock.send(:get, "/people/1", {}, "XML1")
|
146
|
-
# end
|
147
|
-
# ActiveResource::HttpMock.responses.length #=> 1
|
148
|
-
#
|
149
|
-
# ActiveResource::HttpMock.respond_to(false) do |mock|
|
150
|
-
# mock.send(:get, "/people/2", {}, "XML2")
|
151
|
-
# end
|
152
|
-
# ActiveResource::HttpMock.responses.length #=> 2
|
153
|
-
#
|
154
|
-
# This also works with passing in generated pairs of requests and responses, again, just pass in false
|
155
|
-
# as the last argument:
|
156
|
-
#
|
157
|
-
# === Example
|
158
|
-
#
|
159
|
-
# ActiveResource::HttpMock.respond_to do |mock|
|
160
|
-
# mock.send(:get, "/people/1", {}, "XML1")
|
161
|
-
# end
|
162
|
-
# ActiveResource::HttpMock.responses.length #=> 1
|
163
|
-
#
|
164
|
-
# get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil)
|
165
|
-
# ok_response = ActiveResource::Response.new("", 200, {})
|
166
|
-
#
|
167
|
-
# pairs = {get_matz => ok_response}
|
168
|
-
#
|
169
|
-
# ActiveResource::HttpMock.respond_to(pairs, false)
|
170
|
-
# ActiveResource::HttpMock.responses.length #=> 2
|
171
|
-
def respond_to(*args) #:yields: mock
|
172
|
-
pairs = args.first || {}
|
173
|
-
reset! if args.last.class != FalseClass
|
100
|
+
# Accepts a block which declares a set of requests and responses for the HttpMock to respond to. See the main
|
101
|
+
# ActiveResource::HttpMock description for a more detailed explanation.
|
102
|
+
def respond_to(pairs = {}) #:yields: mock
|
103
|
+
reset!
|
174
104
|
responses.concat pairs.to_a
|
175
105
|
if block_given?
|
176
106
|
yield Responder.new(responses)
|
@@ -193,20 +123,16 @@ module ActiveResource
|
|
193
123
|
# def post(path, body, headers)
|
194
124
|
# request = ActiveResource::Request.new(:post, path, body, headers)
|
195
125
|
# self.class.requests << request
|
196
|
-
#
|
197
|
-
# response[1]
|
198
|
-
# else
|
199
|
-
# raise InvalidRequestError.new("Could not find a response recorded for #{request.to_s} - Responses recorded are: - #{inspect_responses}")
|
200
|
-
# end
|
126
|
+
# self.class.responses.assoc(request).try(:second) || raise(InvalidRequestError.new("No response recorded for #{request}"))
|
201
127
|
# end
|
202
|
-
module_eval <<-EOE, __FILE__, __LINE__
|
128
|
+
module_eval <<-EOE, __FILE__, __LINE__
|
203
129
|
def #{method}(path, #{'body, ' if has_body}headers)
|
204
130
|
request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers)
|
205
131
|
self.class.requests << request
|
206
132
|
if response = self.class.responses.assoc(request)
|
207
133
|
response[1]
|
208
134
|
else
|
209
|
-
raise InvalidRequestError.new("
|
135
|
+
raise InvalidRequestError.new("No response recorded for \#{request}")
|
210
136
|
end
|
211
137
|
end
|
212
138
|
EOE
|
@@ -216,39 +142,22 @@ module ActiveResource
|
|
216
142
|
def initialize(site) #:nodoc:
|
217
143
|
@site = site
|
218
144
|
end
|
219
|
-
|
220
|
-
def inspect_responses #:nodoc:
|
221
|
-
self.class.responses.map { |r| r[0].to_s }.inspect
|
222
|
-
end
|
223
145
|
end
|
224
146
|
|
225
147
|
class Request
|
226
148
|
attr_accessor :path, :method, :body, :headers
|
227
149
|
|
228
150
|
def initialize(method, path, body = nil, headers = {})
|
229
|
-
@method, @path, @body, @headers = method, path, body, headers
|
151
|
+
@method, @path, @body, @headers = method, path, body, headers.merge(ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[method] => 'application/xml')
|
230
152
|
end
|
231
153
|
|
232
154
|
def ==(req)
|
233
|
-
path == req.path && method == req.method &&
|
155
|
+
path == req.path && method == req.method && headers == req.headers
|
234
156
|
end
|
235
157
|
|
236
158
|
def to_s
|
237
159
|
"<#{method.to_s.upcase}: #{path} [#{headers}] (#{body})>"
|
238
160
|
end
|
239
|
-
|
240
|
-
private
|
241
|
-
|
242
|
-
def headers_match?(req)
|
243
|
-
# Ignore format header on equality if it's not defined
|
244
|
-
format_header = ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[method]
|
245
|
-
if headers[format_header].present? || req.headers[format_header].blank?
|
246
|
-
headers == req.headers
|
247
|
-
else
|
248
|
-
headers.dup.merge(format_header => req.headers[format_header]) == req.headers
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
161
|
end
|
253
162
|
|
254
163
|
class Response
|