Permalink
Browse files

Allow respond_with to deal with http verb accordingly.

  • Loading branch information...
josevalim committed Jul 31, 2009
1 parent 7a4a679 commit 5b7e81efec649b424037c68a93bddad1bc4e0c23
@@ -202,11 +202,11 @@ def respond_to(*mimes, &block)
# formats allowed:
#
# class PeopleController < ApplicationController
- # respond_to :html, :xml, :json
+ # respond_to :xml, :json
#
# def index
# @people = Person.find(:all)
- # respond_with(@person)
+ # respond_with(@people)
# end
# end
#
@@ -216,64 +216,101 @@ def respond_to(*mimes, &block)
#
# If neither are available, it will raise an error.
#
- # Extra parameters given to respond_with are used when :to_format is invoked.
- # This allows you to set status and location for several formats at the same
- # time. Consider this restful controller response on create for both xml
- # and json formats:
+ # respond_with holds semantics for each HTTP verb. The example above cover
+ # GET requests. Let's check a POST request example:
+ #
+ # def create
+ # @person = Person.new(params[:person])
+ # @person.save
+ # respond_with(@person)
+ # end
+ #
+ # Since the request is a POST, respond_with will check wether @people
+ # resource have errors or not. If it has errors, it will render the error
+ # object with unprocessable entity status (422).
+ #
+ # If no error was found, it will render the @people resource, with status
+ # created (201) and location header set to person_url(@people).
+ #
+ # If you also want to provide html behavior in the method above, you can
+ # supply a block to customize it:
#
# class PeopleController < ApplicationController
- # respond_to :xml, :json
+ # respond_to :html, :xml, :json # Add :html to respond_to definition
#
# def create
- # @person = Person.new(params[:person])
- #
- # if @person.save
- # respond_with(@person, :status => :ok, :location => person_url(@person))
- # else
- # respond_with(@person.errors, :status => :unprocessable_entity)
+ # @person = Person.new(params[:pe])
+ #
+ # respond_with(@person) do |format|
+ # if @person.save
+ # flash[:notice] = 'Person was successfully created.'
+ # format.html { redirect_to @person }
+ # else
+ # format.html { render :action => "new" }
+ # end
# end
# end
# end
#
- # Finally, respond_with also accepts blocks, as in respond_to. Let's take
- # the same controller and create action above and add common html behavior:
+ # It works similarly for PUT requests:
#
- # class PeopleController < ApplicationController
- # respond_to :html, :xml, :json
+ # def update
+ # @person = Person.find(params[:id])
+ # @person.update_attributes(params[:person])
+ # respond_with(@person)
+ # end
#
- # def create
- # @person = Person.new(params[:person])
+ # In case of failures, it works as POST requests, but in success failures
+ # it just reply status ok (200) to the client.
#
- # if @person.save
- # options = { :status => :ok, :location => person_url(@person) }
+ # A DELETE request also works in the same way:
#
- # respond_with(@person, options) do |format|
- # format.html { redirect_to options[:location] }
- # end
- # else
- # respond_with(@person.errors, :status => :unprocessable_entity) do
- # format.html { render :action => :new }
- # end
- # end
- # end
+ # def destroy
+ # @person = Person.find(params[:id])
+ # @person.destroy
+ # respond_with(@person)
# end
#
+ # It just replies with status ok, indicating the record was successfuly
+ # destroyed.
+ #
def respond_with(resource, options={}, &block)
- begin
- respond_to(&block)
- rescue ActionView::MissingTemplate => e
- format = self.formats.first
+ respond_to(&block)
+ rescue ActionView::MissingTemplate => e
+ format = self.formats.first
+ resource = normalize_resource_options_by_verb(resource, options)
- if resource.respond_to?(:"to_#{format}")
- render options.merge(format => resource)
+ if resource.respond_to?(:"to_#{format}")
+ if options.delete(:no_content)
+ head options
else
- raise e
+ render options.merge(format => resource)
end
+ else
+ raise e
end
end
protected
+ # Change respond with behavior based on the HTTP verb.
+ #
+ def normalize_resource_options_by_verb(resource_or_array, options)
+ resource = resource_or_array.is_a?(Array) ? resource_or_array.last : resource_or_array
+ has_errors = resource.respond_to?(:errors) && !resource.errors.empty?
+
+ if has_errors && (request.post? || request.put?)
+ options.reverse_merge!(:status => :unprocessable_entity)
+ return resource.errors
+ elsif request.post?
+ options.reverse_merge!(:status => :created, :location => resource_or_array)
+ elsif !request.get?
+ options.reverse_merge!(:status => :ok, :no_content => true)
+ end
+
+ return resource
+ end
+
# Collect mimes declared in the class method respond_to valid for the
# current action.
#
@@ -474,13 +474,25 @@ def test_format_with_custom_response_type_and_request_headers
class RespondResource
undef_method :to_json
+ def self.model_name
+ @_model_name ||= ActiveModel::Name.new(name)
+ end
+
+ def to_param
+ 13
+ end
+
def to_xml
"XML"
end
def to_js
"JS"
end
+
+ def errors
+ []
+ end
end
class RespondWithController < ActionController::Base
@@ -520,6 +532,10 @@ def _render_js(js, options)
self.content_type ||= Mime::JS
self.response_body = js.respond_to?(:to_js) ? js.to_js : js
end
+
+ def respond_resource_url(id)
+ request.host + "/respond/resource/#{id.to_param}"
+ end
end
class InheritedRespondWithController < RespondWithController
@@ -593,6 +609,51 @@ def test_using_resource
end
end
+ def test_using_resource_for_post
+ @request.accept = "application/xml"
+
+ post :using_resource
+ assert_equal "application/xml", @response.content_type
+ assert_equal 201, @response.status
+ assert_equal "XML", @response.body
+ assert_equal "www.example.com/respond/resource/13", @response.location
+
+ errors = { :name => :invalid }
+ RespondResource.any_instance.stubs(:errors).returns(errors)
+ post :using_resource
+ assert_equal "application/xml", @response.content_type
+ assert_equal 422, @response.status
+ assert_equal errors.to_xml, @response.body
+ assert_nil @response.location
+ end
+
+ def test_using_resource_for_put
+ @request.accept = "application/xml"
+
+ put :using_resource
+ assert_equal "application/xml", @response.content_type
+ assert_equal 200, @response.status
+ assert_equal " ", @response.body
+ assert_nil @response.location
+
+ errors = { :name => :invalid }
+ RespondResource.any_instance.stubs(:errors).returns(errors)
+ put :using_resource
+ assert_equal "application/xml", @response.content_type
+ assert_equal 422, @response.status
+ assert_equal errors.to_xml, @response.body
+ assert_nil @response.location
+ end
+
+ def test_using_resource_for_delete
+ @request.accept = "application/xml"
+ delete :using_resource
+ assert_equal "application/xml", @response.content_type
+ assert_equal 200, @response.status
+ assert_equal " ", @response.body
+ assert_nil @response.location
+ end
+
def test_using_resource_with_options
@request.accept = "application/xml"
get :using_resource_with_options
@@ -648,8 +709,6 @@ def test_not_acceptable
end
class AbstractPostController < ActionController::Base
- respond_to :html, :iphone
-
self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/"
end
@@ -658,7 +717,7 @@ class PostController < AbstractPostController
around_filter :with_iphone
def index
- respond_to # It will use formats declared above
+ respond_to(:html, :iphone)
end
protected

0 comments on commit 5b7e81e

Please sign in to comment.