Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added better support for using the same actions to output for differe…

…nt sources depending on the Accept header [DHH] Added Base#render(:xml => xml) that works just like Base#render(:text => text), but sets the content-type to text/xml and the charset to UTF-8 [DHH]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3838 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 1c16649b4895012eac76f3a7f22f3382b0034a97 1 parent 8152695
@dhh dhh authored
View
19 actionpack/CHANGELOG
@@ -1,5 +1,24 @@
*SVN*
+* Added better support for using the same actions to output for different sources depending on the Accept header [DHH]. Example:
+
+ class WeblogController < ActionController::Base
+ def create
+ @post = Post.create(params[:post])
+
+ respond_to do |type|
+ type.js { render } # renders create.rjs
+ type.html { redirect_to :action => "index" }
+ type.xml do
+ headers["Location"] = url_for(:action => "show", :id => @post)
+ render(:nothing, :status => "201 Created")
+ end
+ end
+ end
+ end
+
+* Added Base#render(:xml => xml) that works just like Base#render(:text => text), but sets the content-type to text/xml and the charset to UTF-8 [DHH]
+
* Integration test's url_for now runs in the context of the last request (if any) so after post /products/show/1 url_for :action => 'new' will yield /product/new [Tobias Luetke]
* Re-added mixed-in helper methods for the JavascriptGenerator. Moved JavascriptGenerators methods to a module that is mixed in after the helpers are added. Also fixed that variables set in the enumeration methods like #collect are set correctly. Documentation added for the enumeration methods [Rick Olson]. Examples:
View
2  actionpack/lib/action_controller.rb
@@ -44,6 +44,7 @@
require 'action_controller/filters'
require 'action_controller/layout'
require 'action_controller/dependencies'
+require 'action_controller/mime_responds'
require 'action_controller/pagination'
require 'action_controller/scaffolding'
require 'action_controller/helpers'
@@ -67,6 +68,7 @@
include ActionController::Benchmarking
include ActionController::Rescue
include ActionController::Dependencies
+ include ActionController::MimeResponds
include ActionController::Pagination
include ActionController::Scaffolding
include ActionController::Helpers
View
13 actionpack/lib/action_controller/base.rb
@@ -1,3 +1,4 @@
+require 'action_controller/mime_type'
require 'action_controller/request'
require 'action_controller/response'
require 'action_controller/routing'
@@ -648,7 +649,10 @@ def render(options = nil, deprecated_status = nil, &block) #:doc:
elsif action_name = options[:action]
render_action(action_name, options[:status], options[:layout])
-
+
+ elsif xml = options[:xml]
+ render_xml(xml, options[:status])
+
elsif partial = options[:partial]
partial = default_template_name if partial == true
if collection = options[:collection]
@@ -715,10 +719,15 @@ def render_text(text = nil, status = nil)
end
def render_javascript(javascript, status = nil)
- @response.headers['Content-Type'] = 'text/javascript'
+ @response.headers['Content-Type'] = 'text/javascript; charset=UTF-8'
render_text(javascript, status)
end
+ def render_xml(xml, status = nil)
+ @response.headers['Content-Type'] = 'text/xml; charset=UTF-8'
+ render_text(xml, status)
+ end
+
def render_nothing(status = nil)
render_text(' ', status)
end
View
53 actionpack/lib/action_controller/mime_responds.rb
@@ -0,0 +1,53 @@
+module ActionController #:nodoc:
+ module MimeResponds #:nodoc:
+ def self.included(base)
+ base.send(:include, ActionController::MimeResponds::InstanceMethods)
+ end
+
+ module InstanceMethods
+ def respond_to(&block)
+ responder = Responder.new(block.binding)
+ yield responder
+ responder.respond
+ end
+ end
+
+ class Responder #:nodoc:
+ def initialize(block_binding)
+ @block_binding = block_binding
+ @mime_type_priority = eval("request.accepts", block_binding)
+ @order = []
+ @responses = {}
+ end
+
+ for mime_type in %w( all html js xml rss atom yaml )
+ eval <<-EOT
+ def #{mime_type}(&block)
+ @order << Mime::#{mime_type.upcase}
+ @responses[Mime::#{mime_type.upcase}] = block
+ end
+ EOT
+ end
+
+ def respond
+ for priority in @mime_type_priority
+ if priority == Mime::ALL
+ @responses[@order.first].call
+ return
+ else
+ if @order.include?(priority)
+ @responses[priority].call
+ return # mime type match found, be happy and return
+ end
+ end
+ end
+
+ if @order.include?(Mime::ALL)
+ @responses[Mime::ALL].call
+ else
+ eval 'render(:nothing => true, :status => "406 Not Acceptable")', @block_binding
+ end
+ end
+ end
+ end
+end
View
44 actionpack/lib/action_controller/mime_type.rb
@@ -0,0 +1,44 @@
+module Mime
+ class Type < String
+ def initialize(string, part_of_all = true)
+ @part_of_all = part_of_all
+ super(string)
+ end
+
+ def to_sym
+ SYMBOLIZED_MIME_TYPES[self] ? SYMBOLIZED_MIME_TYPES[self] : to_sym
+ end
+
+ def ===(list)
+ if list.is_a?(Array)
+ list.include?(self)
+ else
+ super
+ end
+ end
+ end
+
+ SYMBOLIZED_MIME_TYPES = {
+ "" => :unspecified,
+ "*/*" => :all,
+ "text/html" => :html,
+ "application/javascript" => :js,
+ "application/x-javascript" => :js,
+ "text/javascript" => :js,
+ "text/xml" => :xml,
+ "application/xml" => :xml,
+ "application/rss+xml" => :rss,
+ "application/rss+atom" => :atom,
+ "application/x-xml" => :xml,
+ "application/x-yaml" => :yaml
+ }
+
+ ALL = Type.new "*/*"
+ HTML = Type.new "text/html"
+ JS = Type.new "text/javascript"
+ JAVASCRIPT = Type.new "text/javascript"
+ XML = Type.new "application/xml"
+ RSS = Type.new "application/rss+xml"
+ ATOM = Type.new "application/rss+atom"
+ YAML = Type.new "application/x-yaml"
+end
View
19 actionpack/lib/action_controller/request.rb
@@ -54,13 +54,20 @@ def content_type
if @env['HTTP_X_POST_DATA_FORMAT']
case @env['HTTP_X_POST_DATA_FORMAT'].downcase.to_sym
- when :yaml
- @content_type = 'application/x-yaml'
- when :xml
- @content_type = 'application/xml'
- end
+ when :yaml
+ @content_type = 'application/x-yaml'
+ when :xml
+ @content_type = 'application/xml'
+ end
+ end
+
+ @content_type = Mime::Type.new(@content_type)
+ end
+
+ def accepts
+ @accepts ||= (@env['HTTP_ACCEPT'].strip.blank? ? "*/*" : @env['HTTP_ACCEPT']).split(";").collect! do |mime_type|
+ Mime::Type.new(mime_type.strip)
end
- @content_type
end
# Returns true if the request's "X-Requested-With" header contains
View
2  actionpack/lib/action_controller/test_process.rb
@@ -104,7 +104,7 @@ def recycle!
self.request_parameters = {}
self.query_parameters = {}
self.path_parameters = {}
- @request_method = nil
+ @request_method, @accepts, @content_type = nil, nil, nil
end
private
View
3  actionpack/lib/action_view/helpers/javascripts/prototype.js
@@ -703,7 +703,8 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
setRequestHeaders: function() {
var requestHeaders =
['X-Requested-With', 'XMLHttpRequest',
- 'X-Prototype-Version', Prototype.Version];
+ 'X-Prototype-Version', Prototype.Version,
+ 'Accept', 'text/javascript; text/html; text/xml; */*' ];
if (this.options.method == 'post') {
requestHeaders.push('Content-type',
View
102 actionpack/test/controller/mime_responds_test.rb
@@ -0,0 +1,102 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class RespondToController < ActionController::Base
+ def html_xml_or_rss
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.xml { render :text => "XML" }
+ type.rss { render :text => "RSS" }
+ type.all { render :text => "Nothing" }
+ end
+ end
+
+ def js_or_html
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.js { render :text => "JS" }
+ type.all { render :text => "Nothing" }
+ end
+ end
+
+ def html_or_xml
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.xml { render :text => "XML" }
+ type.all { render :text => "Nothing" }
+ end
+ end
+
+ def just_xml
+ respond_to do |type|
+ type.xml { render :text => "XML" }
+ end
+ end
+
+ def rescue_action(e)
+ raise unless ActionController::MissingTemplate === e
+ end
+end
+
+class MimeControllerTest < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ @controller = RespondToController.new
+ @request.host = "www.example.com"
+ end
+
+ def test_html
+ @request.env["HTTP_ACCEPT"] = "text/html"
+ get :js_or_html
+ assert_equal 'HTML', @response.body
+
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+
+ get :just_xml
+ assert_response 406
+ end
+
+ def test_all
+ @request.env["HTTP_ACCEPT"] = "*/*"
+ get :js_or_html
+ assert_equal 'HTML', @response.body # js is not part of all
+
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+
+ get :just_xml
+ assert_equal 'XML', @response.body
+ end
+
+ def test_xml
+ @request.env["HTTP_ACCEPT"] = "application/xml"
+ get :html_xml_or_rss
+ assert_equal 'XML', @response.body
+ end
+
+ def test_js_or_html
+ @request.env["HTTP_ACCEPT"] = "text/javascript; text/html"
+ get :js_or_html
+ assert_equal 'JS', @response.body
+
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+
+ get :just_xml
+ assert_response 406
+ end
+
+ def test_js_or_anything
+ @request.env["HTTP_ACCEPT"] = "text/javascript; */*"
+ get :js_or_html
+ assert_equal 'JS', @response.body
+
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+
+ get :just_xml
+ assert_equal 'XML', @response.body
+ end
+end
View
4 actionpack/test/controller/new_render_test.rb
@@ -544,14 +544,14 @@ def test_render_text_with_assigns
def test_update_page
get :update_page
assert_template nil
- assert_equal 'text/javascript', @response.headers['Content-Type']
+ assert_equal 'text/javascript; charset=UTF-8', @response.headers['Content-Type']
assert_equal 2, @response.body.split($/).length
end
def test_update_page_with_instance_variables
get :update_page_with_instance_variables
assert_template nil
- assert_equal 'text/javascript', @response.headers['Content-Type']
+ assert_equal 'text/javascript; charset=UTF-8', @response.headers['Content-Type']
assert_match /balance/, @response.body
assert_match /\$37/, @response.body
end
View
2  actionpack/test/controller/webservice_test.rb
@@ -71,7 +71,7 @@ def test_register_and_use_yaml_as_symbol
end
def test_register_and_use_xml_simple
- ActionController::Base.param_parsers['application/xml'] = :xml_simple
+ ActionController::Base.param_parsers['application/xml'] = Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) }
process('POST', 'application/xml', '<request><summary>content...</summary><title>SimpleXml</title></request>' )
assert_equal 'summary, title', @controller.response.body
assert @controller.params.has_key?(:summary)
Please sign in to comment.
Something went wrong with that request. Please try again.