aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/base/mime_responds.rb165
-rw-r--r--actionpack/test/controller/mime_responds_test.rb70
-rw-r--r--actionpack/test/fixtures/respond_with/using_resource.html.erb1
3 files changed, 209 insertions, 27 deletions
diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb
index 0ce6660c98..f7c1b071e7 100644
--- a/actionpack/lib/action_controller/base/mime_responds.rb
+++ b/actionpack/lib/action_controller/base/mime_responds.rb
@@ -145,8 +145,45 @@ module ActionController #:nodoc:
# environment.rb as follows.
#
# Mime::Type.register "image/jpg", :jpg
+ #
+ # Respond to also allows you to specify a common block for different formats by using any:
+ #
+ # def index
+ # @people = Person.find(:all)
+ #
+ # respond_to do |format|
+ # format.html
+ # format.any(:xml, :json) { render request.format.to_sym => @people }
+ # end
+ # end
+ #
+ # In the example above, if the format is xml, it will render:
+ #
+ # render :xml => @people
+ #
+ # Or if the format is json:
+ #
+ # render :json => @people
+ #
+ # Since this is a common pattern, you can use the class method respond_to
+ # with the respond_with method to have the same results:
+ #
+ # class PeopleController < ApplicationController
+ # respond_to :html, :xml, :json
+ #
+ # def index
+ # @people = Person.find(:all)
+ # respond_with(@person)
+ # end
+ # end
+ #
+ # Be sure to check respond_with and respond_to documentation for more examples.
+ #
def respond_to(*mimes, &block)
+ options = mimes.extract_options!
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
+
+ resource = options.delete(:with)
responder = Responder.new
block.call(responder) if block_given?
@@ -154,41 +191,117 @@ module ActionController #:nodoc:
mimes.each { |mime| responder.send(mime) }
if format = request.negotiate_mime(responder.order)
- # TODO It should be just: self.formats = [ :foo ]
- self.formats = [format.to_sym]
- self.content_type = format
- self.template.formats = [format.to_sym]
+ respond_to_block_or_template_or_resource(format, resource,
+ options, &responder.response_for(format))
+ else
+ head :not_acceptable
+ end
+ end
- if response = responder.response_for(format)
- response.call
+ # 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
+ # formats allowed:
+ #
+ # class PeopleController < ApplicationController
+ # respond_to :html, :xml, :json
+ #
+ # def index
+ # @people = Person.find(:all)
+ # respond_with(@person)
+ # 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.
+ #
+ # 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:
+ #
+ # class PeopleController < ApplicationController
+ # respond_to :xml, :json
+ #
+ # 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)
+ # 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:
+ #
+ # class PeopleController < ApplicationController
+ # respond_to :html, :xml, :json
+ #
+ # def create
+ # @person = Person.new(params[:person])
+ #
+ # if @person.save
+ # options = { :status => :ok, :location => person_url(@person) }
+ #
+ # 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
+ # end
+ #
+ def respond_with(resource, options={}, &block)
+ respond_to(options.merge!(:with => resource), &block)
+ end
+
+ protected
+
+ def respond_to_block_or_template_or_resource(format, resource, options)
+ # TODO It should be just: self.formats = [ :foo ]
+ self.formats = [format.to_sym]
+ self.content_type = format
+ self.template.formats = [format.to_sym]
+
+ return yield if block_given?
+
+ begin
+ default_render
+ rescue ActionView::MissingTemplate => e
+ if resource && resource.respond_to?("to_#{format.to_sym}")
+ render options.merge(format.to_sym => resource)
else
- default_render
+ raise e
end
- else
- head :not_acceptable
end
end
- protected
+ # Collect mimes declared in the class method respond_to valid for the
+ # current action.
+ #
+ def collect_mimes_from_class_level #:nodoc:
+ action = action_name.to_sym
- # Collect mimes declared in the class method respond_to valid for the
- # current action.
- #
- def collect_mimes_from_class_level #:nodoc:
- action = action_name.to_sym
-
- mimes_for_respond_to.keys.select do |mime|
- config = mimes_for_respond_to[mime]
-
- if config[:except]
- !config[:except].include?(action)
- elsif config[:only]
- config[:only].include?(action)
- else
- true
- end
+ mimes_for_respond_to.keys.select do |mime|
+ config = mimes_for_respond_to[mime]
+
+ if config[:except]
+ !config[:except].include?(action)
+ elsif config[:only]
+ config[:only].include?(action)
+ else
+ true
end
end
+ end
class Responder #:nodoc:
attr_accessor :order
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 48b343272e..369d683d23 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -471,8 +471,20 @@ class RespondToControllerTest < ActionController::TestCase
end
end
+class RespondResource
+ undef_method :to_json
+
+ def to_xml
+ "XML"
+ end
+
+ def to_js
+ "JS"
+ end
+end
+
class RespondWithController < ActionController::Base
- respond_to :html
+ respond_to :html, :json
respond_to :xml, :except => :using_defaults
respond_to :js, :only => :using_defaults
@@ -485,6 +497,23 @@ class RespondWithController < ActionController::Base
def using_defaults_with_type_list
respond_to(:js, :xml)
end
+
+ def using_resource
+ respond_with(RespondResource.new)
+ end
+
+ def using_resource_with_options
+ respond_with(RespondResource.new, :status => :unprocessable_entity) do |format|
+ format.js
+ end
+ end
+
+protected
+
+ def _render_js(js, options)
+ self.content_type ||= Mime::JS
+ self.response_body = js.respond_to?(:to_js) ? js.to_js : js
+ end
end
class RespondWithControllerTest < ActionController::TestCase
@@ -530,6 +559,37 @@ class RespondWithControllerTest < ActionController::TestCase
assert_equal "<p>Hello world!</p>\n", @response.body
end
+ def test_using_resource
+ @request.accept = "text/html"
+ get :using_resource
+ assert_equal "text/html", @response.content_type
+ assert_equal "Hello world!", @response.body
+
+ @request.accept = "application/xml"
+ get :using_resource
+ assert_equal "application/xml", @response.content_type
+ assert_equal "XML", @response.body
+
+ @request.accept = "application/json"
+ assert_raise ActionView::MissingTemplate do
+ get :using_resource
+ end
+ end
+
+ def test_using_resource_with_options
+ @request.accept = "application/xml"
+ get :using_resource_with_options
+ assert_equal "application/xml", @response.content_type
+ assert_equal 422, @response.status
+ assert_equal "XML", @response.body
+
+ @request.accept = "text/javascript"
+ get :using_resource_with_options
+ assert_equal "text/javascript", @response.content_type
+ assert_equal 422, @response.status
+ assert_equal "JS", @response.body
+ end
+
def test_not_acceptable
@request.accept = "application/xml"
get :using_defaults
@@ -538,6 +598,14 @@ class RespondWithControllerTest < ActionController::TestCase
@request.accept = "text/html"
get :using_defaults_with_type_list
assert_equal 406, @response.status
+
+ @request.accept = "application/json"
+ get :using_defaults_with_type_list
+ assert_equal 406, @response.status
+
+ @request.accept = "text/javascript"
+ get :using_resource
+ assert_equal 406, @response.status
end
end
diff --git a/actionpack/test/fixtures/respond_with/using_resource.html.erb b/actionpack/test/fixtures/respond_with/using_resource.html.erb
new file mode 100644
index 0000000000..6769dd60bd
--- /dev/null
+++ b/actionpack/test/fixtures/respond_with/using_resource.html.erb
@@ -0,0 +1 @@
+Hello world! \ No newline at end of file