aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/base/mime_responds.rb150
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb42
-rw-r--r--actionpack/test/controller/mime_responds_test.rb22
3 files changed, 145 insertions, 69 deletions
diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb
index 7320b0f858..d717388045 100644
--- a/actionpack/lib/action_controller/base/mime_responds.rb
+++ b/actionpack/lib/action_controller/base/mime_responds.rb
@@ -1,5 +1,57 @@
module ActionController #:nodoc:
module MimeResponds #:nodoc:
+ extend ActiveSupport::Concern
+
+ included do
+ class_inheritable_reader :mimes_for_respond_to
+ respond_to # Set mimes_for_respond_to hash
+ end
+
+ module ClassMethods
+ # Defines mimes that are rendered by default when invoking respond_with.
+ #
+ # Examples:
+ #
+ # respond_to :html, :xml, :json
+ #
+ # All actions on your controller will respond to :html, :xml and :json.
+ #
+ # But if you want to specify it based on your actions, you can use only and
+ # except:
+ #
+ # respond_to :html
+ # respond_to :xml, :json, :except => [ :edit ]
+ #
+ # The definition above explicits that all actions respond to :html. And all
+ # actions except :edit respond to :xml and :json.
+ #
+ # You can specify also only parameters:
+ #
+ # respond_to :rjs, :only => :create
+ #
+ def respond_to(*mimes)
+ options = mimes.extract_options!
+ mimes_hash = {}
+
+ only_actions = Array(options.delete(:only))
+ except_actions = Array(options.delete(:except))
+
+ mimes.each do |mime|
+ mime = mime.to_sym
+ mimes_hash[mime] = {}
+ mimes_hash[mime][:only] = only_actions unless only_actions.empty?
+ mimes_hash[mime][:except] = except_actions unless except_actions.empty?
+ end
+
+ write_inheritable_hash(:mimes_for_respond_to, mimes_hash)
+ end
+
+ # Clear all mimes in respond_to.
+ #
+ def clear_respond_to!
+ mimes_for_respond_to.each { |k,v| mimes[k] = { :only => [] } }
+ end
+ end
# Without web-service support, an action which collects the data for displaying a list of people
# might look something like this:
@@ -94,24 +146,20 @@ module ActionController #:nodoc:
#
# Mime::Type.register "image/jpg", :jpg
def respond_to(*mimes, &block)
- raise ArgumentError, "respond_to takes either types or a block, never both" unless mimes.any? ^ block
+ responder = Responder.new
- responder = Responder.new(request.formats)
+ block.call(responder) if block_given?
- if block_given?
- block.call(responder)
- else
- mimes.each { |mime| responder.send(mime) }
- end
+ mimes = collect_mimes_from_class_level if mimes.empty?
+ mimes.each { |mime| responder.send(mime) }
- mime = responder.respond
+ 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]
- if mime
- self.formats = [mime.to_sym]
- self.content_type = mime
- self.template.formats = [mime.to_sym]
-
- if response = responder.response_for(mime)
+ if response = responder.response_for(format)
response.call
else
default_render
@@ -121,10 +169,31 @@ module ActionController #:nodoc:
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
+
+ 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
- def initialize(priorities)
- @mime_type_priority = priorities
+ def initialize
@order, @responses = [], {}
end
@@ -132,7 +201,7 @@ module ActionController #:nodoc:
if args.any?
args.each { |type| send(type, &block) }
else
- custom(@mime_type_priority.first, &block)
+ custom(Mime::ALL, &block)
end
end
@@ -143,56 +212,35 @@ module ActionController #:nodoc:
@responses[mime_type] ||= block
end
- def respond
- available_mimes.first
- end
-
def response_for(mime)
- @responses[mime]
+ @responses[mime] || @responses[Mime::ALL]
end
- # Compares mimes sent by the client (@mime_type_priorities) with the ones
- # that the user configured in the controller respond_to. Returns them
- # all in an array.
- #
- def available_mimes
- mimes = []
-
- @mime_type_priority.each do |priority|
- if priority == Mime::ALL
- mimes << @order.first unless mimes.include?(@order.first)
- elsif @order.include?(priority)
- mimes << priority
- end
- end
-
- mimes << Mime::ALL if @order.include?(Mime::ALL)
- mimes
+ def self.generate_method_for_mime(mime)
+ sym = mime.is_a?(Symbol) ? mime : mime.to_sym
+ const = sym.to_s.upcase
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{sym}(&block) # def html(&block)
+ custom(Mime::#{const}, &block) # custom(Mime::HTML, &block)
+ end # end
+ RUBY
end
- protected
+ Mime::SET.each do |mime|
+ generate_method_for_mime(mime)
+ end
def method_missing(symbol, &block)
mime_constant = Mime.const_get(symbol.to_s.upcase)
if Mime::SET.include?(mime_constant)
- generate_method_for_mime(mime_constant)
+ self.class.generate_method_for_mime(mime_constant)
send(symbol, &block)
else
super
end
end
- def generate_method_for_mime(mime)
- sym = mime.is_a?(Symbol) ? mime : mime.to_sym
- const = sym.to_s.upcase
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{sym}(&block) # def html(&block)
- custom(Mime::#{const}, &block) # custom(Mime::HTML, &block)
- end # end
- RUBY
- end
-
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 3f23a5af7a..5f9463eb91 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -161,7 +161,7 @@ module ActionDispatch
# GET /posts/5.xml | request.format => Mime::XML
# GET /posts/5.xhtml | request.format => Mime::HTML
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
-
+ #
def format(view_path = [])
@env["action_dispatch.request.format"] ||=
if parameters[:format]
@@ -173,13 +173,11 @@ module ActionDispatch
end
end
+ # Expand raw_formats by converting Mime::ALL to the Mime::SET.
+ #
def formats
if ActionController::Base.use_accept_header
- if param = parameters[:format]
- Array.wrap(Mime[param])
- else
- accepts.dup
- end.tap do |ret|
+ raw_formats.tap do |ret|
if ret == ONLY_ALL
ret.replace Mime::SET
elsif all = ret.index(Mime::ALL)
@@ -187,7 +185,7 @@ module ActionDispatch
end
end
else
- [format] + Mime::SET
+ raw_formats + Mime::SET
end
end
@@ -232,7 +230,7 @@ module ActionDispatch
def xml_http_request?
!(@env['HTTP_X_REQUESTED_WITH'] !~ /XMLHttpRequest/i)
end
- alias xhr? :xml_http_request?
+ alias :xhr? :xml_http_request?
# Which IP addresses are "trusted proxies" that can be stripped from
# the right-hand-side of X-Forwarded-For
@@ -485,7 +483,35 @@ EOM
session['flash'] || {}
end
+ # Receives an array of mimes and return the first user sent mime that
+ # matches the order array.
+ #
+ def negotiate_mime(order)
+ raw_formats.each do |priority|
+ if priority == Mime::ALL
+ return order.first
+ elsif order.include?(priority)
+ return priority
+ end
+ end
+
+ order.include?(Mime::ALL) ? formats.first : nil
+ end
+
private
+
+ def raw_formats
+ if ActionController::Base.use_accept_header
+ if param = parameters[:format]
+ Array.wrap(Mime[param])
+ else
+ accepts.dup
+ end
+ else
+ [format]
+ end
+ end
+
def named_host?(host)
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index bee327998b..f2c20417f8 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -86,6 +86,7 @@ class RespondToController < ActionController::Base
type.mobile { render :text => "Mobile" }
end
ensure
+ Mime::SET.delete(:mobile)
Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
end
@@ -98,6 +99,7 @@ class RespondToController < ActionController::Base
end
ensure
+ Mime::SET.delete(:mobile)
Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
end
@@ -132,6 +134,7 @@ class RespondToController < ActionController::Base
end
ensure
+ Mime::SET.delete(:iphone)
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
end
@@ -145,6 +148,7 @@ class RespondToController < ActionController::Base
end
ensure
+ Mime::SET.delete(:iphone)
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
end
@@ -467,7 +471,13 @@ class MimeControllerTest < ActionController::TestCase
end
end
+class ClassRespondToController < ActionController::Base
+
+end
+
class AbstractPostController < ActionController::Base
+ respond_to :html, :iphone
+
self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/"
end
@@ -476,10 +486,7 @@ class PostController < AbstractPostController
around_filter :with_iphone
def index
- respond_to do |type|
- type.html
- type.iphone
- end
+ respond_to # It will use formats declared above
end
protected
@@ -489,17 +496,12 @@ protected
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
yield
ensure
+ Mime::SET.delete(:iphone)
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
end
end
class SuperPostController < PostController
- def index
- respond_to do |type|
- type.html
- type.iphone
- end
- end
end
class MimeControllerLayoutsTest < ActionController::TestCase