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.

@@ -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[:xml])
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 : URI.parse(site)
106
- @user = URI.decode(@site.user) if @site.user
107
- @password = URI.decode(@site.password) if @site.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 : URI.parse(proxy)
48
+ @proxy = proxy.is_a?(URI) ? proxy : @uri_parser.parse(proxy)
113
49
  end
114
50
 
115
- # Set the user for remote service.
51
+ # Sets the user for remote service.
116
52
  def user=(user)
117
53
  @user = user
118
54
  end
119
55
 
120
- # Set password for remote service.
56
+ # Sets the password for remote service.
121
57
  def password=(password)
122
58
  @password = password
123
59
  end
124
60
 
125
- # Set the number of seconds after which HTTP requests to the remote service should time out.
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
- # Execute a GET request.
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
- # Execute a DELETE request (see HTTP protocol documentation if unfamiliar).
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
- # Execute a PUT request (see HTTP protocol documentation if unfamiliar).
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
- # Execute a POST request.
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
- # Execute a HEAD request.
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
- logger.info "#{method.to_s.upcase} #{site.scheme}://#{site.host}:#{site.port}#{path}" if logger
170
- result = nil
171
- ms = Benchmark.ms { result = http.send(method, path, *arguments) }
172
- logger.info "--> %d %s (%d %.0fms)" % [result.code, result.message, result.body ? result.body.length : 0, ms] if logger
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=nil)
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
- # Sets authorization header
271
- def authorization_header
272
- (@user || @password ? { 'Authorization' => 'Basic ' + ["#{@user}:#{ @password}"].pack('m').delete("\r\n") } : {})
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 logger #:nodoc:
280
- Base.logger
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
- def self.included(base)
35
- base.class_eval do
36
- extend ActiveResource::CustomMethods::ClassMethods
37
- include ActiveResource::CustomMethods::InstanceMethods
34
+ extend ActiveSupport::Concern
38
35
 
39
- class << self
40
- alias :orig_delete :delete
36
+ included do
37
+ class << self
38
+ alias :orig_delete :delete
41
39
 
42
- # Invokes a GET to a given custom REST method. For example:
43
- #
44
- # Person.get(:active) # GET /people/active.xml
45
- # # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
46
- #
47
- # Person.get(:active, :awesome => true) # GET /people/active.xml?awesome=true
48
- # # => [{:id => 1, :name => 'Ryan'}]
49
- #
50
- # Note: the objects returned from this method are not automatically converted
51
- # into ActiveResource::Base instances - they are ordinary Hashes. If you are expecting
52
- # ActiveResource::Base instances, use the <tt>find</tt> class method with the
53
- # <tt>:from</tt> option. For example:
54
- #
55
- # Person.find(:all, :from => :active)
56
- def get(custom_method_name, options = {})
57
- connection.get(custom_method_collection_url(custom_method_name, options), headers)
58
- end
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
- def post(custom_method_name, options = {}, body = '')
61
- connection.post(custom_method_collection_url(custom_method_name, options), body, headers)
62
- end
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
- def put(custom_method_name, options = {}, body = '')
65
- connection.put(custom_method_collection_url(custom_method_name, options), body, headers)
66
- end
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
- def delete(custom_method_name, options = {})
69
- # Need to jump through some hoops to retain the original class 'delete' method
70
- if custom_method_name.is_a?(Symbol)
71
- connection.delete(custom_method_collection_url(custom_method_name, options), headers)
72
- else
73
- orig_delete(custom_method_name, options)
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
- "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
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.camelize + "Format")
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,3 +1,5 @@
1
+ require 'active_support/json'
2
+
1
3
  module ActiveResource
2
4
  module Formats
3
5
  module JsonFormat
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/hash/conversions'
2
+
1
3
  module ActiveResource
2
4
  module Formats
3
5
  module XmlFormat
@@ -1,4 +1,4 @@
1
- require 'active_resource/connection'
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 showing you what request it could not find a response for and also what requests and response
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__ + 1
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 in
102
- # the following format:
103
- #
104
- # mock.http_method(path, request_headers = {}, body = nil, status = 200, response_headers = {})
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
- # if response = self.class.responses.assoc(request)
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__ + 1
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("Could not find a response recorded for \#{request.to_s} - Responses recorded are: \#{inspect_responses}")
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 && headers_match?(req)
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