public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Format related patches to support serializing data out in the correct format 
with correct http request headers per http method type [#450 state:resolved]

Signed-off-by: Tarmo Tänav <tarmo@itech.ee>
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
rbpandey (author)
Fri Aug 29 18:19:18 -0700 2008
jeremy (committer)
Fri Aug 29 18:45:39 -0700 2008
commit  16b9a554db7e1bf3f5f224cdc5b4d27480e053ff
tree    7eef718fa623a5fcebb90de534b0eba2a6eae020
parent  11eb29f60ab79caf22f7ca715500e32d9a1b03a2
...
848
849
850
851
852
 
 
 
 
 
 
 
853
854
855
...
934
935
936
937
 
938
939
940
941
942
943
944
 
945
946
947
...
848
849
850
 
 
851
852
853
854
855
856
857
858
859
860
...
939
940
941
 
942
943
944
945
946
947
948
 
949
950
951
952
0
@@ -848,8 +848,13 @@ module ActiveResource
0
     #
0
     #   my_group.to_xml(:skip_instruct => true)
0
     #   # => <subsidiary_group> [...] </subsidiary_group>
0
-    def to_xml(options={})
0
-      attributes.to_xml({:root => self.class.element_name}.merge(options))
0
+    def encode(options={})
0
+      case self.class.format
0
+        when ActiveResource::Formats[:xml]
0
+          self.class.format.encode(attributes, {:root => self.class.element_name}.merge(options))
0
+        else
0
+          self.class.format.encode(attributes, options)
0
+      end
0
     end
0
 
0
     # A method to reload the attributes of this object from the remote web service.
0
@@ -934,14 +939,14 @@ module ActiveResource
0
 
0
       # Update the resource on the remote service.
0
       def update
0
-        returning connection.put(element_path(prefix_options), to_xml, self.class.headers) do |response|
0
+        returning connection.put(element_path(prefix_options), encode, self.class.headers) do |response|
0
           load_attributes_from_response(response)
0
         end
0
       end
0
 
0
       # Create (i.e., save to the remote service) the new resource.
0
       def create
0
-        returning connection.post(collection_path, to_xml, self.class.headers) do |response|
0
+        returning connection.post(collection_path, encode, self.class.headers) do |response|
0
           self.id = id_from_response(response)
0
           load_attributes_from_response(response)
0
         end
...
63
64
65
 
 
 
 
 
 
 
66
67
68
...
106
107
108
109
 
110
111
112
113
114
115
 
116
117
118
119
120
121
 
122
123
124
125
126
127
 
128
129
130
...
187
188
189
190
 
191
192
193
194
195
 
 
196
197
198
...
200
201
202
 
 
 
 
203
204
205
...
63
64
65
66
67
68
69
70
71
72
73
74
75
...
113
114
115
 
116
117
118
119
120
121
 
122
123
124
125
126
127
 
128
129
130
131
132
133
 
134
135
136
137
...
194
195
196
 
197
198
199
200
 
 
201
202
203
204
205
...
207
208
209
210
211
212
213
214
215
216
0
@@ -63,6 +63,13 @@ module ActiveResource
0
   # This class is used by ActiveResource::Base to interface with REST
0
   # services.
0
   class Connection
0
+
0
+    HTTP_FORMAT_HEADER_NAMES = {  :get => 'Accept',
0
+      :put => 'Content-Type',
0
+      :post => 'Content-Type',
0
+      :delete => 'Accept'
0
+    }
0
+
0
     attr_reader :site, :user, :password, :timeout
0
     attr_accessor :format
0
 
0
@@ -106,25 +113,25 @@ module ActiveResource
0
     # Execute a GET request.
0
     # Used to get (find) resources.
0
     def get(path, headers = {})
0
-      format.decode(request(:get, path, build_request_headers(headers)).body)
0
+      format.decode(request(:get, path, build_request_headers(headers, :get)).body)
0
     end
0
 
0
     # Execute a DELETE request (see HTTP protocol documentation if unfamiliar).
0
     # Used to delete resources.
0
     def delete(path, headers = {})
0
-      request(:delete, path, build_request_headers(headers))
0
+      request(:delete, path, build_request_headers(headers, :delete))
0
     end
0
 
0
     # Execute a PUT request (see HTTP protocol documentation if unfamiliar).
0
     # Used to update resources.
0
     def put(path, body = '', headers = {})
0
-      request(:put, path, body.to_s, build_request_headers(headers))
0
+      request(:put, path, body.to_s, build_request_headers(headers, :put))
0
     end
0
 
0
     # Execute a POST request.
0
     # Used to create new resources.
0
     def post(path, body = '', headers = {})
0
-      request(:post, path, body.to_s, build_request_headers(headers))
0
+      request(:post, path, body.to_s, build_request_headers(headers, :post))
0
     end
0
 
0
     # Execute a HEAD request.
0
@@ -187,12 +194,12 @@ module ActiveResource
0
       end
0
 
0
       def default_header
0
-        @default_header ||= { 'Content-Type' => format.mime_type }
0
+        @default_header ||= {}
0
       end
0
 
0
       # Builds headers for request to remote service.
0
-      def build_request_headers(headers)
0
-        authorization_header.update(default_header).update(headers)
0
+      def build_request_headers(headers, http_method=nil)
0
+        authorization_header.update(default_header).update(headers).update(http_format_header(http_method))
0
       end
0
 
0
       # Sets authorization header
0
@@ -200,6 +207,10 @@ module ActiveResource
0
         (@user || @password ? { 'Authorization' => 'Basic ' + ["#{@user}:#{ @password}"].pack('m').delete("\r\n") } : {})
0
       end
0
 
0
+      def http_format_header(http_method)
0
+        {HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type}
0
+      end
0
+
0
       def logger #:nodoc:
0
         Base.logger
0
       end
...
30
31
32
33
 
34
35
36
...
83
84
85
86
 
87
88
89
90
91
92
 
 
 
93
94
 
95
96
 
97
98
99
 
100
101
102
103
 
104
105
106
...
110
111
112
113
 
114
115
116
...
30
31
32
 
33
34
35
36
...
83
84
85
 
86
87
88
89
90
 
 
91
92
93
94
 
95
96
 
97
98
99
 
100
101
102
103
 
104
105
106
107
...
111
112
113
 
114
115
116
117
0
@@ -30,7 +30,7 @@ module ActiveResource
0
   #   Person.get(:active)  # GET /people/active.xml
0
   #   # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
0
   #
0
-  module CustomMethods 
0
+  module CustomMethods
0
     def self.included(base)
0
       base.class_eval do
0
         extend ActiveResource::CustomMethods::ClassMethods
0
@@ -83,24 +83,25 @@ module ActiveResource
0
         "#{prefix(prefix_options)}#{collection_name}/#{method_name}.#{format.extension}#{query_string(query_options)}"
0
       end
0
     end
0
-    
0
+
0
     module InstanceMethods
0
       def get(method_name, options = {})
0
         connection.get(custom_method_element_url(method_name, options), self.class.headers)
0
       end
0
-      
0
-      def post(method_name, options = {}, body = '')
0
+
0
+      def post(method_name, options = {}, body = nil)
0
+        request_body = body.nil? ? encode : body
0
         if new?
0
-          connection.post(custom_method_new_element_url(method_name, options), (body.nil? ? to_xml : body), self.class.headers)
0
+          connection.post(custom_method_new_element_url(method_name, options), request_body, self.class.headers)
0
         else
0
-          connection.post(custom_method_element_url(method_name, options), body, self.class.headers)
0
+          connection.post(custom_method_element_url(method_name, options), request_body, self.class.headers)
0
         end
0
       end
0
-      
0
+
0
       def put(method_name, options = {}, body = '')
0
         connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
0
       end
0
-      
0
+
0
       def delete(method_name, options = {})
0
         connection.delete(custom_method_element_url(method_name, options), self.class.headers)
0
       end
0
@@ -110,7 +111,7 @@ module ActiveResource
0
         def custom_method_element_url(method_name, options = {})
0
           "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.#{self.class.format.extension}#{self.class.send!(:query_string, options)}"
0
         end
0
-      
0
+
0
         def custom_method_new_element_url(method_name, options = {})
0
           "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.#{self.class.format.extension}#{self.class.send!(:query_string, options)}"
0
         end
...
2
3
4
5
 
6
7
8
9
 
10
11
12
13
14
 
 
15
16
17
 
18
19
20
21
22
23
24
 
...
2
3
4
 
5
6
7
8
 
9
10
11
12
 
 
13
14
15
16
 
17
18
19
20
21
22
 
23
24
0
@@ -2,22 +2,22 @@ module ActiveResource
0
   module Formats
0
     module JsonFormat
0
       extend self
0
-      
0
+
0
       def extension
0
         "json"
0
       end
0
-      
0
+
0
       def mime_type
0
         "application/json"
0
       end
0
-      
0
-      def encode(hash)
0
+
0
+      def encode(hash, options={})
0
         hash.to_json
0
       end
0
-      
0
+
0
       def decode(json)
0
         ActiveSupport::JSON.decode(json)
0
       end
0
     end
0
   end
0
-end
0
\ No newline at end of file
0
+end
...
2
3
4
5
 
6
7
8
9
 
10
11
12
13
14
15
 
 
 
16
17
 
18
19
20
21
 
22
23
24
...
28
29
30
31
 
32
33
34
35
 
...
2
3
4
 
5
6
7
8
 
9
10
11
12
 
 
 
13
14
15
16
 
17
18
19
20
 
21
22
23
24
...
28
29
30
 
31
32
33
 
34
35
0
@@ -2,23 +2,23 @@ module ActiveResource
0
   module Formats
0
     module XmlFormat
0
       extend self
0
-      
0
+
0
       def extension
0
         "xml"
0
       end
0
-      
0
+
0
       def mime_type
0
         "application/xml"
0
       end
0
-      
0
-      def encode(hash)
0
-        hash.to_xml
0
+
0
+      def encode(hash, options={})
0
+        hash.to_xml(options)
0
       end
0
-      
0
+
0
       def decode(xml)
0
         from_xml_data(Hash.from_xml(xml))
0
       end
0
-      
0
+
0
       private
0
         # Manipulate from_xml Hash, because xml_simple is not exactly what we
0
         # want for Active Resource.
0
@@ -28,7 +28,7 @@ module ActiveResource
0
           else
0
             data
0
           end
0
-        end      
0
+        end
0
     end
0
   end
0
-end
0
\ No newline at end of file
0
+end
...
146
147
148
149
 
150
151
152
...
146
147
148
 
149
150
151
152
0
@@ -146,7 +146,7 @@ module ActiveResource
0
     attr_accessor :path, :method, :body, :headers
0
 
0
     def initialize(method, path, body = nil, headers = {})
0
-      @method, @path, @body, @headers = method, path, body, headers.reverse_merge('Content-Type' => 'application/xml')
0
+      @method, @path, @body, @headers = method, path, body, headers.merge(ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[method] => 'application/xml')
0
     end
0
 
0
     def ==(other_request)
...
10
11
12
13
14
 
15
16
17
...
10
11
12
 
 
13
14
15
16
0
@@ -10,8 +10,7 @@ class CustomMethodsTest < Test::Unit::TestCase
0
     @ryan  = { :name => 'Ryan' }.to_xml(:root => 'person')
0
     @addy  = { :id => 1, :street => '12345 Street' }.to_xml(:root => 'address')
0
     @addy_deep  = { :id => 1, :street => '12345 Street', :zip => "27519" }.to_xml(:root => 'address')
0
-    @default_request_headers = { 'Content-Type' => 'application/xml' }
0
-    
0
+
0
     ActiveResource::HttpMock.respond_to do |mock|
0
       mock.get    "/people/1.xml",             {}, @matz
0
       mock.get    "/people/1/shallow.xml", {}, @matz
...
825
826
827
828
 
829
830
831
...
825
826
827
 
828
829
830
831
0
@@ -825,7 +825,7 @@ class BaseTest < Test::Unit::TestCase
0
 
0
   def test_to_xml
0
     matz = Person.find(1)
0
-    xml = matz.to_xml
0
+    xml = matz.encode
0
     assert xml.starts_with?('<?xml version="1.0" encoding="UTF-8"?>')
0
     assert xml.include?('<name>Matz</name>')
0
     assert xml.include?('<id type="integer">1</id>')
...
5
6
7
8
 
9
10
11
 
 
 
 
 
 
 
 
 
12
13
14
15
 
16
17
18
...
21
22
23
24
 
25
26
27
...
32
33
34
35
 
36
37
38
...
40
41
42
43
 
44
45
46
47
48
49
 
 
50
51
52
...
57
58
59
60
61
 
 
 
 
 
62
63
64
65
66
 
67
68
69
70
71
72
73
 
74
75
76
...
5
6
7
 
8
9
10
 
11
12
13
14
15
16
17
18
19
20
21
22
 
23
24
25
26
...
29
30
31
 
32
33
34
35
...
40
41
42
 
43
44
45
46
...
48
49
50
 
51
52
53
54
55
 
 
56
57
58
59
60
...
65
66
67
 
68
69
70
71
72
73
74
75
76
77
 
78
79
80
81
82
83
84
 
85
86
87
88
0
@@ -5,14 +5,22 @@ class FormatTest < Test::Unit::TestCase
0
   def setup
0
     @matz  = { :id => 1, :name => 'Matz' }
0
     @david = { :id => 2, :name => 'David' }
0
-    
0
+
0
     @programmers = [ @matz, @david ]
0
   end
0
-  
0
+
0
+  def test_http_format_header_name
0
+    header_name = ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[:get]
0
+    assert_equal 'Accept', header_name
0
+
0
+    headers_names = [ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[:put], ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[:post]]
0
+    headers_names.each{|header_name| assert_equal 'Content-Type', header_name}
0
+  end
0
+
0
   def test_formats_on_single_element
0
     for format in [ :json, :xml ]
0
       using_format(Person, format) do
0
-        ActiveResource::HttpMock.respond_to.get "/people/1.#{format}", {}, ActiveResource::Formats[format].encode(@david)
0
+        ActiveResource::HttpMock.respond_to.get "/people/1.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@david)
0
         assert_equal @david[:name], Person.find(1).name
0
       end
0
     end
0
@@ -21,7 +29,7 @@ class FormatTest < Test::Unit::TestCase
0
   def test_formats_on_collection
0
     for format in [ :json, :xml ]
0
       using_format(Person, format) do
0
-        ActiveResource::HttpMock.respond_to.get "/people.#{format}", {}, ActiveResource::Formats[format].encode(@programmers)
0
+        ActiveResource::HttpMock.respond_to.get "/people.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@programmers)
0
         remote_programmers = Person.find(:all)
0
         assert_equal 2, remote_programmers.size
0
         assert remote_programmers.select { |p| p.name == 'David' }
0
@@ -32,7 +40,7 @@ class FormatTest < Test::Unit::TestCase
0
   def test_formats_on_custom_collection_method
0
     for format in [ :json, :xml ]
0
       using_format(Person, format) do
0
-        ActiveResource::HttpMock.respond_to.get "/people/retrieve.#{format}?name=David", {}, ActiveResource::Formats[format].encode([@david])
0
+        ActiveResource::HttpMock.respond_to.get "/people/retrieve.#{format}?name=David", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode([@david])
0
         remote_programmers = Person.get(:retrieve, :name => 'David')
0
         assert_equal 1, remote_programmers.size
0
         assert_equal @david[:id], remote_programmers[0]['id']
0
@@ -40,13 +48,13 @@ class FormatTest < Test::Unit::TestCase
0
       end
0
     end
0
   end
0
-  
0
+
0
   def test_formats_on_custom_element_method
0
     for format in [ :json, :xml ]
0
       using_format(Person, format) do
0
         ActiveResource::HttpMock.respond_to do |mock|
0
-          mock.get "/people/2.#{format}", {}, ActiveResource::Formats[format].encode(@david)
0
-          mock.get "/people/2/shallow.#{format}", {}, ActiveResource::Formats[format].encode(@david)
0
+          mock.get "/people/2.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@david)
0
+          mock.get "/people/2/shallow.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@david)
0
         end
0
         remote_programmer = Person.find(2).get(:shallow)
0
         assert_equal @david[:id], remote_programmer['id']
0
@@ -57,20 +65,24 @@ class FormatTest < Test::Unit::TestCase
0
     for format in [ :json, :xml ]
0
       ryan = ActiveResource::Formats[format].encode({ :name => 'Ryan' })
0
       using_format(Person, format) do
0
-        ActiveResource::HttpMock.respond_to.post "/people/new/register.#{format}", {}, ryan, 201, 'Location' => "/people/5.#{format}"
0
         remote_ryan = Person.new(:name => 'Ryan')
0
+        ActiveResource::HttpMock.respond_to.post "/people.#{format}", {'Content-Type' => ActiveResource::Formats[format].mime_type}, ryan, 201, {'Location' => "/people/5.#{format}"}
0
+        remote_ryan.save
0
+
0
+        remote_ryan = Person.new(:name => 'Ryan')
0
+        ActiveResource::HttpMock.respond_to.post "/people/new/register.#{format}", {'Content-Type' => ActiveResource::Formats[format].mime_type}, ryan, 201, {'Location' => "/people/5.#{format}"}
0
         assert_equal ActiveResource::Response.new(ryan, 201, {'Location' => "/people/5.#{format}"}), remote_ryan.post(:register)
0
       end
0
     end
0
   end
0
-  
0
+
0
   def test_setting_format_before_site
0
     resource = Class.new(ActiveResource::Base)
0
     resource.format = :json
0
     resource.site   = 'http://37s.sunrise.i:3000'
0
     assert_equal ActiveResource::Formats[:json], resource.connection.format
0
   end
0
-  
0
+
0
   private
0
     def using_format(klass, mime_type_reference)
0
       previous_format = klass.format

Comments

wmoxam Wed Sep 17 14:19:03 -0700 2008

The documentation wasn’t updated. So encode has to_xml’s documentation.

Also, why is the API changing in a point release (2.1.1)? Shouldn’t this have waited until 2.2, and a deprecation warning added in 2.1.1?