activeresource_csi 2.3.5.p6
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/CHANGELOG +292 -0
- data/README +165 -0
- data/Rakefile +139 -0
- data/lib/active_resource/base.rb +1157 -0
- data/lib/active_resource/connection.rb +283 -0
- data/lib/active_resource/custom_methods.rb +120 -0
- data/lib/active_resource/exceptions.rb +66 -0
- data/lib/active_resource/formats/json_format.rb +23 -0
- data/lib/active_resource/formats/xml_format.rb +34 -0
- data/lib/active_resource/formats.rb +14 -0
- data/lib/active_resource/http_mock.rb +207 -0
- data/lib/active_resource/validations.rb +290 -0
- data/lib/active_resource/version.rb +9 -0
- data/lib/active_resource.rb +44 -0
- data/lib/activeresource.rb +2 -0
- data/test/abstract_unit.rb +21 -0
- data/test/authorization_test.rb +122 -0
- data/test/base/custom_methods_test.rb +100 -0
- data/test/base/equality_test.rb +52 -0
- data/test/base/load_test.rb +161 -0
- data/test/base_errors_test.rb +85 -0
- data/test/base_test.rb +1038 -0
- data/test/connection_test.rb +238 -0
- data/test/debug.log +5335 -0
- data/test/fixtures/beast.rb +14 -0
- data/test/fixtures/customer.rb +3 -0
- data/test/fixtures/person.rb +3 -0
- data/test/fixtures/proxy.rb +4 -0
- data/test/fixtures/street_address.rb +4 -0
- data/test/format_test.rb +112 -0
- data/test/setter_trap.rb +26 -0
- metadata +119 -0
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'date'
|
3
|
+
require 'time'
|
4
|
+
require 'uri'
|
5
|
+
require 'benchmark'
|
6
|
+
|
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
|
+
# Class to handle connections to remote web services.
|
74
|
+
# This class is used by ActiveResource::Base to interface with REST
|
75
|
+
# services.
|
76
|
+
class Connection
|
77
|
+
|
78
|
+
HTTP_FORMAT_HEADER_NAMES = { :get => 'Accept',
|
79
|
+
:put => 'Content-Type',
|
80
|
+
:post => 'Content-Type',
|
81
|
+
:delete => 'Accept',
|
82
|
+
:head => 'Accept'
|
83
|
+
}
|
84
|
+
|
85
|
+
attr_reader :site, :user, :password, :timeout, :proxy, :ssl_options
|
86
|
+
attr_accessor :format
|
87
|
+
|
88
|
+
class << self
|
89
|
+
def requests
|
90
|
+
@@requests ||= []
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# The +site+ parameter is required and will set the +site+
|
95
|
+
# attribute to the URI for the remote resource service.
|
96
|
+
def initialize(site, format = ActiveResource::Formats[:xml])
|
97
|
+
raise ArgumentError, 'Missing site URI' unless site
|
98
|
+
@user = @password = nil
|
99
|
+
self.site = site
|
100
|
+
self.format = format
|
101
|
+
end
|
102
|
+
|
103
|
+
# Set URI for remote service.
|
104
|
+
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
|
108
|
+
end
|
109
|
+
|
110
|
+
# Set the proxy for remote service.
|
111
|
+
def proxy=(proxy)
|
112
|
+
@proxy = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Set the user for remote service.
|
116
|
+
def user=(user)
|
117
|
+
@user = user
|
118
|
+
end
|
119
|
+
|
120
|
+
# Set password for remote service.
|
121
|
+
def password=(password)
|
122
|
+
@password = password
|
123
|
+
end
|
124
|
+
|
125
|
+
# Set the number of seconds after which HTTP requests to the remote service should time out.
|
126
|
+
def timeout=(timeout)
|
127
|
+
@timeout = timeout
|
128
|
+
end
|
129
|
+
|
130
|
+
# Hash of options applied to Net::HTTP instance when +site+ protocol is 'https'.
|
131
|
+
def ssl_options=(opts={})
|
132
|
+
@ssl_options = opts
|
133
|
+
end
|
134
|
+
|
135
|
+
# Execute a GET request.
|
136
|
+
# Used to get (find) resources.
|
137
|
+
def get(path, headers = {})
|
138
|
+
format.decode(request(:get, path, build_request_headers(headers, :get)).body)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Execute a DELETE request (see HTTP protocol documentation if unfamiliar).
|
142
|
+
# Used to delete resources.
|
143
|
+
def delete(path, headers = {})
|
144
|
+
request(:delete, path, build_request_headers(headers, :delete))
|
145
|
+
end
|
146
|
+
|
147
|
+
# Execute a PUT request (see HTTP protocol documentation if unfamiliar).
|
148
|
+
# Used to update resources.
|
149
|
+
def put(path, body = '', headers = {})
|
150
|
+
request(:put, path, body.to_s, build_request_headers(headers, :put))
|
151
|
+
end
|
152
|
+
|
153
|
+
# Execute a POST request.
|
154
|
+
# Used to create new resources.
|
155
|
+
def post(path, body = '', headers = {})
|
156
|
+
request(:post, path, body.to_s, build_request_headers(headers, :post))
|
157
|
+
end
|
158
|
+
|
159
|
+
# Execute a HEAD request.
|
160
|
+
# Used to obtain meta-information about resources, such as whether they exist and their size (via response headers).
|
161
|
+
def head(path, headers = {})
|
162
|
+
request(:head, path, build_request_headers(headers, :head))
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
private
|
167
|
+
# Makes request to remote service.
|
168
|
+
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
|
173
|
+
handle_response(result)
|
174
|
+
rescue Timeout::Error => e
|
175
|
+
raise TimeoutError.new(e.message)
|
176
|
+
rescue OpenSSL::SSL::SSLError => e
|
177
|
+
raise SSLError.new(e.message)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Handles response and error codes from remote service.
|
181
|
+
def handle_response(response)
|
182
|
+
case response.code.to_i
|
183
|
+
when 301,302
|
184
|
+
raise(Redirection.new(response))
|
185
|
+
when 200...400
|
186
|
+
response
|
187
|
+
when 400
|
188
|
+
raise(BadRequest.new(response))
|
189
|
+
when 401
|
190
|
+
raise(UnauthorizedAccess.new(response))
|
191
|
+
when 403
|
192
|
+
raise(ForbiddenAccess.new(response))
|
193
|
+
when 404
|
194
|
+
raise(ResourceNotFound.new(response))
|
195
|
+
when 405
|
196
|
+
raise(MethodNotAllowed.new(response))
|
197
|
+
when 409
|
198
|
+
raise(ResourceConflict.new(response))
|
199
|
+
when 410
|
200
|
+
raise(ResourceGone.new(response))
|
201
|
+
when 422
|
202
|
+
raise(ResourceInvalid.new(response))
|
203
|
+
when 401...500
|
204
|
+
raise(ClientError.new(response))
|
205
|
+
when 500...600
|
206
|
+
raise(ServerError.new(response))
|
207
|
+
else
|
208
|
+
raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Creates new Net::HTTP instance for communication with
|
213
|
+
# remote service and resources.
|
214
|
+
def http
|
215
|
+
configure_http(new_http)
|
216
|
+
end
|
217
|
+
|
218
|
+
def new_http
|
219
|
+
if @proxy
|
220
|
+
Net::HTTP.new(@site.host, @site.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password)
|
221
|
+
else
|
222
|
+
Net::HTTP.new(@site.host, @site.port)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def configure_http(http)
|
227
|
+
http = apply_ssl_options(http)
|
228
|
+
|
229
|
+
# Net::HTTP timeouts default to 60 seconds.
|
230
|
+
if @timeout
|
231
|
+
http.open_timeout = @timeout
|
232
|
+
http.read_timeout = @timeout
|
233
|
+
end
|
234
|
+
|
235
|
+
http
|
236
|
+
end
|
237
|
+
|
238
|
+
def apply_ssl_options(http)
|
239
|
+
return http unless @site.is_a?(URI::HTTPS)
|
240
|
+
|
241
|
+
http.use_ssl = true
|
242
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
243
|
+
return http unless defined?(@ssl_options)
|
244
|
+
|
245
|
+
http.ca_path = @ssl_options[:ca_path] if @ssl_options[:ca_path]
|
246
|
+
http.ca_file = @ssl_options[:ca_file] if @ssl_options[:ca_file]
|
247
|
+
|
248
|
+
http.cert = @ssl_options[:cert] if @ssl_options[:cert]
|
249
|
+
http.key = @ssl_options[:key] if @ssl_options[:key]
|
250
|
+
|
251
|
+
http.cert_store = @ssl_options[:cert_store] if @ssl_options[:cert_store]
|
252
|
+
http.ssl_timeout = @ssl_options[:ssl_timeout] if @ssl_options[:ssl_timeout]
|
253
|
+
|
254
|
+
http.verify_mode = @ssl_options[:verify_mode] if @ssl_options[:verify_mode]
|
255
|
+
http.verify_callback = @ssl_options[:verify_callback] if @ssl_options[:verify_callback]
|
256
|
+
http.verify_depth = @ssl_options[:verify_depth] if @ssl_options[:verify_depth]
|
257
|
+
|
258
|
+
http
|
259
|
+
end
|
260
|
+
|
261
|
+
def default_header
|
262
|
+
@default_header ||= {}
|
263
|
+
end
|
264
|
+
|
265
|
+
# 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)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Sets authorization header
|
271
|
+
def authorization_header
|
272
|
+
(@user || @password ? { 'Authorization' => 'Basic ' + ["#{@user}:#{ @password}"].pack('m').delete("\r\n") } : {})
|
273
|
+
end
|
274
|
+
|
275
|
+
def http_format_header(http_method)
|
276
|
+
{HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type}
|
277
|
+
end
|
278
|
+
|
279
|
+
def logger #:nodoc:
|
280
|
+
Base.logger
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
# A module to support custom REST methods and sub-resources, allowing you to break out
|
3
|
+
# of the "default" REST methods with your own custom resource requests. For example,
|
4
|
+
# say you use Rails to expose a REST service and configure your routes with:
|
5
|
+
#
|
6
|
+
# map.resources :people, :new => { :register => :post },
|
7
|
+
# :member => { :promote => :put, :deactivate => :delete }
|
8
|
+
# :collection => { :active => :get }
|
9
|
+
#
|
10
|
+
# This route set creates routes for the following HTTP requests:
|
11
|
+
#
|
12
|
+
# POST /people/new/register.xml # PeopleController.register
|
13
|
+
# PUT /people/1/promote.xml # PeopleController.promote with :id => 1
|
14
|
+
# DELETE /people/1/deactivate.xml # PeopleController.deactivate with :id => 1
|
15
|
+
# GET /people/active.xml # PeopleController.active
|
16
|
+
#
|
17
|
+
# Using this module, Active Resource can use these custom REST methods just like the
|
18
|
+
# standard methods.
|
19
|
+
#
|
20
|
+
# class Person < ActiveResource::Base
|
21
|
+
# self.site = "http://37s.sunrise.i:3000"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# Person.new(:name => 'Ryan).post(:register) # POST /people/new/register.xml
|
25
|
+
# # => { :id => 1, :name => 'Ryan' }
|
26
|
+
#
|
27
|
+
# Person.find(1).put(:promote, :position => 'Manager') # PUT /people/1/promote.xml
|
28
|
+
# Person.find(1).delete(:deactivate) # DELETE /people/1/deactivate.xml
|
29
|
+
#
|
30
|
+
# Person.get(:active) # GET /people/active.xml
|
31
|
+
# # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
|
32
|
+
#
|
33
|
+
module CustomMethods
|
34
|
+
def self.included(base)
|
35
|
+
base.class_eval do
|
36
|
+
extend ActiveResource::CustomMethods::ClassMethods
|
37
|
+
include ActiveResource::CustomMethods::InstanceMethods
|
38
|
+
|
39
|
+
class << self
|
40
|
+
alias :orig_delete :delete
|
41
|
+
|
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
|
59
|
+
|
60
|
+
def post(custom_method_name, options = {}, body = '')
|
61
|
+
connection.post(custom_method_collection_url(custom_method_name, options), body, headers)
|
62
|
+
end
|
63
|
+
|
64
|
+
def put(custom_method_name, options = {}, body = '')
|
65
|
+
connection.put(custom_method_collection_url(custom_method_name, options), body, headers)
|
66
|
+
end
|
67
|
+
|
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
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
module ClassMethods
|
81
|
+
def custom_method_collection_url(method_name, options = {})
|
82
|
+
prefix_options, query_options = split_options(options)
|
83
|
+
"#{prefix(prefix_options)}#{collection_name}/#{method_name}.#{format.extension}#{query_string(query_options)}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
module InstanceMethods
|
88
|
+
def get(method_name, options = {})
|
89
|
+
connection.get(custom_method_element_url(method_name, options), self.class.headers)
|
90
|
+
end
|
91
|
+
|
92
|
+
def post(method_name, options = {}, body = nil)
|
93
|
+
request_body = body.blank? ? encode : body
|
94
|
+
if new?
|
95
|
+
connection.post(custom_method_new_element_url(method_name, options), request_body, self.class.headers)
|
96
|
+
else
|
97
|
+
connection.post(custom_method_element_url(method_name, options), request_body, self.class.headers)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def put(method_name, options = {}, body = '')
|
102
|
+
connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
|
103
|
+
end
|
104
|
+
|
105
|
+
def delete(method_name, options = {})
|
106
|
+
connection.delete(custom_method_element_url(method_name, options), self.class.headers)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
private
|
111
|
+
def custom_method_element_url(method_name, options = {})
|
112
|
+
"#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
|
113
|
+
end
|
114
|
+
|
115
|
+
def custom_method_new_element_url(method_name, options = {})
|
116
|
+
"#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
class ConnectionError < StandardError # :nodoc:
|
3
|
+
attr_reader :response
|
4
|
+
|
5
|
+
def initialize(response, message = nil)
|
6
|
+
@response = response
|
7
|
+
@message = message
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Raised when a Timeout::Error occurs.
|
16
|
+
class TimeoutError < ConnectionError
|
17
|
+
def initialize(message)
|
18
|
+
@message = message
|
19
|
+
end
|
20
|
+
def to_s; @message ;end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Raised when a OpenSSL::SSL::SSLError occurs.
|
24
|
+
class SSLError < ConnectionError
|
25
|
+
def initialize(message)
|
26
|
+
@message = message
|
27
|
+
end
|
28
|
+
def to_s; @message ;end
|
29
|
+
end
|
30
|
+
|
31
|
+
# 3xx Redirection
|
32
|
+
class Redirection < ConnectionError # :nodoc:
|
33
|
+
def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
|
34
|
+
end
|
35
|
+
|
36
|
+
# 4xx Client Error
|
37
|
+
class ClientError < ConnectionError; end # :nodoc:
|
38
|
+
|
39
|
+
# 400 Bad Request
|
40
|
+
class BadRequest < ClientError; end # :nodoc
|
41
|
+
|
42
|
+
# 401 Unauthorized
|
43
|
+
class UnauthorizedAccess < ClientError; end # :nodoc
|
44
|
+
|
45
|
+
# 403 Forbidden
|
46
|
+
class ForbiddenAccess < ClientError; end # :nodoc
|
47
|
+
|
48
|
+
# 404 Not Found
|
49
|
+
class ResourceNotFound < ClientError; end # :nodoc:
|
50
|
+
|
51
|
+
# 409 Conflict
|
52
|
+
class ResourceConflict < ClientError; end # :nodoc:
|
53
|
+
|
54
|
+
# 410 Gone
|
55
|
+
class ResourceGone < ClientError; end # :nodoc:
|
56
|
+
|
57
|
+
# 5xx Server Error
|
58
|
+
class ServerError < ConnectionError; end # :nodoc:
|
59
|
+
|
60
|
+
# 405 Method Not Allowed
|
61
|
+
class MethodNotAllowed < ClientError # :nodoc:
|
62
|
+
def allowed_methods
|
63
|
+
@response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
module Formats
|
3
|
+
module JsonFormat
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def extension
|
7
|
+
"json"
|
8
|
+
end
|
9
|
+
|
10
|
+
def mime_type
|
11
|
+
"application/json"
|
12
|
+
end
|
13
|
+
|
14
|
+
def encode(hash, options = nil)
|
15
|
+
ActiveSupport::JSON.encode(hash, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def decode(json)
|
19
|
+
ActiveSupport::JSON.decode(json)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
module Formats
|
3
|
+
module XmlFormat
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def extension
|
7
|
+
"xml"
|
8
|
+
end
|
9
|
+
|
10
|
+
def mime_type
|
11
|
+
"application/xml"
|
12
|
+
end
|
13
|
+
|
14
|
+
def encode(hash, options={})
|
15
|
+
hash.to_xml(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def decode(xml)
|
19
|
+
from_xml_data(Hash.from_xml(xml))
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
# Manipulate from_xml Hash, because xml_simple is not exactly what we
|
24
|
+
# want for Active Resource.
|
25
|
+
def from_xml_data(data)
|
26
|
+
if data.is_a?(Hash) && data.keys.size == 1
|
27
|
+
data.values.first
|
28
|
+
else
|
29
|
+
data
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
module Formats
|
3
|
+
# Lookup the format class from a mime type reference symbol. Example:
|
4
|
+
#
|
5
|
+
# ActiveResource::Formats[:xml] # => ActiveResource::Formats::XmlFormat
|
6
|
+
# ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat
|
7
|
+
def self.[](mime_type_reference)
|
8
|
+
ActiveResource::Formats.const_get(mime_type_reference.to_s.camelize + "Format")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'active_resource/formats/xml_format'
|
14
|
+
require 'active_resource/formats/json_format'
|