mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge branch 'master' of git://github.com/rails/rails
This commit is contained in:
commit
e566fc0577
16 changed files with 540 additions and 95 deletions
|
@ -1,5 +1,55 @@
|
||||||
module ActionController #:nodoc:
|
module ActionController #:nodoc:
|
||||||
module MimeResponds #:nodoc:
|
module MimeResponds #:nodoc:
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
class_inheritable_reader :mimes_for_respond_to
|
||||||
|
clear_respond_to
|
||||||
|
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!
|
||||||
|
|
||||||
|
only_actions = Array(options.delete(:only))
|
||||||
|
except_actions = Array(options.delete(:except))
|
||||||
|
|
||||||
|
mimes.each do |mime|
|
||||||
|
mime = mime.to_sym
|
||||||
|
mimes_for_respond_to[mime] = {}
|
||||||
|
mimes_for_respond_to[mime][:only] = only_actions unless only_actions.empty?
|
||||||
|
mimes_for_respond_to[mime][:except] = except_actions unless except_actions.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Clear all mimes in respond_to.
|
||||||
|
#
|
||||||
|
def clear_respond_to
|
||||||
|
write_inheritable_attribute(:mimes_for_respond_to, ActiveSupport::OrderedHash.new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Without web-service support, an action which collects the data for displaying a list of people
|
# Without web-service support, an action which collects the data for displaying a list of people
|
||||||
# might look something like this:
|
# might look something like this:
|
||||||
#
|
#
|
||||||
|
@ -92,50 +142,187 @@ module ActionController #:nodoc:
|
||||||
# environment.rb as follows.
|
# environment.rb as follows.
|
||||||
#
|
#
|
||||||
# Mime::Type.register "image/jpg", :jpg
|
# Mime::Type.register "image/jpg", :jpg
|
||||||
def respond_to(*types, &block)
|
#
|
||||||
raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
|
# Respond to also allows you to specify a common block for different formats by using any:
|
||||||
block ||= lambda { |responder| types.each { |type| responder.send(type) } }
|
#
|
||||||
responder = Responder.new(self)
|
# def index
|
||||||
block.call(responder)
|
# @people = Person.find(:all)
|
||||||
responder.respond
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
mimes = collect_mimes_from_class_level if mimes.empty?
|
||||||
|
mimes.each { |mime| responder.send(mime) }
|
||||||
|
block.call(responder) if block_given?
|
||||||
|
|
||||||
|
if format = request.negotiate_mime(responder.order)
|
||||||
|
respond_to_block_or_template_or_resource(format, resource,
|
||||||
|
options, &responder.response_for(format))
|
||||||
|
else
|
||||||
|
head :not_acceptable
|
||||||
|
end
|
||||||
|
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
|
||||||
|
# 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)
|
||||||
|
self.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
|
||||||
|
raise e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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
|
end
|
||||||
|
|
||||||
class Responder #:nodoc:
|
class Responder #:nodoc:
|
||||||
|
attr_accessor :order
|
||||||
def initialize(controller)
|
|
||||||
@controller = controller
|
|
||||||
@request = controller.request
|
|
||||||
@response = controller.response
|
|
||||||
|
|
||||||
@mime_type_priority = @request.formats
|
def initialize
|
||||||
|
@order, @responses = [], {}
|
||||||
@order = []
|
|
||||||
@responses = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def custom(mime_type, &block)
|
|
||||||
mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
|
|
||||||
|
|
||||||
@order << mime_type
|
|
||||||
|
|
||||||
@responses[mime_type] ||= Proc.new do
|
|
||||||
# TODO: Remove this when new base is merged in
|
|
||||||
@controller.formats = [mime_type.to_sym]
|
|
||||||
@controller.content_type = mime_type
|
|
||||||
@controller.template.formats = [mime_type.to_sym]
|
|
||||||
|
|
||||||
block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def any(*args, &block)
|
def any(*args, &block)
|
||||||
if args.any?
|
if args.any?
|
||||||
args.each { |type| send(type, &block) }
|
args.each { |type| send(type, &block) }
|
||||||
else
|
else
|
||||||
custom(@mime_type_priority.first, &block)
|
custom(Mime::ALL, &block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
alias :all :any
|
||||||
|
|
||||||
|
def custom(mime_type, &block)
|
||||||
|
mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
|
||||||
|
|
||||||
|
@order << mime_type
|
||||||
|
@responses[mime_type] ||= block
|
||||||
|
end
|
||||||
|
|
||||||
|
def response_for(mime)
|
||||||
|
@responses[mime] || @responses[Mime::ALL]
|
||||||
|
end
|
||||||
|
|
||||||
def self.generate_method_for_mime(mime)
|
def self.generate_method_for_mime(mime)
|
||||||
sym = mime.is_a?(Symbol) ? mime : mime.to_sym
|
sym = mime.is_a?(Symbol) ? mime : mime.to_sym
|
||||||
const = sym.to_s.upcase
|
const = sym.to_s.upcase
|
||||||
|
@ -152,7 +339,7 @@ module ActionController #:nodoc:
|
||||||
|
|
||||||
def method_missing(symbol, &block)
|
def method_missing(symbol, &block)
|
||||||
mime_constant = Mime.const_get(symbol.to_s.upcase)
|
mime_constant = Mime.const_get(symbol.to_s.upcase)
|
||||||
|
|
||||||
if Mime::SET.include?(mime_constant)
|
if Mime::SET.include?(mime_constant)
|
||||||
self.class.generate_method_for_mime(mime_constant)
|
self.class.generate_method_for_mime(mime_constant)
|
||||||
send(symbol, &block)
|
send(symbol, &block)
|
||||||
|
@ -161,25 +348,6 @@ module ActionController #:nodoc:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def respond
|
|
||||||
for priority in @mime_type_priority
|
|
||||||
if priority == Mime::ALL
|
|
||||||
@responses[@order.first].call
|
|
||||||
return
|
|
||||||
else
|
|
||||||
if @responses[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
|
|
||||||
@controller.send :head, :not_acceptable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,11 +11,10 @@ module ActionController
|
||||||
|
|
||||||
def render(options)
|
def render(options)
|
||||||
super
|
super
|
||||||
options[:_template] ||= _action_view._partial
|
|
||||||
self.content_type ||= begin
|
self.content_type ||= begin
|
||||||
mime = options[:_template].mime_type
|
mime = options[:_template].mime_type
|
||||||
formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first)
|
formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first)
|
||||||
end
|
end.to_s
|
||||||
response_body
|
response_body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ module Mime
|
||||||
end
|
end
|
||||||
|
|
||||||
def lookup_by_extension(extension)
|
def lookup_by_extension(extension)
|
||||||
EXTENSION_LOOKUP[extension]
|
EXTENSION_LOOKUP[extension.to_s]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
|
# Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
# http://www.iana.org/assignments/media-types/
|
# http://www.iana.org/assignments/media-types/
|
||||||
|
|
||||||
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
|
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
|
||||||
Mime::Type.register "*/*", :all
|
|
||||||
Mime::Type.register "text/plain", :text, [], %w(txt)
|
Mime::Type.register "text/plain", :text, [], %w(txt)
|
||||||
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
|
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
|
||||||
Mime::Type.register "text/css", :css
|
Mime::Type.register "text/css", :css
|
||||||
|
@ -18,4 +17,7 @@ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
|
||||||
|
|
||||||
# http://www.ietf.org/rfc/rfc4627.txt
|
# http://www.ietf.org/rfc/rfc4627.txt
|
||||||
# http://www.json.org/JSONRequest.html
|
# http://www.json.org/JSONRequest.html
|
||||||
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
|
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
|
||||||
|
|
||||||
|
# Create Mime::ALL but do not add it to the SET.
|
||||||
|
Mime::ALL = Mime::Type.new("*/*", :all, [])
|
||||||
|
|
|
@ -161,7 +161,7 @@ module ActionDispatch
|
||||||
# GET /posts/5.xml | request.format => Mime::XML
|
# GET /posts/5.xml | request.format => Mime::XML
|
||||||
# GET /posts/5.xhtml | request.format => Mime::HTML
|
# 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>
|
# 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 = [])
|
def format(view_path = [])
|
||||||
@env["action_dispatch.request.format"] ||=
|
@env["action_dispatch.request.format"] ||=
|
||||||
if parameters[:format]
|
if parameters[:format]
|
||||||
|
@ -173,13 +173,11 @@ module ActionDispatch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Expand raw_formats by converting Mime::ALL to the Mime::SET.
|
||||||
|
#
|
||||||
def formats
|
def formats
|
||||||
if ActionController::Base.use_accept_header
|
if ActionController::Base.use_accept_header
|
||||||
if param = parameters[:format]
|
raw_formats.tap do |ret|
|
||||||
Array.wrap(Mime[param])
|
|
||||||
else
|
|
||||||
accepts.dup
|
|
||||||
end.tap do |ret|
|
|
||||||
if ret == ONLY_ALL
|
if ret == ONLY_ALL
|
||||||
ret.replace Mime::SET
|
ret.replace Mime::SET
|
||||||
elsif all = ret.index(Mime::ALL)
|
elsif all = ret.index(Mime::ALL)
|
||||||
|
@ -187,7 +185,7 @@ module ActionDispatch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
[format] + Mime::SET
|
raw_formats + Mime::SET
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -232,7 +230,7 @@ module ActionDispatch
|
||||||
def xml_http_request?
|
def xml_http_request?
|
||||||
!(@env['HTTP_X_REQUESTED_WITH'] !~ /XMLHttpRequest/i)
|
!(@env['HTTP_X_REQUESTED_WITH'] !~ /XMLHttpRequest/i)
|
||||||
end
|
end
|
||||||
alias xhr? :xml_http_request?
|
alias :xhr? :xml_http_request?
|
||||||
|
|
||||||
# Which IP addresses are "trusted proxies" that can be stripped from
|
# Which IP addresses are "trusted proxies" that can be stripped from
|
||||||
# the right-hand-side of X-Forwarded-For
|
# the right-hand-side of X-Forwarded-For
|
||||||
|
@ -485,7 +483,35 @@ EOM
|
||||||
session['flash'] || {}
|
session['flash'] || {}
|
||||||
end
|
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
|
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)
|
def named_host?(host)
|
||||||
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
|
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
|
||||||
end
|
end
|
||||||
|
|
|
@ -160,11 +160,24 @@ module ActionView
|
||||||
#
|
#
|
||||||
# error_messages_for 'user'
|
# error_messages_for 'user'
|
||||||
#
|
#
|
||||||
|
# You can also supply an object:
|
||||||
|
#
|
||||||
|
# error_messages_for @user
|
||||||
|
#
|
||||||
|
# This will use the last part of the model name in the presentation. For instance, if
|
||||||
|
# this is a MyKlass::User object, this will use "user" as the name in the String. This
|
||||||
|
# is taken from MyKlass::User.model_name.human, which can be overridden.
|
||||||
|
#
|
||||||
# To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
|
# To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
|
||||||
# will be the name used in the header message:
|
# will be the name used in the header message:
|
||||||
#
|
#
|
||||||
# error_messages_for 'user_common', 'user', :object_name => 'user'
|
# error_messages_for 'user_common', 'user', :object_name => 'user'
|
||||||
#
|
#
|
||||||
|
# You can also use a number of objects, which will have the same naming semantics
|
||||||
|
# as a single object.
|
||||||
|
#
|
||||||
|
# error_messages_for @user, @post
|
||||||
|
#
|
||||||
# If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual
|
# If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual
|
||||||
# object (or array of objects to use):
|
# object (or array of objects to use):
|
||||||
#
|
#
|
||||||
|
@ -176,15 +189,20 @@ module ActionView
|
||||||
def error_messages_for(*params)
|
def error_messages_for(*params)
|
||||||
options = params.extract_options!.symbolize_keys
|
options = params.extract_options!.symbolize_keys
|
||||||
|
|
||||||
if object = options.delete(:object)
|
objects = Array.wrap(options.delete(:object) || params).map do |object|
|
||||||
objects = [object].flatten
|
unless object.respond_to?(:to_model)
|
||||||
else
|
object = instance_variable_get("@#{object}")
|
||||||
objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
|
object = convert_to_model(object)
|
||||||
|
else
|
||||||
|
object = object.to_model
|
||||||
|
options[:object_name] ||= object.class.model_name.human
|
||||||
|
end
|
||||||
|
object
|
||||||
end
|
end
|
||||||
|
|
||||||
objects.map! {|o| convert_to_model(o) }
|
objects.compact!
|
||||||
|
|
||||||
count = objects.inject(0) {|sum, object| sum + object.errors.count }
|
count = objects.inject(0) {|sum, object| sum + object.errors.count }
|
||||||
unless count.zero?
|
unless count.zero?
|
||||||
html = {}
|
html = {}
|
||||||
[:id, :class].each do |key|
|
[:id, :class].each do |key|
|
||||||
|
|
|
@ -86,6 +86,7 @@ class RespondToController < ActionController::Base
|
||||||
type.mobile { render :text => "Mobile" }
|
type.mobile { render :text => "Mobile" }
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
|
Mime::SET.delete(:mobile)
|
||||||
Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
|
Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -98,6 +99,7 @@ class RespondToController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
ensure
|
ensure
|
||||||
|
Mime::SET.delete(:mobile)
|
||||||
Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
|
Mime.module_eval { remove_const :MOBILE if const_defined?(:MOBILE) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -132,6 +134,7 @@ class RespondToController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
ensure
|
ensure
|
||||||
|
Mime::SET.delete(:iphone)
|
||||||
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
|
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -145,6 +148,7 @@ class RespondToController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
ensure
|
ensure
|
||||||
|
Mime::SET.delete(:iphone)
|
||||||
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
|
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -162,7 +166,7 @@ class RespondToController < ActionController::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class MimeControllerTest < ActionController::TestCase
|
class RespondToControllerTest < ActionController::TestCase
|
||||||
tests RespondToController
|
tests RespondToController
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
|
@ -436,10 +440,10 @@ class MimeControllerTest < ActionController::TestCase
|
||||||
def test_render_action_for_html
|
def test_render_action_for_html
|
||||||
@controller.instance_eval do
|
@controller.instance_eval do
|
||||||
def render(*args)
|
def render(*args)
|
||||||
unless args.empty?
|
@action = args.first[:action] unless args.empty?
|
||||||
@action = args.first[:action] || action_name
|
@action ||= action_name
|
||||||
end
|
|
||||||
response.body = "#{@action} - #{@template.formats}"
|
response.body = "#{@action} - #{formats}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -467,7 +471,185 @@ class MimeControllerTest < ActionController::TestCase
|
||||||
end
|
end
|
||||||
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, :json
|
||||||
|
respond_to :xml, :except => :using_defaults
|
||||||
|
respond_to :js, :only => :using_defaults
|
||||||
|
|
||||||
|
def using_defaults
|
||||||
|
respond_to do |format|
|
||||||
|
format.csv { render :text => "CSV" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def default_overwritten
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { render :text => "HTML" }
|
||||||
|
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 InheritedRespondWithController < RespondWithController
|
||||||
|
clear_respond_to
|
||||||
|
respond_to :xml, :json
|
||||||
|
|
||||||
|
def index
|
||||||
|
respond_with(RespondResource.new) do |format|
|
||||||
|
format.json { render :text => "JSON" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class RespondWithControllerTest < ActionController::TestCase
|
||||||
|
tests RespondWithController
|
||||||
|
|
||||||
|
def setup
|
||||||
|
super
|
||||||
|
ActionController::Base.use_accept_header = true
|
||||||
|
@request.host = "www.example.com"
|
||||||
|
end
|
||||||
|
|
||||||
|
def teardown
|
||||||
|
super
|
||||||
|
ActionController::Base.use_accept_header = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_using_defaults
|
||||||
|
@request.accept = "*/*"
|
||||||
|
get :using_defaults
|
||||||
|
assert_equal "text/html", @response.content_type
|
||||||
|
assert_equal 'Hello world!', @response.body
|
||||||
|
|
||||||
|
@request.accept = "text/csv"
|
||||||
|
get :using_defaults
|
||||||
|
assert_equal "text/csv", @response.content_type
|
||||||
|
assert_equal "CSV", @response.body
|
||||||
|
|
||||||
|
@request.accept = "text/javascript"
|
||||||
|
get :using_defaults
|
||||||
|
assert_equal "text/javascript", @response.content_type
|
||||||
|
assert_equal '$("body").visualEffect("highlight");', @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_using_defaults_with_type_list
|
||||||
|
@request.accept = "*/*"
|
||||||
|
get :using_defaults_with_type_list
|
||||||
|
assert_equal "text/javascript", @response.content_type
|
||||||
|
assert_equal '$("body").visualEffect("highlight");', @response.body
|
||||||
|
|
||||||
|
@request.accept = "application/xml"
|
||||||
|
get :using_defaults_with_type_list
|
||||||
|
assert_equal "application/xml", @response.content_type
|
||||||
|
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_default_overwritten
|
||||||
|
get :default_overwritten
|
||||||
|
assert_equal "text/html", @response.content_type
|
||||||
|
assert_equal "HTML", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_clear_respond_to
|
||||||
|
@controller = InheritedRespondWithController.new
|
||||||
|
@request.accept = "text/html"
|
||||||
|
get :index
|
||||||
|
assert_equal 406, @response.status
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_first_in_respond_to_has_higher_priority
|
||||||
|
@controller = InheritedRespondWithController.new
|
||||||
|
@request.accept = "*/*"
|
||||||
|
get :index
|
||||||
|
assert_equal "application/xml", @response.content_type
|
||||||
|
assert_equal "XML", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_not_acceptable
|
||||||
|
@request.accept = "application/xml"
|
||||||
|
get :using_defaults
|
||||||
|
assert_equal 406, @response.status
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
class AbstractPostController < ActionController::Base
|
class AbstractPostController < ActionController::Base
|
||||||
|
respond_to :html, :iphone
|
||||||
|
|
||||||
self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/"
|
self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -476,10 +658,7 @@ class PostController < AbstractPostController
|
||||||
around_filter :with_iphone
|
around_filter :with_iphone
|
||||||
|
|
||||||
def index
|
def index
|
||||||
respond_to do |type|
|
respond_to # It will use formats declared above
|
||||||
type.html
|
|
||||||
type.iphone
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
@ -489,17 +668,12 @@ protected
|
||||||
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
|
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
|
Mime::SET.delete(:iphone)
|
||||||
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
|
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class SuperPostController < PostController
|
class SuperPostController < PostController
|
||||||
def index
|
|
||||||
respond_to do |type|
|
|
||||||
type.html
|
|
||||||
type.iphone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class MimeControllerLayoutsTest < ActionController::TestCase
|
class MimeControllerLayoutsTest < ActionController::TestCase
|
||||||
|
|
|
@ -338,16 +338,11 @@ class RequestTest < ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
test "XMLHttpRequest" do
|
test "XMLHttpRequest" do
|
||||||
begin
|
with_accept_header false do
|
||||||
ActionController::Base.use_accept_header, old =
|
|
||||||
false, ActionController::Base.use_accept_header
|
|
||||||
|
|
||||||
request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'
|
request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'
|
||||||
request.expects(:parameters).at_least_once.returns({})
|
request.expects(:parameters).at_least_once.returns({})
|
||||||
assert request.xhr?
|
assert request.xhr?
|
||||||
assert_equal Mime::JS, request.format
|
assert_equal Mime::JS, request.format
|
||||||
ensure
|
|
||||||
ActionController::Base.use_accept_header = old
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -396,10 +391,54 @@ class RequestTest < ActiveSupport::TestCase
|
||||||
assert_equal({"bar" => 2}, request.query_parameters)
|
assert_equal({"bar" => 2}, request.query_parameters)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "formats with accept header" do
|
||||||
|
with_accept_header true do
|
||||||
|
request = stub_request 'HTTP_ACCEPT' => 'text/html'
|
||||||
|
request.expects(:parameters).at_least_once.returns({})
|
||||||
|
assert_equal [ Mime::HTML ], request.formats
|
||||||
|
|
||||||
|
request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8'
|
||||||
|
request.expects(:parameters).at_least_once.returns({})
|
||||||
|
assert_equal with_set(Mime::XML, Mime::HTML), request.formats
|
||||||
|
end
|
||||||
|
|
||||||
|
with_accept_header false do
|
||||||
|
request = stub_request
|
||||||
|
request.expects(:parameters).at_least_once.returns({ :format => :txt })
|
||||||
|
assert_equal with_set(Mime::TEXT), request.formats
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "negotiate_mime" do
|
||||||
|
with_accept_header true do
|
||||||
|
request = stub_request 'HTTP_ACCEPT' => 'text/html'
|
||||||
|
request.expects(:parameters).at_least_once.returns({})
|
||||||
|
|
||||||
|
assert_equal nil, request.negotiate_mime([Mime::XML, Mime::JSON])
|
||||||
|
assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::HTML])
|
||||||
|
assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::ALL])
|
||||||
|
|
||||||
|
request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8'
|
||||||
|
request.expects(:parameters).at_least_once.returns({})
|
||||||
|
assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV])
|
||||||
|
assert_equal Mime::CSV, request.negotiate_mime([Mime::CSV, Mime::YAML])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def stub_request(env={})
|
def stub_request(env={})
|
||||||
ActionDispatch::Request.new(env)
|
ActionDispatch::Request.new(env)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def with_set(*args)
|
||||||
|
args + Mime::SET
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_accept_header(value)
|
||||||
|
ActionController::Base.use_accept_header, old = value, ActionController::Base.use_accept_header
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
ActionController::Base.use_accept_header = old
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
1
actionpack/test/fixtures/respond_with/using_defaults.html.erb
vendored
Normal file
1
actionpack/test/fixtures/respond_with/using_defaults.html.erb
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Hello world!
|
1
actionpack/test/fixtures/respond_with/using_defaults.js.rjs
vendored
Normal file
1
actionpack/test/fixtures/respond_with/using_defaults.js.rjs
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
page[:body].visual_effect :highlight
|
1
actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs
vendored
Normal file
1
actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
page[:body].visual_effect :highlight
|
1
actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder
vendored
Normal file
1
actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
xml.p "Hello world!"
|
1
actionpack/test/fixtures/respond_with/using_resource.html.erb
vendored
Normal file
1
actionpack/test/fixtures/respond_with/using_resource.html.erb
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Hello world!
|
|
@ -3,11 +3,14 @@ require 'abstract_unit'
|
||||||
class ActiveRecordHelperI18nTest < Test::Unit::TestCase
|
class ActiveRecordHelperI18nTest < Test::Unit::TestCase
|
||||||
include ActionView::Context
|
include ActionView::Context
|
||||||
include ActionView::Helpers::ActiveModelHelper
|
include ActionView::Helpers::ActiveModelHelper
|
||||||
|
|
||||||
attr_reader :request
|
attr_reader :request
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@object = stub :errors => stub(:count => 1, :full_messages => ['full_messages'])
|
@object = stub :errors => stub(:count => 1, :full_messages => ['full_messages'])
|
||||||
|
@object.stubs :to_model => @object
|
||||||
|
@object.stubs :class => stub(:model_name => stub(:human => ""))
|
||||||
|
|
||||||
@object_name = 'book_seller'
|
@object_name = 'book_seller'
|
||||||
@object_name_without_underscore = 'book seller'
|
@object_name_without_underscore = 'book seller'
|
||||||
|
|
||||||
|
@ -39,7 +42,7 @@ class ActiveRecordHelperI18nTest < Test::Unit::TestCase
|
||||||
I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns ''
|
I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns ''
|
||||||
error_messages_for(:object => @object, :locale => 'en')
|
error_messages_for(:object => @object, :locale => 'en')
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_error_messages_for_given_object_name_it_translates_object_name
|
def test_error_messages_for_given_object_name_it_translates_object_name
|
||||||
I18n.expects(:t).with(:header, :locale => 'en', :scope => [:activerecord, :errors, :template], :count => 1, :model => @object_name_without_underscore).returns "1 error prohibited this #{@object_name_without_underscore} from being saved"
|
I18n.expects(:t).with(:header, :locale => 'en', :scope => [:activerecord, :errors, :template], :count => 1, :model => @object_name_without_underscore).returns "1 error prohibited this #{@object_name_without_underscore} from being saved"
|
||||||
I18n.expects(:t).with(@object_name, :default => @object_name_without_underscore, :count => 1, :scope => [:activerecord, :models]).once.returns @object_name_without_underscore
|
I18n.expects(:t).with(@object_name, :default => @object_name_without_underscore, :count => 1, :scope => [:activerecord, :models]).once.returns @object_name_without_underscore
|
||||||
|
|
|
@ -295,6 +295,16 @@ class ActiveRecordHelperTest < ActionView::TestCase
|
||||||
assert_equal '', error_messages_for('user', :object => nil)
|
assert_equal '', error_messages_for('user', :object => nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_error_messages_for_model_objects
|
||||||
|
error = error_messages_for(@post)
|
||||||
|
assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>),
|
||||||
|
error
|
||||||
|
|
||||||
|
error = error_messages_for(@user, @post)
|
||||||
|
assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this user from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>),
|
||||||
|
error
|
||||||
|
end
|
||||||
|
|
||||||
def test_form_with_string_multipart
|
def test_form_with_string_multipart
|
||||||
assert_dom_equal(
|
assert_dom_equal(
|
||||||
%(<form action="create" enctype="multipart/form-data" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
|
%(<form action="create" enctype="multipart/form-data" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
|
||||||
|
|
|
@ -2,7 +2,7 @@ require 'active_support/inflector'
|
||||||
|
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
class Name < String
|
class Name < String
|
||||||
attr_reader :singular, :plural, :element, :collection, :partial_path
|
attr_reader :singular, :plural, :element, :collection, :partial_path, :human
|
||||||
alias_method :cache_key, :collection
|
alias_method :cache_key, :collection
|
||||||
|
|
||||||
def initialize(name)
|
def initialize(name)
|
||||||
|
@ -10,6 +10,7 @@ module ActiveModel
|
||||||
@singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
|
@singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
|
||||||
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
|
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
|
||||||
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
|
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
|
||||||
|
@human = @element.gsub(/_/, " ")
|
||||||
@collection = ActiveSupport::Inflector.tableize(self).freeze
|
@collection = ActiveSupport::Inflector.tableize(self).freeze
|
||||||
@partial_path = "#{@collection}/#{@element}".freeze
|
@partial_path = "#{@collection}/#{@element}".freeze
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue