aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/metal
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2009-08-01 15:29:39 +0200
committerJosé Valim <jose.valim@gmail.com>2009-08-07 17:16:15 +0200
commitf59984cc81bd1a64a53a2480a9b4e05fe7357d7c (patch)
tree66e77bc67fb50cd5dccf19f41120aceab80f21b1 /actionpack/lib/action_controller/metal
parentc44f7e39f46058842845f8c95c3e49f7c59c3aad (diff)
downloadrails-f59984cc81bd1a64a53a2480a9b4e05fe7357d7c.tar.gz
rails-f59984cc81bd1a64a53a2480a9b4e05fe7357d7c.tar.bz2
rails-f59984cc81bd1a64a53a2480a9b4e05fe7357d7c.zip
Add nagivational behavior to respond_with.
Diffstat (limited to 'actionpack/lib/action_controller/metal')
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb7
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb157
2 files changed, 114 insertions, 50 deletions
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 6d35137428..8575d30335 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -55,14 +55,15 @@ module ActionController
elsif args.empty?
raise ArgumentError, "too few arguments to head"
end
- options = args.extract_options!
- status = args.shift || options.delete(:status) || :ok
+ options = args.extract_options!
+ status = args.shift || options.delete(:status) || :ok
+ location = options.delete(:location)
options.each do |key, value|
headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
end
- render :nothing => true, :status => status
+ render :nothing => true, :status => status, :location => location
end
# Sets the etag and/or last_modified on the response and checks it against
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 9a6c8aa58b..837496e477 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -198,11 +198,11 @@ module ActionController #:nodoc:
end
# respond_with allows you to respond an action with a given resource. It
- # requires that you set your class with a :respond_to method with the
+ # requires that you set your class with a respond_to method with the
# formats allowed:
#
# class PeopleController < ApplicationController
- # respond_to :xml, :json
+ # respond_to :html, :xml, :json
#
# def index
# @people = Person.find(:all)
@@ -210,14 +210,43 @@ module ActionController #:nodoc:
# end
# end
#
- # When a request comes with format :xml, the respond_with will first search
- # for a template as person/index.xml, if the template is not available, it
- # will see if the given resource responds to :to_xml.
+ # When a request comes, for example with format :xml, three steps happen:
+ #
+ # 1) respond_with searches for a template at people/index.xml;
+ #
+ # 2) if the template is not available, it will check if the given
+ # resource responds to :to_xml.
+ #
+ # 3) if a :location option was provided, redirect to the location with
+ # redirect status if a string was given, or render an action if a
+ # symbol was given.
+ #
+ # If all steps fail, a missing template error will be raised.
+ #
+ # === Supported options
+ #
+ # [status]
+ # Sets the response status.
+ #
+ # [head]
+ # Tell respond_with to set the content type, status and location header,
+ # but do not render the object, leaving the response body empty. This
+ # option only has effect if the resource is being rendered. If a
+ # template was found, it's going to be rendered anyway.
+ #
+ # [location]
+ # Sets the location header with the given value. It accepts a string,
+ # representing the location header value, or a symbol representing an
+ # action name.
+ #
+ # === Builtin HTTP verb semantics
#
- # If neither are available, it will raise an error.
+ # respond_with holds semantics for each HTTP verb. Depending on the verb
+ # and the resource status, respond_with will automatically set the options
+ # above.
#
- # respond_with holds semantics for each HTTP verb. The example above cover
- # GET requests. Let's check a POST request example:
+ # Above we saw an example for GET requests, where actually no option is
+ # configured. A create action for POST requests, could be written as:
#
# def create
# @person = Person.new(params[:person])
@@ -225,34 +254,40 @@ module ActionController #:nodoc:
# 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).
+ # respond_with will inspect the @person object and check if we have any
+ # error. If errors are empty, it will add status and location to the options
+ # hash. Then the create action in case of success, is equivalent to this:
#
- # If no error was found, it will render the @people resource, with status
- # created (201) and location header set to person_url(@people).
+ # respond_with(@person, :status => :created, :location => @person)
#
- # If you also want to provide html behavior in the method above, you can
- # supply a block to customize it:
+ # From them on, the lookup happens as described above. Let's suppose a :xml
+ # request and we don't have a people/create.xml template. But since the
+ # @person object responds to :to_xml, it will render the newly created
+ # resource and set status and location.
#
- # class PeopleController < ApplicationController
- # respond_to :html, :xml, :json # Add :html to respond_to definition
- #
- # def create
- # @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
+ # However, if the request is :html, a template is not available and @person
+ # does not respond to :to_html. But since a :location options was provided,
+ # it will redirect to it.
+ #
+ # In case of failures (when the @person could not be saved and errors are
+ # not empty), respond_with can be expanded as this:
+ #
+ # respond_with(@person.errors, :status => :unprocessable_entity, :location => :new)
+ #
+ # In other words, respond_with(@person) for POST requests is expanded
+ # internally into this:
+ #
+ # def create
+ # @person = Person.new(params[:person])
+ #
+ # if @person.save
+ # respond_with(@person, :status => :created, :location => @person)
+ # else
+ # respond_with(@person.errors, :status => :unprocessable_entity, :location => :new)
# end
# end
#
- # It works similarly for PUT requests:
+ # For an update action for PUT requests, we would have:
#
# def update
# @person = Person.find(params[:id])
@@ -260,10 +295,23 @@ module ActionController #:nodoc:
# respond_with(@person)
# end
#
- # In case of failures, it works as POST requests, but in success failures
- # it just reply status ok (200) to the client.
+ # Which, in face of success and failure scenarios, can be expanded as:
#
- # A DELETE request also works in the same way:
+ # def update
+ # @person = Person.find(params[:id])
+ # @person.update_attributes(params[:person])
+ #
+ # if @person.save
+ # respond_with(@person, :status => :ok, :location => @person, :head => true)
+ # else
+ # respond_with(@person.errors, :status => :unprocessable_entity, :location => :edit)
+ # end
+ # end
+ #
+ # Notice that in case of success, we just need to reply :ok to the client.
+ # The option :head ensures that the object is not rendered.
+ #
+ # Finally, we have the destroy action with DELETE verb:
#
# def destroy
# @person = Person.find(params[:id])
@@ -271,21 +319,30 @@ module ActionController #:nodoc:
# respond_with(@person)
# end
#
- # It just replies with status ok, indicating the record was successfuly
- # destroyed.
+ # Which is expanded as:
+ #
+ # def destroy
+ # @person = Person.find(params[:id])
+ # @person.destroy
+ # respond_with(@person, :status => :ok, :location => @person, :head => true)
+ # end
+ #
+ # In this case, since @person.destroyed? returns true, polymorphic urls will
+ # redirect to the collection url, instead of the resource url.
#
def respond_with(resource, options={}, &block)
respond_to(&block)
rescue ActionView::MissingTemplate => e
format = self.formats.first
resource = normalize_resource_options_by_verb(resource, options)
+ action = options.delete(:location) if options[:location].is_a?(Symbol)
if resource.respond_to?(:"to_#{format}")
- if options.delete(:no_content)
- head options
- else
- render options.merge(format => resource)
- end
+ options.delete(:head) ? head(options) : render(options.merge(format => resource))
+ elsif action
+ render :action => action
+ elsif options[:location]
+ redirect_to options[:location]
else
raise e
end
@@ -296,16 +353,22 @@ module ActionController #:nodoc:
# 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?
+ resource = resource_or_array.is_a?(Array) ? resource_or_array.last : resource_or_array
- if has_errors && (request.post? || request.put?)
- options.reverse_merge!(:status => :unprocessable_entity)
+ if resource.respond_to?(:errors) && !resource.errors.empty?
+ options[:status] ||= :unprocessable_entity
+ options[:location] ||= :new if request.post?
+ options[:location] ||= :edit if request.put?
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)
+ options[:location] ||= resource_or_array
+
+ if request.post?
+ options[:status] ||= :created
+ else
+ options[:status] ||= :ok
+ options[:head] = true unless options.key?(:head)
+ end
end
return resource