Permalink
Browse files

Added respond_with.

Signed-off-by: Yehuda Katz <wycats@gmail.com>
  • Loading branch information...
josevalim authored and wycats committed Jul 29, 2009
1 parent bbe8607 commit 09de34ca56598ae5d0302a14715b2a11b6cc9845
@@ -145,50 +145,163 @@ def clear_respond_to!
# 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)

This comment has been minimized.

@jonathan

jonathan Jul 29, 2009

looks like a typo. probably wanting @people and not @person

This comment has been minimized.

@vijaydev

vijaydev Nov 7, 2011

Member

fixed :D

# 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?
mimes = collect_mimes_from_class_level if mimes.empty?
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
@@ -471,8 +471,20 @@ def test_format_with_custom_response_type_and_request_headers
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 @@ def using_defaults
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 @@ def test_using_defaults_with_type_list
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 @@ def test_not_acceptable
@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

3 comments on commit 09de34c

@jonathan

This comment has been minimized.

jonathan replied Jul 29, 2009

So, the magic here seems to be that you don't really need a controller if you are using all restful actions. You could create a RESTcontroller < ApplictionController and have your PersonController < RESTController. Have your RESTcontroller have all your default rest actions and use @obj instead of @person and @options instead of options and set those with a before filter. Then, in your PersonController, just define the methods and away you go.

At least, that's a potential I see. I could be missing something.

@josevalim

This comment has been minimized.

Contributor

josevalim replied Jul 29, 2009

Yes, it's a typo. Thanks!
And about your second comment, you are right too. Check this: http://github.com/josevalim/inherited_resources.

@ghost

This comment has been minimized.

ghost replied Aug 20, 2009

Just wondering, but how would one use this along with options to the to_xml or to_json method for the model?

Please sign in to comment.