aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/action_controller.rb7
-rw-r--r--actionpack/lib/action_controller/base.rb68
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb27
-rw-r--r--actionpack/lib/action_controller/dispatcher.rb7
-rw-r--r--actionpack/lib/action_controller/http_authentication.rb72
-rw-r--r--actionpack/lib/action_controller/integration.rb12
-rw-r--r--actionpack/lib/action_controller/layout.rb25
-rw-r--r--actionpack/lib/action_controller/polymorphic_routes.rb7
-rwxr-xr-xactionpack/lib/action_controller/request.rb3
-rw-r--r--actionpack/lib/action_controller/resources.rb15
-rw-r--r--actionpack/lib/action_controller/response.rb41
-rw-r--r--actionpack/lib/action_controller/routing.rb2
-rw-r--r--actionpack/lib/action_controller/routing/builder.rb3
-rw-r--r--actionpack/lib/action_controller/routing/recognition_optimisation.rb1
-rw-r--r--actionpack/lib/action_controller/routing/segments.rb14
-rw-r--r--actionpack/lib/action_controller/test_process.rb4
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb22
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb2
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack.rb4
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb4
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb2
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb2
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb6
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb49
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb8
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb23
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb4
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb6
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb2
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb4
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb3
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb7
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb4
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb4
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb3
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb10
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb58
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb2
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb2
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb6
-rw-r--r--actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb15
-rw-r--r--actionpack/lib/action_pack/version.rb2
-rw-r--r--actionpack/lib/action_view/base.rb30
-rw-r--r--actionpack/lib/action_view/helpers/active_record_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb167
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb11
-rw-r--r--actionpack/lib/action_view/paths.rb8
-rw-r--r--actionpack/lib/action_view/renderable.rb26
-rw-r--r--actionpack/lib/action_view/template.rb25
54 files changed, 558 insertions, 299 deletions
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index ca826e7bfc..d03f4cb231 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -31,7 +31,12 @@ rescue LoadError
end
end
-require 'action_controller/vendor/rack-1.0/rack'
+begin
+ gem 'rack', '~> 1.0.0'
+ require 'rack'
+rescue Gem::LoadError
+ require 'action_controller/vendor/rack-1.0/rack'
+end
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 4fc52c1470..6348961ff4 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -22,7 +22,7 @@ module ActionController #:nodoc:
attr_reader :allowed_methods
def initialize(*allowed_methods)
- super("Only #{allowed_methods.to_sentence} requests are allowed.")
+ super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
@allowed_methods = allowed_methods
end
@@ -408,7 +408,7 @@ module ActionController #:nodoc:
# Return an array containing the names of public methods that have been marked hidden from the action processor.
# By default, all methods defined in ActionController::Base and included modules are hidden.
- # More methods can be hidden using <tt>hide_actions</tt>.
+ # More methods can be hidden using <tt>hide_action</tt>.
def hidden_actions
read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, [])
end
@@ -908,12 +908,14 @@ module ActionController #:nodoc:
end
options = extra_options
+ elsif !options.is_a?(Hash)
+ extra_options[:partial] = options
+ options = extra_options
end
layout = pick_layout(options)
response.layout = layout.path_without_format_and_extension if layout
logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout
- layout = layout.path_without_format_and_extension if layout
if content_type = options[:content_type]
response.content_type = content_type.to_s
@@ -982,6 +984,7 @@ module ActionController #:nodoc:
# of sending it as the response body to the browser.
def render_to_string(options = nil, &block) #:doc:
render(options, &block)
+ response.body
ensure
response.content_type = nil
erase_render_results
@@ -1018,7 +1021,7 @@ module ActionController #:nodoc:
# Clears the rendered results, allowing for another render to be performed.
def erase_render_results #:nodoc:
- response.body = nil
+ response.body = []
@performed_render = false
end
@@ -1101,7 +1104,6 @@ module ActionController #:nodoc:
end
response.redirected_to = options
- logger.info("Redirected to #{options}") if logger && logger.info?
case options
# The scheme name consist of a letter followed by any combination of
@@ -1124,6 +1126,7 @@ module ActionController #:nodoc:
def redirect_to_full_url(url, status)
raise DoubleRenderError if performed?
+ logger.info("Redirected to #{url}") if logger && logger.info?
response.redirect(url, interpret_status(status))
@performed_redirect = true
end
@@ -1133,6 +1136,11 @@ module ActionController #:nodoc:
# request is considered stale and should be generated from scratch. Otherwise,
# it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent.
#
+ # Parameters:
+ # * <tt>:etag</tt>
+ # * <tt>:last_modified</tt>
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
+ #
# Example:
#
# def show
@@ -1153,20 +1161,34 @@ module ActionController #:nodoc:
# Sets the etag, last_modified, or both on the response and renders a
# "304 Not Modified" response if the request is already fresh.
#
+ # Parameters:
+ # * <tt>:etag</tt>
+ # * <tt>:last_modified</tt>
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
+ #
# Example:
#
# def show
# @article = Article.find(params[:id])
- # fresh_when(:etag => @article, :last_modified => @article.created_at.utc)
+ # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true)
# end
#
# This will render the show template if the request isn't sending a matching etag or
# If-Modified-Since header and just a "304 Not Modified" response if there's a match.
+ #
def fresh_when(options)
- options.assert_valid_keys(:etag, :last_modified)
+ options.assert_valid_keys(:etag, :last_modified, :public)
response.etag = options[:etag] if options[:etag]
response.last_modified = options[:last_modified] if options[:last_modified]
+
+ if options[:public]
+ cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip }
+ cache_control.delete("private")
+ cache_control.delete("no-cache")
+ cache_control << "public"
+ response.headers["Cache-Control"] = cache_control.join(', ')
+ end
if request.fresh?(response)
head :not_modified
@@ -1178,15 +1200,26 @@ module ActionController #:nodoc:
#
# Examples:
# expires_in 20.minutes
- # expires_in 3.hours, :private => false
- # expires in 3.hours, 'max-stale' => 5.hours, :private => nil, :public => true
+ # expires_in 3.hours, :public => true
+ # expires in 3.hours, 'max-stale' => 5.hours, :public => true
#
# This method will overwrite an existing Cache-Control header.
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
def expires_in(seconds, options = {}) #:doc:
- cache_options = { 'max-age' => seconds, 'private' => true }.symbolize_keys.merge!(options.symbolize_keys)
- cache_options.delete_if { |k,v| v.nil? or v == false }
- cache_control = cache_options.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
+ cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip }
+
+ cache_control << "max-age=#{seconds}"
+ cache_control.delete("no-cache")
+ if options[:public]
+ cache_control.delete("private")
+ cache_control << "public"
+ else
+ cache_control << "private"
+ end
+
+ # This allows for additional headers to be passed through like 'max-stale' => 5.hours
+ cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
+
response.headers["Cache-Control"] = cache_control.join(', ')
end
@@ -1215,13 +1248,12 @@ module ActionController #:nodoc:
response.status = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
if append_response
- response.body ||= ''
- response.body << text.to_s
+ response.body_parts << text.to_s
else
response.body = case text
- when Proc then text
- when nil then " " # Safari doesn't pass the headers of the return if the response is zero length
- else text.to_s
+ when Proc then text
+ when nil then [" "] # Safari doesn't pass the headers of the return if the response is zero length
+ else [text.to_s]
end
end
end
@@ -1298,7 +1330,7 @@ module ActionController #:nodoc:
rescue ActionView::MissingTemplate => e
# Was the implicit template missing, or was it another template?
if e.path == default_template_name
- raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
+ raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence(:locale => :en)}", caller
else
raise e
end
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index 34e1c3527f..87b5029e57 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -129,24 +129,23 @@ module ActionController #:nodoc:
attr_reader :path, :extension
class << self
- def path_for(controller, options, infer_extension=true)
+ def path_for(controller, options, infer_extension = true)
new(controller, options, infer_extension).path
end
end
# When true, infer_extension will look up the cache path extension from the request's path & format.
- # This is desirable when reading and writing the cache, but not when expiring the cache - expire_action should expire the same files regardless of the request format.
- def initialize(controller, options = {}, infer_extension=true)
- if infer_extension and options.is_a? Hash
- request_extension = extract_extension(controller.request)
- options = options.reverse_merge(:format => request_extension)
+ # This is desirable when reading and writing the cache, but not when expiring the cache -
+ # expire_action should expire the same files regardless of the request format.
+ def initialize(controller, options = {}, infer_extension = true)
+ if infer_extension
+ extract_extension(controller.request)
+ options = options.reverse_merge(:format => @extension) if options.is_a?(Hash)
end
+
path = controller.url_for(options).split('://').last
normalize!(path)
- if infer_extension
- @extension = request_extension
- add_extension!(path, @extension)
- end
+ add_extension!(path, @extension)
@path = URI.unescape(path)
end
@@ -162,13 +161,7 @@ module ActionController #:nodoc:
def extract_extension(request)
# Don't want just what comes after the last '.' to accommodate multi part extensions
# such as tar.gz.
- extension = request.path[/^[^.]+\.(.+)$/, 1]
-
- # If there's no extension in the path, check request.format
- if extension.nil?
- extension = request.cache_format
- end
- extension
+ @extension = request.path[/^[^.]+\.(.+)$/, 1] || request.cache_format
end
end
end
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index ec40b5c4e6..07931e4a4a 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -13,7 +13,6 @@ module ActionController
end
if defined?(ActiveRecord)
- after_dispatch :checkin_connections
to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
end
@@ -115,11 +114,5 @@ module ActionController
def flush_logger
Base.logger.flush
end
-
- def checkin_connections
- # Don't return connection (and peform implicit rollback) if this request is a part of integration test
- return if @env.key?("rack.test")
- ActiveRecord::Base.clear_active_connections!
- end
end
end
diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb
index 2ccbc22420..b6b5267c66 100644
--- a/actionpack/lib/action_controller/http_authentication.rb
+++ b/actionpack/lib/action_controller/http_authentication.rb
@@ -68,8 +68,11 @@ module ActionController
#
# Simple Digest example:
#
+ # require 'digest/md5'
# class PostsController < ApplicationController
- # USERS = {"dhh" => "secret"}
+ # REALM = "SuperSecret"
+ # USERS = {"dhh" => "secret", #plain text password
+ # "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password
#
# before_filter :authenticate, :except => [:index]
#
@@ -83,14 +86,18 @@ module ActionController
#
# private
# def authenticate
- # authenticate_or_request_with_http_digest(realm) do |username|
+ # authenticate_or_request_with_http_digest(REALM) do |username|
# USERS[username]
# end
# end
# end
#
- # NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password so the framework can appropriately
- # hash it to check the user's credentials. Returning +nil+ will cause authentication to fail.
+ # NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately
+ # hash to check the user's credentials. Returning +nil+ will cause authentication to fail.
+ # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
+ # the password file or database is compromised, the attacker would be able to use the ha1 hash to
+ # authenticate as the user at this +realm+, but would not have the user's password to try using at
+ # other sites.
#
# On shared hosts, Apache sometimes doesn't pass authentication headers to
# FCGI instances. If your environment matches this description and you cannot
@@ -177,26 +184,37 @@ module ActionController
end
# Raises error unless the request credentials response value matches the expected value.
+ # First try the password as a ha1 digest password. If this fails, then try it as a plain
+ # text password.
def validate_digest_response(request, realm, &password_procedure)
credentials = decode_credentials_header(request)
valid_nonce = validate_nonce(request, credentials[:nonce])
- if valid_nonce && realm == credentials[:realm] && opaque(request.session.session_id) == credentials[:opaque]
+ if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque]
password = password_procedure.call(credentials[:username])
- expected = expected_response(request.env['REQUEST_METHOD'], credentials[:uri], credentials, password)
- expected == credentials[:response]
+
+ [true, false].any? do |password_is_ha1|
+ expected = expected_response(request.env['REQUEST_METHOD'], request.env['REQUEST_URI'], credentials, password, password_is_ha1)
+ expected == credentials[:response]
+ end
end
end
# Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
- def expected_response(http_method, uri, credentials, password)
- ha1 = ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
+ # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
+ # of a plain-text password.
+ def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
+ ha1 = password_is_ha1 ? password : ha1(credentials, password)
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':'))
end
- def encode_credentials(http_method, credentials, password)
- credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password)
+ def ha1(credentials, password)
+ ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
+ end
+
+ def encode_credentials(http_method, credentials, password, password_is_ha1)
+ credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
"Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ')
end
@@ -213,8 +231,7 @@ module ActionController
end
def authentication_header(controller, realm)
- session_id = controller.request.session.session_id
- controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce(session_id)}", opaque="#{opaque(session_id)}")
+ controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
end
def authentication_request(controller, realm, message = nil)
@@ -252,23 +269,36 @@ module ActionController
# POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
# of this document.
#
- # The nonce is opaque to the client.
- def nonce(session_id, time = Time.now)
+ # The nonce is opaque to the client. Composed of Time, and hash of Time with secret
+ # key from the Rails session secret generated upon creation of project. Ensures
+ # the time cannot be modifed by client.
+ def nonce(time = Time.now)
t = time.to_i
- hashed = [t, session_id]
+ hashed = [t, secret_key]
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
Base64.encode64("#{t}:#{digest}").gsub("\n", '')
end
- def validate_nonce(request, value)
+ # Might want a shorter timeout depending on whether the request
+ # is a PUT or POST, and if client is browser or web service.
+ # Can be much shorter if the Stale directive is implemented. This would
+ # allow a user to use new nonce without prompting user again for their
+ # username and password.
+ def validate_nonce(request, value, seconds_to_timeout=5*60)
t = Base64.decode64(value).split(":").first.to_i
- nonce(request.session.session_id, t) == value && (t - Time.now.to_i).abs <= 10 * 60
+ nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
end
- # Opaque based on digest of session_id
- def opaque(session_id)
- Base64.encode64(::Digest::MD5::hexdigest(session_id)).gsub("\n", '')
+ # Opaque based on random generation - but changing each request?
+ def opaque()
+ ::Digest::MD5.hexdigest(secret_key)
end
+
+ # Set in /initializers/session_store.rb, and loaded even if sessions are not in use.
+ def secret_key
+ ActionController::Base.session_options[:secret]
+ end
+
end
end
end
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index 1c05ab0bf6..fda6b639d1 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -5,7 +5,7 @@ require 'active_support/test_case'
module ActionController
module Integration #:nodoc:
# An integration Session instance represents a set of requests and responses
- # performed sequentially by some virtual user. Becase you can instantiate
+ # performed sequentially by some virtual user. Because you can instantiate
# multiple sessions and run them side-by-side, you can also mimic (to some
# limited extent) multiple simultaneous users interacting with your system.
#
@@ -332,11 +332,13 @@ module ActionController
@cookies[name] = value
end
- @body = ""
if body.is_a?(String)
- @body << body
+ @body_parts = [body]
+ @body = body
else
- body.each { |part| @body << part }
+ @body_parts = []
+ body.each { |part| @body_parts << part.to_s }
+ @body = @body_parts.join
end
if @controller = ActionController::Base.last_instantiation
@@ -349,7 +351,7 @@ module ActionController
@response = Response.new
@response.status = status.to_s
@response.headers.replace(@headers)
- @response.body = @body
+ @response.body = @body_parts
end
# Decorate the response with the standard behavior of the
diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb
index a0db7acf72..6ec0c1b304 100644
--- a/actionpack/lib/action_controller/layout.rb
+++ b/actionpack/lib/action_controller/layout.rb
@@ -198,7 +198,7 @@ module ActionController #:nodoc:
# is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
# object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
# weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
- def active_layout(passed_layout = nil)
+ def active_layout(passed_layout = nil, options = {})
layout = passed_layout || default_layout
return layout if layout.respond_to?(:render)
@@ -207,21 +207,23 @@ module ActionController #:nodoc:
when Proc then layout.call(self)
else layout
end
-
- find_layout(active_layout, @template.template_format) if active_layout
+
+ find_layout(active_layout, default_template_format, options[:html_fallback]) if active_layout
end
private
def default_layout #:nodoc:
- layout = self.class.read_inheritable_attribute(:layout) unless default_template_format == :js
+ layout = self.class.read_inheritable_attribute(:layout)
return layout unless self.class.read_inheritable_attribute(:auto_layout)
find_layout(layout, default_template_format)
rescue ActionView::MissingTemplate
nil
end
- def find_layout(layout, *formats) #:nodoc:
- view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", *formats)
+ def find_layout(layout, format, html_fallback=false) #:nodoc:
+ view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", format, html_fallback)
+ rescue ActionView::MissingTemplate
+ raise if Mime::Type.lookup_by_extension(format.to_s).html?
end
def pick_layout(options)
@@ -232,7 +234,7 @@ module ActionController #:nodoc:
when NilClass, TrueClass
active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name)
else
- active_layout(layout)
+ active_layout(layout, :html_fallback => true)
end
else
active_layout if action_has_layout? && candidate_for_layout?(options)
@@ -258,7 +260,12 @@ module ActionController #:nodoc:
template = options[:template] || default_template(options[:action])
if options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty?
begin
- !self.view_paths.find_template(template, default_template_format).exempt_from_layout?
+ template_object = self.view_paths.find_template(template, default_template_format)
+ # this restores the behavior from 2.2.2, where response.template.template_format was reset
+ # to :html for :js requests with a matching html template.
+ # see v2.2.2, ActionView::Base, lines 328-330
+ @real_format = :html if response.template.template_format == :js && template_object.format == "html"
+ !template_object.exempt_from_layout?
rescue ActionView::MissingTemplate
true
end
@@ -268,7 +275,7 @@ module ActionController #:nodoc:
end
def default_template_format
- response.template.template_format
+ @real_format || response.template.template_format
end
end
end
diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb
index 924d1aa6bd..d9b614c237 100644
--- a/actionpack/lib/action_controller/polymorphic_routes.rb
+++ b/actionpack/lib/action_controller/polymorphic_routes.rb
@@ -163,7 +163,8 @@ module ActionController
if parent.is_a?(Symbol) || parent.is_a?(String)
string << "#{parent}_"
else
- string << "#{RecordIdentifier.__send__("singular_class_name", parent)}_"
+ string << "#{RecordIdentifier.__send__("plural_class_name", parent)}".singularize
+ string << "_"
end
end
end
@@ -171,7 +172,9 @@ module ActionController
if record.is_a?(Symbol) || record.is_a?(String)
route << "#{record}_"
else
- route << "#{RecordIdentifier.__send__("#{inflection}_class_name", record)}_"
+ route << "#{RecordIdentifier.__send__("plural_class_name", record)}"
+ route = route.singularize if inflection == :singular
+ route << "_"
end
action_prefix(options) + namespace + route + routing_type(options).to_s
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index 0e95cfc147..ef223f157c 100755
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -32,7 +32,7 @@ module ActionController
# <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS
# constant above, an UnknownHttpMethod exception is raised.
def request_method
- @request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}")
+ @request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
end
# Returns the HTTP request \method used for action processing as a
@@ -442,6 +442,7 @@ EOM
end
def reset_session
+ @env['rack.session.options'].delete(:id)
@env['rack.session'] = {}
end
diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb
index 3af21967df..86abb7b2f4 100644
--- a/actionpack/lib/action_controller/resources.rb
+++ b/actionpack/lib/action_controller/resources.rb
@@ -91,7 +91,7 @@ module ActionController
end
def shallow_path_prefix
- @shallow_path_prefix ||= "#{path_prefix unless @options[:shallow]}"
+ @shallow_path_prefix ||= @options[:shallow] ? @options[:namespace].try(:sub, /\/$/, '') : path_prefix
end
def member_path
@@ -103,7 +103,7 @@ module ActionController
end
def shallow_name_prefix
- @shallow_name_prefix ||= "#{name_prefix unless @options[:shallow]}"
+ @shallow_name_prefix ||= @options[:shallow] ? @options[:namespace].try(:gsub, /\//, '_') : name_prefix
end
def nesting_name_prefix
@@ -630,7 +630,7 @@ module ActionController
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
action_path ||= Base.resources_path_names[action] || action
- map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m)
+ map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true })
end
end
end
@@ -641,9 +641,9 @@ module ActionController
map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
end
- def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil)
+ def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} )
if resource.has_action?(action)
- action_options = action_options_for(action, resource, method)
+ action_options = action_options_for(action, resource, method, resource_options)
formatted_route_path = "#{route_path}.:format"
if route_name && @set.named_routes[route_name.to_sym].nil?
@@ -660,9 +660,10 @@ module ActionController
end
end
- def action_options_for(action, resource, method = nil)
+ def action_options_for(action, resource, method = nil, resource_options = {})
default_options = { :action => action.to_s }
require_id = !resource.kind_of?(SingletonResource)
+ force_id = resource_options[:force_id] && !resource.kind_of?(SingletonResource)
case default_options[:action]
when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
@@ -670,7 +671,7 @@ module ActionController
when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
- else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements)
+ else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(force_id))
end
end
end
diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb
index ccff473df0..febe4ccf29 100644
--- a/actionpack/lib/action_controller/response.rb
+++ b/actionpack/lib/action_controller/response.rb
@@ -40,14 +40,28 @@ module ActionController # :nodoc:
delegate :default_charset, :to => 'ActionController::Base'
def initialize
- @status = 200
+ super
@header = Rack::Utils::HeaderHash.new(DEFAULT_HEADERS)
+ @session, @assigns = [], []
+ end
- @writer = lambda { |x| @body << x }
- @block = nil
+ def body
+ str = ''
+ each { |part| str << part.to_s }
+ str
+ end
- @body = "",
- @session, @assigns = [], []
+ def body=(body)
+ @body =
+ if body.is_a?(String)
+ [body]
+ else
+ body
+ end
+ end
+
+ def body_parts
+ @body
end
def location; headers['Location'] end
@@ -152,7 +166,7 @@ module ActionController # :nodoc:
@writer = lambda { |x| callback.call(x) }
@body.call(self, self)
elsif @body.is_a?(String)
- @body.each_line(&callback)
+ callback.call(@body)
else
@body.each(&callback)
end
@@ -162,7 +176,8 @@ module ActionController # :nodoc:
end
def write(str)
- @writer.call str.to_s
+ str = str.to_s
+ @writer.call str
str
end
@@ -186,7 +201,7 @@ module ActionController # :nodoc:
if request && request.etag_matches?(etag)
self.status = '304 Not Modified'
- self.body = ''
+ self.body = []
end
set_conditional_cache_control!
@@ -195,7 +210,11 @@ module ActionController # :nodoc:
def nonempty_ok_response?
ok = !status || status.to_s[0..2] == '200'
- ok && body.is_a?(String) && !body.empty?
+ ok && string_body?
+ end
+
+ def string_body?
+ !body_parts.respond_to?(:call) && body_parts.any? && body_parts.all? { |part| part.is_a?(String) }
end
def set_conditional_cache_control!
@@ -216,8 +235,8 @@ module ActionController # :nodoc:
headers.delete('Content-Length')
elsif length = headers['Content-Length']
headers['Content-Length'] = length.to_s
- elsif !body.respond_to?(:call) && (!status || status.to_s[0..2] != '304')
- headers["Content-Length"] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
+ elsif string_body? && (!status || status.to_s[0..2] != '304')
+ headers["Content-Length"] = Rack::Utils.bytesize(body).to_s
end
end
diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb
index a2141a77dc..c0eb61340b 100644
--- a/actionpack/lib/action_controller/routing.rb
+++ b/actionpack/lib/action_controller/routing.rb
@@ -267,7 +267,7 @@ module ActionController
module Routing
SEPARATORS = %w( / . ? )
- HTTP_METHODS = [:get, :head, :post, :put, :delete]
+ HTTP_METHODS = [:get, :head, :post, :put, :delete, :options]
ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set
diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb
index 44d759444a..d9590c88b8 100644
--- a/actionpack/lib/action_controller/routing/builder.rb
+++ b/actionpack/lib/action_controller/routing/builder.rb
@@ -159,7 +159,8 @@ module ActionController
path = "/#{path}" unless path[0] == ?/
path = "#{path}/" unless path[-1] == ?/
- path = "/#{options[:path_prefix].to_s.gsub(/^\//,'')}#{path}" if options[:path_prefix]
+ prefix = options[:path_prefix].to_s.gsub(/^\//,'')
+ path = "/#{prefix}#{path}" unless prefix.blank?
segments = segments_for_route_path(path)
defaults, requirements, conditions = divide_route_options(segments, options)
diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb
index ebc553512f..9bfebff0c0 100644
--- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb
+++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb
@@ -98,7 +98,6 @@ module ActionController
if Array === item
i += 1
start = (i == 1)
- final = (i == list.size)
tag, sub = item
if tag == :dynamic
body += padding + "#{start ? 'if' : 'elsif'} true\n"
diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb
index 129e87c139..4f936d51d2 100644
--- a/actionpack/lib/action_controller/routing/segments.rb
+++ b/actionpack/lib/action_controller/routing/segments.rb
@@ -3,7 +3,11 @@ module ActionController
class Segment #:nodoc:
RESERVED_PCHAR = ':@&=+$,;'
SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
- UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
+ if RUBY_VERSION >= '1.9'
+ UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze
+ else
+ UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
+ end
# TODO: Convert :is_optional accessor to read only
attr_accessor :is_optional
@@ -314,13 +318,17 @@ module ActionController
end
def regexp_chunk
- '(\.[^/?\.]+)?'
+ '/|(\.[^/?\.]+)?'
end
def to_s
'(.:format)?'
end
-
+
+ def extract_value
+ "#{local_name} = options[:#{key}] && options[:#{key}].to_s.downcase"
+ end
+
#the value should not include the period (.)
def match_extraction(next_capture)
%[
diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb
index dbaec00bee..9dd09c30b4 100644
--- a/actionpack/lib/action_controller/test_process.rb
+++ b/actionpack/lib/action_controller/test_process.rb
@@ -258,11 +258,11 @@ module ActionController #:nodoc:
# Returns binary content (downloadable file), converted to a String
def binary_content
- raise "Response body is not a Proc: #{body.inspect}" unless body.kind_of?(Proc)
+ raise "Response body is not a Proc: #{body_parts.inspect}" unless body_parts.kind_of?(Proc)
require 'stringio'
sio = StringIO.new
- body.call(self, sio)
+ body_parts.call(self, sio)
sio.rewind
sio.read
diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb
index bb6cb437b7..16720b915b 100644
--- a/actionpack/lib/action_controller/url_rewriter.rb
+++ b/actionpack/lib/action_controller/url_rewriter.rb
@@ -68,29 +68,17 @@ module ActionController
# This generates, among other things, the method <tt>users_path</tt>. By default,
# this method is accessible from your controllers, views and mailers. If you need
# to access this auto-generated method from other places (such as a model), then
- # you can do that in two ways.
- #
- # The first way is to include ActionController::UrlWriter in your class:
+ # you can do that by including ActionController::UrlWriter in your class:
#
# class User < ActiveRecord::Base
- # include ActionController::UrlWriter # !!!
+ # include ActionController::UrlWriter
#
- # def name=(value)
- # write_attribute('name', value)
- # write_attribute('base_uri', users_path) # !!!
+ # def base_uri
+ # user_path(self)
# end
# end
#
- # The second way is to access them through ActionController::UrlWriter.
- # The autogenerated named routes methods are available as class methods:
- #
- # class User < ActiveRecord::Base
- # def name=(value)
- # write_attribute('name', value)
- # path = ActionController::UrlWriter.users_path # !!!
- # write_attribute('base_uri', path) # !!!
- # end
- # end
+ # User.find(1).base_uri # => "/users/1"
module UrlWriter
def self.included(base) #:nodoc:
ActionController::Routing::Routes.install_helpers(base)
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
index 376bb87409..e2c49c284f 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
@@ -556,7 +556,7 @@ module HTML
end
# Attribute value.
- next if statement.sub!(/^\[\s*([[:alpha:]][\w\-]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match|
+ next if statement.sub!(/^\[\s*([[:alpha:]][\w\-:]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match|
name, equality, value = $1, $2, $3
if value == "?"
value = values.shift
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb
index 6c03b55552..6349b95094 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb
@@ -23,14 +23,16 @@ module Rack
# Return the Rack release as a dotted string.
def self.release
- "0.4"
+ "1.0 bundled"
end
autoload :Builder, "rack/builder"
autoload :Cascade, "rack/cascade"
+ autoload :Chunked, "rack/chunked"
autoload :CommonLogger, "rack/commonlogger"
autoload :ConditionalGet, "rack/conditionalget"
autoload :ContentLength, "rack/content_length"
+ autoload :ContentType, "rack/content_type"
autoload :File, "rack/file"
autoload :Deflater, "rack/deflater"
autoload :Directory, "rack/directory"
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb
index 8489c9b9c4..214df6299e 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb
@@ -8,8 +8,8 @@ module Rack
attr_accessor :realm
- def initialize(app, &authenticator)
- @app, @authenticator = app, authenticator
+ def initialize(app, realm=nil, &authenticator)
+ @app, @realm, @authenticator = app, realm, authenticator
end
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb
index 6d2bd29c2e..e579dc9632 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb
@@ -21,7 +21,7 @@ module Rack
attr_writer :passwords_hashed
- def initialize(app)
+ def initialize(*args)
super
@passwords_hashed = nil
end
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb
index a40f57b7f1..a8aa3bf996 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb
@@ -8,7 +8,7 @@ module Rack
class Request < Auth::AbstractRequest
def method
- @env['REQUEST_METHOD']
+ @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD']
end
def digest?
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb
index 25994d5a44..295235e56a 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb
@@ -34,11 +34,7 @@ module Rack
end
def use(middleware, *args, &block)
- @ins << if block_given?
- lambda { |app| middleware.new(app, *args, &block) }
- else
- lambda { |app| middleware.new(app, *args) }
- end
+ @ins << lambda { |app| middleware.new(app, *args, &block) }
end
def run(app)
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb
new file mode 100644
index 0000000000..280d89dd65
--- /dev/null
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb
@@ -0,0 +1,49 @@
+require 'rack/utils'
+
+module Rack
+
+ # Middleware that applies chunked transfer encoding to response bodies
+ # when the response does not include a Content-Length header.
+ class Chunked
+ include Rack::Utils
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ status, headers, body = @app.call(env)
+ headers = HeaderHash.new(headers)
+
+ if env['HTTP_VERSION'] == 'HTTP/1.0' ||
+ STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
+ headers['Content-Length'] ||
+ headers['Transfer-Encoding']
+ [status, headers.to_hash, body]
+ else
+ dup.chunk(status, headers, body)
+ end
+ end
+
+ def chunk(status, headers, body)
+ @body = body
+ headers.delete('Content-Length')
+ headers['Transfer-Encoding'] = 'chunked'
+ [status, headers.to_hash, self]
+ end
+
+ def each
+ term = "\r\n"
+ @body.each do |chunk|
+ size = bytesize(chunk)
+ next if size == 0
+ yield [size.to_s(16), term, chunk, term].join
+ end
+ yield ["0", term, "", term].join
+ end
+
+ def close
+ @body.close if @body.respond_to?(:close)
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb
index bce22a32c5..1e56d43853 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb
@@ -3,21 +3,23 @@ require 'rack/utils'
module Rack
# Sets the Content-Length header on responses with fixed-length bodies.
class ContentLength
+ include Rack::Utils
+
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
- headers = Utils::HeaderHash.new(headers)
+ headers = HeaderHash.new(headers)
- if !Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
+ if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
!headers['Content-Length'] &&
!headers['Transfer-Encoding'] &&
(body.respond_to?(:to_ary) || body.respond_to?(:to_str))
body = [body] if body.respond_to?(:to_str) # rack 0.4 compat
- length = body.to_ary.inject(0) { |len, part| len + part.length }
+ length = body.to_ary.inject(0) { |len, part| len + bytesize(part) }
headers['Content-Length'] = length.to_s
end
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb
new file mode 100644
index 0000000000..0c1e1ca3e1
--- /dev/null
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb
@@ -0,0 +1,23 @@
+require 'rack/utils'
+
+module Rack
+
+ # Sets the Content-Type header on responses which don't have one.
+ #
+ # Builder Usage:
+ # use Rack::ContentType, "text/plain"
+ #
+ # When no content type argument is provided, "text/html" is assumed.
+ class ContentType
+ def initialize(app, content_type = "text/html")
+ @app, @content_type = app, content_type
+ end
+
+ def call(env)
+ status, headers, body = @app.call(env)
+ headers = Utils::HeaderHash.new(headers)
+ headers['Content-Type'] ||= @content_type
+ [status, headers.to_hash, body]
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb
index 3e66680092..a42b7477ae 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb
@@ -36,12 +36,12 @@ module Rack
mtime = headers.key?("Last-Modified") ?
Time.httpdate(headers["Last-Modified"]) : Time.now
body = self.class.gzip(body, mtime)
- size = body.respond_to?(:bytesize) ? body.bytesize : body.size
+ size = Rack::Utils.bytesize(body)
headers = headers.merge("Content-Encoding" => "gzip", "Content-Length" => size.to_s)
[status, headers, [body]]
when "deflate"
body = self.class.deflate(body)
- size = body.respond_to?(:bytesize) ? body.bytesize : body.size
+ size = Rack::Utils.bytesize(body)
headers = headers.merge("Content-Encoding" => "deflate", "Content-Length" => size.to_s)
[status, headers, [body]]
when "identity"
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb
index 56ee5e7b60..acdd3029d3 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb
@@ -70,7 +70,7 @@ table { width:100%%; }
return unless @path_info.include? ".."
body = "Forbidden\n"
- size = body.respond_to?(:bytesize) ? body.bytesize : body.size
+ size = Rack::Utils.bytesize(body)
return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]]
end
@@ -89,6 +89,8 @@ table { width:100%%; }
type = stat.directory? ? 'directory' : Mime.mime_type(ext)
size = stat.directory? ? '-' : filesize_format(size)
mtime = stat.mtime.httpdate
+ url << '/' if stat.directory?
+ basename << '/' if stat.directory?
@files << [ url, basename, size, type, mtime ]
end
@@ -120,7 +122,7 @@ table { width:100%%; }
def entity_not_found
body = "Entity not found: #{@path_info}\n"
- size = body.respond_to?(:bytesize) ? body.bytesize : body.size
+ size = Rack::Utils.bytesize(body)
return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]]
end
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb
index 7869227a36..fe62bd6b86 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb
@@ -60,7 +60,7 @@ module Rack
body = self
else
body = [F.read(@path)]
- size = body.first.size
+ size = Utils.bytesize(body.first)
end
[200, {
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb
index f2c976cf46..e38156c7f0 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb
@@ -1,3 +1,5 @@
+require 'rack/content_length'
+
module Rack
module Handler
class CGI
@@ -6,6 +8,8 @@ module Rack
end
def self.serve(app)
+ app = ContentLength.new(app)
+
env = ENV.to_hash
env.delete "HTTP_CONTENT_LENGTH"
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb
index f03e1615c9..6324c7d274 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb
@@ -1,5 +1,6 @@
require 'fcgi'
require 'socket'
+require 'rack/content_length'
module Rack
module Handler
@@ -29,6 +30,8 @@ module Rack
end
def self.serve(request, app)
+ app = Rack::ContentLength.new(app)
+
env = request.env
env.delete "HTTP_CONTENT_LENGTH"
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb
index 1f850fc77b..c65ba3ec8e 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb
@@ -1,5 +1,6 @@
require 'lsapi'
-#require 'cgi'
+require 'rack/content_length'
+
module Rack
module Handler
class LSWS
@@ -9,11 +10,13 @@ module Rack
end
end
def self.serve(app)
+ app = Rack::ContentLength.new(app)
+
env = ENV.to_hash
env.delete "HTTP_CONTENT_LENGTH"
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
env.update({"rack.version" => [0,1],
- "rack.input" => $stdin,
+ "rack.input" => StringIO.new($stdin.read.to_s),
"rack.errors" => $stderr,
"rack.multithread" => false,
"rack.multiprocess" => true,
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb
index 178a1a8fe4..f0c0d58330 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb
@@ -1,5 +1,7 @@
require 'mongrel'
require 'stringio'
+require 'rack/content_length'
+require 'rack/chunked'
module Rack
module Handler
@@ -33,7 +35,7 @@ module Rack
end
def initialize(app)
- @app = app
+ @app = Rack::Chunked.new(Rack::ContentLength.new(app))
end
def process(request, response)
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb
index fd18a8359b..9495c66374 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb
@@ -1,5 +1,7 @@
require 'scgi'
require 'stringio'
+require 'rack/content_length'
+require 'rack/chunked'
module Rack
module Handler
@@ -14,7 +16,7 @@ module Rack
end
def initialize(settings = {})
- @app = settings[:app]
+ @app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app]))
@log = Object.new
def @log.info(*args); end
def @log.error(*args); end
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb
index 7ad088b36a..3d4fedff75 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb
@@ -1,9 +1,12 @@
require "thin"
+require "rack/content_length"
+require "rack/chunked"
module Rack
module Handler
class Thin
def self.run(app, options={})
+ app = Rack::Chunked.new(Rack::ContentLength.new(app))
server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
options[:Port] || 8080,
app)
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb
index 40be79de13..829e7d6bf8 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb
@@ -1,5 +1,6 @@
require 'webrick'
require 'stringio'
+require 'rack/content_length'
module Rack
module Handler
@@ -14,7 +15,7 @@ module Rack
def initialize(server, app)
super server
- @app = app
+ @app = Rack::ContentLength.new(app)
end
def service(req, res)
@@ -35,7 +36,12 @@ module Rack
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
env["QUERY_STRING"] ||= ""
env["REQUEST_PATH"] ||= "/"
- env.delete "PATH_INFO" if env["PATH_INFO"] == ""
+ if env["PATH_INFO"] == ""
+ env.delete "PATH_INFO"
+ else
+ path, n = req.request_uri.path, env["SCRIPT_NAME"].length
+ env["PATH_INFO"] = path[n, path.length-n]
+ end
status, headers, body = @app.call(env)
begin
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb
index 7eb05437f0..44a33ce36e 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb
@@ -88,7 +88,9 @@ module Rack
## within the application. This may be an
## empty string, if the request URL targets
## the application root and does not have a
- ## trailing slash.
+ ## trailing slash. This information should be
+ ## decoded by the server if it comes from a
+ ## URL.
## <tt>QUERY_STRING</tt>:: The portion of the request URL that
## follows the <tt>?</tt>, if any. May be
@@ -372,59 +374,43 @@ module Rack
## === The Content-Length
def check_content_length(status, headers, env)
- chunked_response = false
- headers.each { |key, value|
- if key.downcase == 'transfer-encoding'
- chunked_response = value.downcase != 'identity'
- end
- }
-
headers.each { |key, value|
if key.downcase == 'content-length'
- ## There must be a <tt>Content-Length</tt>, except when the
- ## +Status+ is 1xx, 204 or 304, in which case there must be none
- ## given.
+ ## There must not be a <tt>Content-Length</tt> header when the
+ ## +Status+ is 1xx, 204 or 304.
assert("Content-Length header found in #{status} response, not allowed") {
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
- assert('Content-Length header should not be used if body is chunked') {
- not chunked_response
- }
-
bytes = 0
string_body = true
- @body.each { |part|
- unless part.kind_of?(String)
- string_body = false
- break
- end
+ if @body.respond_to?(:to_ary)
+ @body.each { |part|
+ unless part.kind_of?(String)
+ string_body = false
+ break
+ end
- bytes += (part.respond_to?(:bytesize) ? part.bytesize : part.size)
- }
-
- if env["REQUEST_METHOD"] == "HEAD"
- assert("Response body was given for HEAD request, but should be empty") {
- bytes == 0
+ bytes += Rack::Utils.bytesize(part)
}
- else
- if string_body
- assert("Content-Length header was #{value}, but should be #{bytes}") {
- value == bytes.to_s
+
+ if env["REQUEST_METHOD"] == "HEAD"
+ assert("Response body was given for HEAD request, but should be empty") {
+ bytes == 0
}
+ else
+ if string_body
+ assert("Content-Length header was #{value}, but should be #{bytes}") {
+ value == bytes.to_s
+ }
+ end
end
end
return
end
}
-
- if [ String, Array ].include?(@body.class) && !chunked_response
- assert('No Content-Length header found') {
- Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
- }
- end
end
## === The Body
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb
index a593110139..caf60d5b19 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb
@@ -16,6 +16,8 @@ module Rack
# Your application's +call+ should end returning Response#finish.
class Response
+ attr_accessor :length
+
def initialize(body=[], status=200, header={}, &block)
@status = status
@header = Utils::HeaderHash.new({"Content-Type" => "text/html"}.
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb
index 5f13404dce..28258c7c89 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb
@@ -27,7 +27,7 @@ module Rack
message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s
detail = env["rack.showstatus.detail"] || message
body = @template.result(binding)
- size = body.respond_to?(:bytesize) ? body.bytesize : body.size
+ size = Rack::Utils.bytesize(body)
[status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]]
else
[status, headers, body]
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb
index eb1457a850..0ff32df181 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb
@@ -12,7 +12,11 @@ module Rack
# first, since they are most specific.
class URLMap
- def initialize(map)
+ def initialize(map = {})
+ remap(map)
+ end
+
+ def remap(map)
@mapping = map.map { |location, app|
if location =~ %r{\Ahttps?://(.*?)(/.*)}
host, location = $1, $2
diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb
index f352cb6783..0a61bce707 100644
--- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb
+++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb
@@ -144,6 +144,19 @@ module Rack
end
module_function :select_best_encoding
+ # Return the bytesize of String; uses String#length under Ruby 1.8 and
+ # String#bytesize under 1.9.
+ if ''.respond_to?(:bytesize)
+ def bytesize(string)
+ string.bytesize
+ end
+ else
+ def bytesize(string)
+ string.size
+ end
+ end
+ module_function :bytesize
+
# Context allows the use of a compatible middleware at different points
# in a request handling stack. A compatible middleware must define
# #context which should take the arguments env and app. The first of which
@@ -359,7 +372,7 @@ module Rack
data = body
end
- Utils.normalize_params(params, name, data)
+ Utils.normalize_params(params, name, data) unless data.nil?
break if buf.empty? || content_length == -1
}
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index f20e44a7d5..e0aa2a5f2f 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
- TINY = 0
+ TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.')
end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 65b2062337..9c0134e7f7 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -183,12 +183,12 @@ module ActionView #:nodoc:
cattr_accessor :debug_rjs
# Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.
- # Automaticaly reloading templates are not thread safe and should only be used in development mode.
- @@cache_template_loading = false
+ # Automatically reloading templates are not thread safe and should only be used in development mode.
+ @@cache_template_loading = nil
cattr_accessor :cache_template_loading
def self.cache_template_loading?
- ActionController::Base.allow_concurrency || cache_template_loading
+ ActionController::Base.allow_concurrency || (cache_template_loading.nil? ? !ActiveSupport::Dependencies.load? : cache_template_loading)
end
attr_internal :request
@@ -221,10 +221,12 @@ module ActionView #:nodoc:
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
@assigns = assigns_for_first_render
@assigns_added = nil
- @_render_stack = []
@controller = controller
@helpers = ProxyModule.new(self)
self.view_paths = view_paths
+
+ @_first_render = nil
+ @_current_render = nil
end
attr_reader :view_paths
@@ -286,7 +288,25 @@ module ActionView #:nodoc:
# Access the current template being rendered.
# Returns a ActionView::Template object.
def template
- @_render_stack.last
+ @_current_render
+ end
+
+ def template=(template) #:nodoc:
+ @_first_render ||= template
+ @_current_render = template
+ end
+
+ def with_template(current_template)
+ last_template, self.template = template, current_template
+ yield
+ ensure
+ self.template = last_template
+ end
+
+ def punctuate_body!(part)
+ flush_output_buffer
+ response.body_parts << part
+ nil
end
private
diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb
index 8b56d241ae..541899ea6a 100644
--- a/actionpack/lib/action_view/helpers/active_record_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_record_helper.rb
@@ -121,7 +121,7 @@ module ActionView
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
(errors = obj.errors.on(method))
content_tag("div",
- "#{options[:prepend_text]}#{errors.is_a?(Array) ? errors.first : errors}#{options[:append_text]}",
+ "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.is_a?(Array) ? errors.first : errors)}#{options[:append_text]}",
:class => options[:css_class]
)
else
@@ -198,7 +198,7 @@ module ActionView
locale.t :header, :count => count, :model => object_name
end
message = options.include?(:message) ? options[:message] : locale.t(:body)
- error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join
+ error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, ERB::Util.html_escape(msg)) } }.join
contents = ''
contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index e86ca27f31..9e39536653 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -131,6 +131,14 @@ module ActionView
ensure
self.output_buffer = old_buffer
end
+
+ # Add the output buffer to the response body and start a new one.
+ def flush_output_buffer #:nodoc:
+ if output_buffer && output_buffer != ''
+ response.body_parts << output_buffer
+ self.output_buffer = ''
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index b7ef1fb90d..c74909a360 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -876,8 +876,8 @@ module ActionView
input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
end
- # Given an ordering of datetime components, create the selection html
- # and join them with their appropriate seperators
+ # Given an ordering of datetime components, create the selection HTML
+ # and join them with their appropriate separators.
def build_selects_from_types(order)
select = ''
order.reverse.each do |type|
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 4fef2b443e..a59829b23f 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -5,17 +5,24 @@ require 'action_view/helpers/form_tag_helper'
module ActionView
module Helpers
- # Form helpers are designed to make working with models much easier compared to using just standard HTML
- # elements by providing a set of methods for creating forms based on your models. This helper generates the HTML
- # for forms, providing a method for each sort of input (e.g., text, password, select, and so on). When the form
- # is submitted (i.e., when the user hits the submit button or <tt>form.submit</tt> is called via JavaScript), the form inputs will be bundled into the <tt>params</tt> object and passed back to the controller.
+ # Form helpers are designed to make working with models much easier
+ # compared to using just standard HTML elements by providing a set of
+ # methods for creating forms based on your models. This helper generates
+ # the HTML for forms, providing a method for each sort of input
+ # (e.g., text, password, select, and so on). When the form is submitted
+ # (i.e., when the user hits the submit button or <tt>form.submit</tt> is
+ # called via JavaScript), the form inputs will be bundled into the
+ # <tt>params</tt> object and passed back to the controller.
#
- # There are two types of form helpers: those that specifically work with model attributes and those that don't.
- # This helper deals with those that work with model attributes; to see an example of form helpers that don't work
- # with model attributes, check the ActionView::Helpers::FormTagHelper documentation.
+ # There are two types of form helpers: those that specifically work with
+ # model attributes and those that don't. This helper deals with those that
+ # work with model attributes; to see an example of form helpers that don't
+ # work with model attributes, check the ActionView::Helpers::FormTagHelper
+ # documentation.
#
- # The core method of this helper, form_for, gives you the ability to create a form for a model instance;
- # for example, let's say that you have a model <tt>Person</tt> and want to create a new instance of it:
+ # The core method of this helper, form_for, gives you the ability to create
+ # a form for a model instance; for example, let's say that you have a model
+ # <tt>Person</tt> and want to create a new instance of it:
#
# # Note: a @person variable will have been created in the controller.
# # For example: @person = Person.new
@@ -40,17 +47,22 @@ module ActionView
# <%= submit_tag 'Create' %>
# <% end %>
#
- # This example will render the <tt>people/_form</tt> partial, setting a local variable called <tt>form</tt> which references the yielded FormBuilder.
- #
- # The <tt>params</tt> object created when this form is submitted would look like:
+ # This example will render the <tt>people/_form</tt> partial, setting a
+ # local variable called <tt>form</tt> which references the yielded
+ # FormBuilder. The <tt>params</tt> object created when this form is
+ # submitted would look like:
#
# {"action"=>"create", "controller"=>"persons", "person"=>{"first_name"=>"William", "last_name"=>"Smith"}}
#
- # The params hash has a nested <tt>person</tt> value, which can therefore be accessed with <tt>params[:person]</tt> in the controller.
- # If were editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than <tt>Person.new</tt> in the controller), the objects
- # attribute values are filled into the form (e.g., the <tt>person_first_name</tt> field would have that person's first name in it).
+ # The params hash has a nested <tt>person</tt> value, which can therefore
+ # be accessed with <tt>params[:person]</tt> in the controller. If were
+ # editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than
+ # <tt>Person.new</tt> in the controller), the objects attribute values are
+ # filled into the form (e.g., the <tt>person_first_name</tt> field would
+ # have that person's first name in it).
#
- # If the object name contains square brackets the id for the object will be inserted. For example:
+ # If the object name contains square brackets the id for the object will be
+ # inserted. For example:
#
# <%= text_field "person[]", "name" %>
#
@@ -58,8 +70,10 @@ module ActionView
#
# <input type="text" id="person_<%= @person.id %>_name" name="person[<%= @person.id %>][name]" value="<%= @person.name %>" />
#
- # If the helper is being used to generate a repetitive sequence of similar form elements, for example in a partial
- # used by <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may come in handy. Example:
+ # If the helper is being used to generate a repetitive sequence of similar
+ # form elements, for example in a partial used by
+ # <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may
+ # come in handy. Example:
#
# <%= text_field "person", "name", "index" => 1 %>
#
@@ -67,14 +81,17 @@ module ActionView
#
# <input type="text" id="person_1_name" name="person[1][name]" value="<%= @person.name %>" />
#
- # An <tt>index</tt> option may also be passed to <tt>form_for</tt> and <tt>fields_for</tt>. This automatically applies
- # the <tt>index</tt> to all the nested fields.
+ # An <tt>index</tt> option may also be passed to <tt>form_for</tt> and
+ # <tt>fields_for</tt>. This automatically applies the <tt>index</tt> to
+ # all the nested fields.
#
- # There are also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
- # link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html
+ # There are also methods for helping to build form tags in
+ # link:classes/ActionView/Helpers/FormOptionsHelper.html,
+ # link:classes/ActionView/Helpers/DateHelper.html, and
+ # link:classes/ActionView/Helpers/ActiveRecordHelper.html
module FormHelper
- # Creates a form and a scope around a specific model object that is used as
- # a base for questioning about values for the fields.
+ # Creates a form and a scope around a specific model object that is used
+ # as a base for questioning about values for the fields.
#
# Rails provides succinct resource-oriented form generation with +form_for+
# like this:
@@ -86,13 +103,15 @@ module ActionView
# <%= f.text_field :author %><br />
# <% end %>
#
- # There, +form_for+ is able to generate the rest of RESTful form parameters
- # based on introspection on the record, but to understand what it does we
- # need to dig first into the alternative generic usage it is based upon.
+ # There, +form_for+ is able to generate the rest of RESTful form
+ # parameters based on introspection on the record, but to understand what
+ # it does we need to dig first into the alternative generic usage it is
+ # based upon.
#
# === Generic form_for
#
- # The generic way to call +form_for+ yields a form builder around a model:
+ # The generic way to call +form_for+ yields a form builder around a
+ # model:
#
# <% form_for :person, :url => { :action => "update" } do |f| %>
# <%= f.error_messages %>
@@ -103,8 +122,8 @@ module ActionView
# <% end %>
#
# There, the first argument is a symbol or string with the name of the
- # object the form is about, and also the name of the instance variable the
- # object is stored in.
+ # object the form is about, and also the name of the instance variable
+ # the object is stored in.
#
# The form builder acts as a regular form helper that somehow carries the
# model. Thus, the idea is that
@@ -137,17 +156,18 @@ module ActionView
# In any of its variants, the rightmost argument to +form_for+ is an
# optional hash of options:
#
- # * <tt>:url</tt> - The URL the form is submitted to. It takes the same fields
- # you pass to +url_for+ or +link_to+. In particular you may pass here a
- # named route directly as well. Defaults to the current action.
+ # * <tt>:url</tt> - The URL the form is submitted to. It takes the same
+ # fields you pass to +url_for+ or +link_to+. In particular you may pass
+ # here a named route directly as well. Defaults to the current action.
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
#
- # Worth noting is that the +form_for+ tag is called in a ERb evaluation block,
- # not an ERb output block. So that's <tt><% %></tt>, not <tt><%= %></tt>.
+ # Worth noting is that the +form_for+ tag is called in a ERb evaluation
+ # block, not an ERb output block. So that's <tt><% %></tt>, not
+ # <tt><%= %></tt>.
#
# Also note that +form_for+ doesn't create an exclusive scope. It's still
- # possible to use both the stand-alone FormHelper methods and methods from
- # FormTagHelper. For example:
+ # possible to use both the stand-alone FormHelper methods and methods
+ # from FormTagHelper. For example:
#
# <% form_for :person, @person, :url => { :action => "update" } do |f| %>
# First name: <%= f.text_field :first_name %>
@@ -156,16 +176,16 @@ module ActionView
# Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %>
# <% end %>
#
- # This also works for the methods in FormOptionHelper and DateHelper that are
- # designed to work with an object as base, like FormOptionHelper#collection_select
- # and DateHelper#datetime_select.
+ # This also works for the methods in FormOptionHelper and DateHelper that
+ # are designed to work with an object as base, like
+ # FormOptionHelper#collection_select and DateHelper#datetime_select.
#
# === Resource-oriented style
#
- # As we said above, in addition to manually configuring the +form_for+ call,
- # you can rely on automated resource identification, which will use the conventions
- # and named routes of that approach. This is the preferred way to use +form_for+
- # nowadays.
+ # As we said above, in addition to manually configuring the +form_for+
+ # call, you can rely on automated resource identification, which will use
+ # the conventions and named routes of that approach. This is the
+ # preferred way to use +form_for+ nowadays.
#
# For example, if <tt>@post</tt> is an existing record you want to edit
#
@@ -205,8 +225,10 @@ module ActionView
#
# === Customized form builders
#
- # You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers,
- # then use your custom builder. For example, let's say you made a helper to automatically add labels to form inputs.
+ # You can also build forms using a customized FormBuilder class. Subclass
+ # FormBuilder and override or define some more helpers, then use your
+ # custom builder. For example, let's say you made a helper to
+ # automatically add labels to form inputs.
#
# <% form_for :person, @person, :url => { :action => "update" }, :builder => LabellingFormBuilder do |f| %>
# <%= f.text_field :first_name %>
@@ -219,16 +241,23 @@ module ActionView
#
# <%= render :partial => f %>
#
- # The rendered template is <tt>people/_labelling_form</tt> and the local variable referencing the form builder is called <tt>labelling_form</tt>.
+ # The rendered template is <tt>people/_labelling_form</tt> and the local
+ # variable referencing the form builder is called
+ # <tt>labelling_form</tt>.
#
- # In many cases you will want to wrap the above in another helper, so you could do something like the following:
+ # The custom FormBuilder class is automatically merged with the options
+ # of a nested fields_for call, unless it's explicitely set.
+ #
+ # In many cases you will want to wrap the above in another helper, so you
+ # could do something like the following:
#
# def labelled_form_for(record_or_name_or_array, *args, &proc)
# options = args.extract_options!
# form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
# end
#
- # If you don't need to attach a form to a model instance, then check out FormTagHelper#form_tag.
+ # If you don't need to attach a form to a model instance, then check out
+ # FormTagHelper#form_tag.
def form_for(record_or_name_or_array, *args, &proc)
raise ArgumentError, "Missing block" unless block_given?
@@ -599,7 +628,7 @@ module ActionView
#
# The HTML specification says unchecked check boxes are not successful, and
# thus web browsers do not send them. Unfortunately this introduces a gotcha:
- # if an Invoice model has a +paid+ flag, and in the form that edits a paid
+ # if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
# invoice the user unchecks its check box, no +paid+ parameter is sent. So,
# any mass-assignment idiom like
#
@@ -607,12 +636,15 @@ module ActionView
#
# wouldn't update the flag.
#
- # To prevent this the helper generates a hidden field with the same name as
- # the checkbox after the very check box. So, the client either sends only the
- # hidden field (representing the check box is unchecked), or both fields.
- # Since the HTML specification says key/value pairs have to be sent in the
- # same order they appear in the form and Rails parameters extraction always
- # gets the first occurrence of any given key, that works in ordinary forms.
+ # To prevent this the helper generates an auxiliary hidden field before
+ # the very check box. The hidden field has the same name and its
+ # attributes mimick an unchecked check box.
+ #
+ # This way, the client either sends only the hidden field (representing
+ # the check box is unchecked), or both fields. Since the HTML specification
+ # says key/value pairs have to be sent in the same order they appear in the
+ # form, and parameters extraction gets the last occurrence of any repeated
+ # key in the query string, that works for ordinary forms.
#
# Unfortunately that workaround does not work when the check box goes
# within an array-like parameter, as in
@@ -623,22 +655,26 @@ module ActionView
# <% end %>
#
# because parameter name repetition is precisely what Rails seeks to distinguish
- # the elements of the array.
+ # the elements of the array. For each item with a checked check box you
+ # get an extra ghost item with only that attribute, assigned to "0".
+ #
+ # In that case it is preferable to either use +check_box_tag+ or to use
+ # hashes instead of arrays.
#
# ==== Examples
# # Let's say that @post.validated? is 1:
# check_box("post", "validated")
- # # => <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
- # # <input name="post[validated]" type="hidden" value="0" />
+ # # => <input name="post[validated]" type="hidden" value="0" />
+ # # <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
#
# # Let's say that @puppy.gooddog is "no":
# check_box("puppy", "gooddog", {}, "yes", "no")
- # # => <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
- # # <input name="puppy[gooddog]" type="hidden" value="no" />
+ # # => <input name="puppy[gooddog]" type="hidden" value="no" />
+ # # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
#
# check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
- # # => <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
- # # <input name="eula[accepted]" type="hidden" value="no" />
+ # # => <input name="eula[accepted]" type="hidden" value="no" />
+ # # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
#
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
@@ -910,6 +946,11 @@ module ActionView
index = ""
end
+ if options[:builder]
+ args << {} unless args.last.is_a?(Hash)
+ args.last[:builder] ||= options[:builder]
+ end
+
case record_or_name_or_array
when String, Symbol
if nested_attributes_association?(record_or_name_or_array)
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 4646bc118b..6d39a53adc 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -360,8 +360,8 @@ module ActionView
end
if confirm = options.delete("confirm")
- options["onclick"] ||= ''
- options["onclick"] << "return #{confirm_javascript_function(confirm)};"
+ options["onclick"] ||= 'return true;'
+ options["onclick"] = "if (!#{confirm_javascript_function(confirm)}) return false; #{options['onclick']}"
end
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index e622f97b9e..dea958deaf 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -15,6 +15,7 @@ module ActionView
# * <tt>:country_code</tt> - Sets the country code for the phone number.
#
# ==== Examples
+ # number_to_phone(5551234) # => 555-1234
# number_to_phone(1235551234) # => 123-555-1234
# number_to_phone(1235551234, :area_code => true) # => (123) 555-1234
# number_to_phone(1235551234, :delimiter => " ") # => 123 555 1234
@@ -37,7 +38,8 @@ module ActionView
str << if area_code
number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
else
- number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
+ number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
+ number.starts_with?('-') ? number.slice!(1..-1) : number
end
str << " x #{extension}" unless extension.blank?
str
@@ -138,7 +140,7 @@ module ActionView
# number_with_delimiter(12345678) # => 12,345,678
# number_with_delimiter(12345678.05) # => 12,345,678.05
# number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
- # number_with_delimiter(12345678, :seperator => ",") # => 12,345,678
+ # number_with_delimiter(12345678, :separator => ",") # => 12,345,678
# number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
# # => 98 765 432,98
#
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 18a209dcea..91ef72e54b 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -107,7 +107,7 @@ module ActionView
# on the page in an Ajax response.
module PrototypeHelper
unless const_defined? :CALLBACKS
- CALLBACKS = Set.new([ :uninitialized, :loading, :loaded,
+ CALLBACKS = Set.new([ :create, :uninitialized, :loading, :loaded,
:interactive, :complete, :failure, :success ] +
(100..599).to_a)
AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 63fe0c1c57..573b99b96e 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -324,7 +324,7 @@ module ActionView
# Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
# will limit what should be linked. You can add HTML attributes to the links using
- # <tt>:href_options</tt>. Possible values for <tt>:link</tt> are <tt>:all</tt> (default),
+ # <tt>:html</tt>. Possible values for <tt>:link</tt> are <tt>:all</tt> (default),
# <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
# e-mail address is yielded and the result is used as the link text.
#
@@ -341,7 +341,7 @@ module ActionView
# # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
#
# post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
- # auto_link(post_body, :href_options => { :target => '_blank' }) do |text|
+ # auto_link(post_body, :html => { :target => '_blank' }) do |text|
# truncate(text, 15)
# end
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
@@ -359,7 +359,7 @@ module ActionView
# auto_link(post_body, :all, :target => "_blank") # => Once upon\na time
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
# Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
- def auto_link(text, *args, &block)#link = :all, href_options = {}, &block)
+ def auto_link(text, *args, &block)#link = :all, html = {}, &block)
return '' if text.blank?
options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
@@ -536,8 +536,9 @@ module ActionView
text.gsub(AUTO_LINK_RE) do
href = $&
punctuation = ''
- # detect already linked URLs
- if $` =~ /<a\s[^>]*href="$/
+ left, right = $`, $'
+ # detect already linked URLs and URLs in the middle of a tag
+ if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
# do not change string; URL is alreay linked
href
else
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 41f9f486e5..8cc3fe291c 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -40,7 +40,7 @@ module ActionView #:nodoc:
each(&:load!)
end
- def find_template(original_template_path, format = nil)
+ def find_template(original_template_path, format = nil, html_fallback = true)
return original_template_path if original_template_path.respond_to?(:render)
template_path = original_template_path.sub(/^\//, '')
@@ -54,14 +54,14 @@ module ActionView #:nodoc:
elsif template = load_path[template_path]
return template
# Try to find html version if the format is javascript
- elsif format == :js && template = load_path["#{template_path}.#{I18n.locale}.html"]
+ elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.locale}.html"]
return template
- elsif format == :js && template = load_path["#{template_path}.html"]
+ elsif format == :js && html_fallback && template = load_path["#{template_path}.html"]
return template
end
end
- return Template.new(original_template_path, original_template_path =~ /\A\// ? "" : ".") if File.file?(original_template_path)
+ return Template.new(original_template_path, original_template_path.to_s =~ /\A\// ? "" : ".") if File.file?(original_template_path)
raise MissingTemplate.new(self, original_template_path, format)
end
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index 41080ed629..ff7bc7d9de 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -27,23 +27,19 @@ module ActionView
def render(view, local_assigns = {})
compile(local_assigns)
- stack = view.instance_variable_get(:@_render_stack)
- stack.push(self)
-
- view.send(:_evaluate_assigns_and_ivars)
- view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
-
- result = view.send(method_name(local_assigns), local_assigns) do |*names|
- ivar = :@_proc_for_layout
- if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
- view.capture(*names, &proc)
- elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
- view.instance_variable_get(ivar)
+ view.with_template self do
+ view.send(:_evaluate_assigns_and_ivars)
+ view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
+
+ view.send(method_name(local_assigns), local_assigns) do |*names|
+ ivar = :@_proc_for_layout
+ if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
+ view.capture(*names, &proc)
+ elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
+ view.instance_variable_get(ivar)
+ end
end
end
-
- stack.pop
- result
end
def method_name(local_assigns)
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index ea838b9b02..c339c8a554 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -103,12 +103,12 @@ module ActionView #:nodoc:
@@exempt_from_layout.merge(regexps)
end
- attr_accessor :filename, :load_path, :base_path
+ attr_accessor :template_path, :filename, :load_path, :base_path
attr_accessor :locale, :name, :format, :extension
delegate :to_s, :to => :path
def initialize(template_path, load_path)
- template_path = template_path.dup
+ @template_path = template_path.dup
@load_path, @filename = load_path, File.join(load_path, template_path)
@base_path, @name, @locale, @format, @extension = split(template_path)
@base_path.to_s.gsub!(/\/$/, '') # Push to split method
@@ -119,13 +119,20 @@ module ActionView #:nodoc:
def accessible_paths
paths = []
- paths << path
- paths << path_without_extension
- if multipart?
- formats = format.split(".")
- paths << "#{path_without_format_and_extension}.#{formats.first}"
- paths << "#{path_without_format_and_extension}.#{formats.second}"
+
+ if valid_extension?(extension)
+ paths << path
+ paths << path_without_extension
+ if multipart?
+ formats = format.split(".")
+ paths << "#{path_without_format_and_extension}.#{formats.first}"
+ paths << "#{path_without_format_and_extension}.#{formats.second}"
+ end
+ else
+ # template without explicit template handler should only be reachable through its exact path
+ paths << template_path
end
+
paths
end
@@ -211,7 +218,7 @@ module ActionView #:nodoc:
# Returns file split into an array
# [base_path, name, locale, format, extension]
def split(file)
- if m = file.match(/^(.*\/)?([^\.]+)\.(.*)$/)
+ if m = file.to_s.match(/^(.*\/)?([^\.]+)\.(.*)$/)
base_path = m[1]
name = m[2]
extensions = m[3]