aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG7
-rw-r--r--actionpack/README18
-rw-r--r--actionpack/Rakefile2
-rwxr-xr-xactionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/assertions/model_assertions.rb3
-rw-r--r--actionpack/lib/action_controller/assertions/routing_assertions.rb6
-rw-r--r--actionpack/lib/action_controller/assertions/selector_assertions.rb26
-rw-r--r--actionpack/lib/action_controller/assertions/tag_assertions.rb6
-rwxr-xr-xactionpack/lib/action_controller/base.rb16
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb15
-rw-r--r--actionpack/lib/action_controller/cgi_ext/cookie.rb41
-rw-r--r--actionpack/lib/action_controller/cgi_ext/stdinput.rb1
-rw-r--r--actionpack/lib/action_controller/cgi_process.rb3
-rw-r--r--actionpack/lib/action_controller/dispatcher.rb8
-rw-r--r--actionpack/lib/action_controller/filters.rb40
-rw-r--r--actionpack/lib/action_controller/helpers.rb14
-rw-r--r--actionpack/lib/action_controller/integration.rb24
-rw-r--r--actionpack/lib/action_controller/mime_responds.rb2
-rw-r--r--actionpack/lib/action_controller/mime_type.rb4
-rw-r--r--actionpack/lib/action_controller/polymorphic_routes.rb13
-rw-r--r--actionpack/lib/action_controller/rack_process.rb321
-rwxr-xr-xactionpack/lib/action_controller/request.rb34
-rw-r--r--actionpack/lib/action_controller/request_forgery_protection.rb2
-rwxr-xr-xactionpack/lib/action_controller/request_profiler.rb25
-rw-r--r--actionpack/lib/action_controller/resources.rb6
-rw-r--r--actionpack/lib/action_controller/routing/builder.rb6
-rw-r--r--actionpack/lib/action_controller/routing/segments.rb2
-rw-r--r--actionpack/lib/action_controller/session/cookie_store.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb23
-rw-r--r--actionpack/lib/action_controller/test_process.rb9
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb4
-rw-r--r--actionpack/lib/action_pack/version.rb4
-rw-r--r--actionpack/lib/action_view/base.rb10
-rw-r--r--actionpack/lib/action_view/compiled_templates.rb69
-rw-r--r--actionpack/lib/action_view/helpers/active_record_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb24
-rwxr-xr-xactionpack/lib/action_view/helpers/date_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/debug_helper.rb33
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb129
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb56
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb23
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb12
-rw-r--r--actionpack/lib/action_view/helpers/record_tag_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb31
-rw-r--r--actionpack/lib/action_view/helpers/scriptaculous_helper.rb163
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb4
-rw-r--r--actionpack/lib/action_view/template.rb4
-rw-r--r--actionpack/lib/action_view/test_case.rb14
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb14
-rw-r--r--actionpack/test/controller/caching_test.rb42
-rwxr-xr-xactionpack/test/controller/cgi_test.rb33
-rw-r--r--actionpack/test/controller/helper_test.rb2
-rw-r--r--actionpack/test/controller/rack_test.rb150
-rw-r--r--actionpack/test/controller/routing_test.rb7
-rw-r--r--actionpack/test/controller/test_test.rb20
-rw-r--r--actionpack/test/template/compiled_templates_test.rb192
-rwxr-xr-xactionpack/test/template/date_helper_test.rb6
-rw-r--r--actionpack/test/template/form_helper_test.rb11
-rw-r--r--actionpack/test/template/prototype_helper_test.rb6
-rw-r--r--actionpack/test/template/text_helper_test.rb3
62 files changed, 1099 insertions, 667 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 4a24d2f8b9..9622029362 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,4 +1,9 @@
-*2.1.0 RC1 (May 11th, 2008)*
+* Added Rack processor [Ezra Zygmuntowicz, Josh Peek]
+
+
+*2.1.0 (May 31st, 2008)*
+
+* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing]
* Fixed that forgery protection can be used without session tracking (Peter Jones) [#139]
diff --git a/actionpack/README b/actionpack/README
index 2746c3cc43..6090089bb9 100644
--- a/actionpack/README
+++ b/actionpack/README
@@ -31,7 +31,7 @@ http://www.rubyonrails.org.
A short rundown of the major features:
* Actions grouped in controller as methods instead of separate command objects
- and can therefore share helper methods.
+ and can therefore share helper methods
BlogController < ActionController::Base
def show
@@ -168,7 +168,7 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionController/Base.html]
-* Javascript and Ajax integration.
+* Javascript and Ajax integration
link_to_function "Greeting", "alert('Hello world!')"
link_to_remote "Delete this post", :update => "posts",
@@ -177,7 +177,7 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html]
-* Pagination for navigating lists of results.
+* Pagination for navigating lists of results
# controller
def list
@@ -192,15 +192,9 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionController/Pagination.html]
-* Easy testing of both controller and template result through TestRequest/Response
-
- class LoginControllerTest < Test::Unit::TestCase
- def setup
- @controller = LoginController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
+* Easy testing of both controller and rendered template through ActionController::TestCase
+ class LoginControllerTest < ActionController::TestCase
def test_failing_authenticate
process :authenticate, :user_name => "nop", :password => ""
assert flash.has_key?(:alert)
@@ -208,7 +202,7 @@ A short rundown of the major features:
end
end
- {Learn more}[link:classes/ActionController/TestRequest.html]
+ {Learn more}[link:classes/ActionController/TestCase.html]
* Automated benchmarking and integrated logging
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index 0147a5c1e8..b37f756c1e 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -76,7 +76,7 @@ spec = Gem::Specification.new do |s|
s.has_rdoc = true
s.requirements << 'none'
- s.add_dependency('activesupport', '= 2.0.991' + PKG_BUILD)
+ s.add_dependency('activesupport', '= 2.1.0' + PKG_BUILD)
s.require_path = 'lib'
s.autorequire = 'action_controller'
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 810a5fb9b5..3c4a339d50 100755
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -53,6 +53,7 @@ require 'action_controller/streaming'
require 'action_controller/session_management'
require 'action_controller/http_authentication'
require 'action_controller/components'
+require 'action_controller/rack_process'
require 'action_controller/record_identifier'
require 'action_controller/request_forgery_protection'
require 'action_controller/headers'
diff --git a/actionpack/lib/action_controller/assertions/model_assertions.rb b/actionpack/lib/action_controller/assertions/model_assertions.rb
index 0b4313055a..d25214bb66 100644
--- a/actionpack/lib/action_controller/assertions/model_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/model_assertions.rb
@@ -1,7 +1,8 @@
module ActionController
module Assertions
module ModelAssertions
- # Ensures that the passed record is valid by ActiveRecord standards and returns any error messages if it is not.
+ # Ensures that the passed record is valid by Active Record standards and
+ # returns any error messages if it is not.
#
# ==== Examples
#
diff --git a/actionpack/lib/action_controller/assertions/routing_assertions.rb b/actionpack/lib/action_controller/assertions/routing_assertions.rb
index 2acd003243..491b72d586 100644
--- a/actionpack/lib/action_controller/assertions/routing_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/routing_assertions.rb
@@ -59,7 +59,7 @@ module ActionController
end
end
- # Asserts that the provided options can be used to generate the provided path. This is the inverse of #assert_recognizes.
+ # Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
# The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in
# a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
#
@@ -96,8 +96,8 @@ module ActionController
end
# Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
- # <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines #assert_recognizes
- # and #assert_generates into one step.
+ # <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines +assert_recognizes+
+ # and +assert_generates+ into one step.
#
# The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The
# +message+ parameter allows you to specify a custom error message to display upon failure.
diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb
index 9ef093acfc..d3594e711c 100644
--- a/actionpack/lib/action_controller/assertions/selector_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb
@@ -12,12 +12,12 @@ module ActionController
NO_STRIP = %w{pre script style textarea}
end
- # Adds the #assert_select method for use in Rails functional
+ # Adds the +assert_select+ method for use in Rails functional
# test cases, which can be used to make assertions on the response HTML of a controller
- # action. You can also call #assert_select within another #assert_select to
+ # action. You can also call +assert_select+ within another +assert_select+ to
# make assertions on elements selected by the enclosing assertion.
#
- # Use #css_select to select elements without making an assertions, either
+ # Use +css_select+ to select elements without making an assertions, either
# from the response HTML or elements selected by the enclosing assertion.
#
# In addition to HTML responses, you can make the following assertions:
@@ -44,8 +44,8 @@ module ActionController
# base element and any of its children. Returns an empty array if no
# match is found.
#
- # The selector may be a CSS selector expression (+String+), an expression
- # with substitution values (+Array+) or an HTML::Selector object.
+ # The selector may be a CSS selector expression (String), an expression
+ # with substitution values (Array) or an HTML::Selector object.
#
# ==== Examples
# # Selects all div tags
@@ -114,8 +114,8 @@ module ActionController
# starting from (and including) that element and all its children in
# depth-first order.
#
- # If no element if specified, calling #assert_select will select from the
- # response HTML. Calling #assert_select inside an #assert_select block will
+ # If no element if specified, calling +assert_select+ will select from the
+ # response HTML. Calling #assert_select inside an +assert_select+ block will
# run the assertion for each element selected by the enclosing assertion.
#
# ==== Example
@@ -130,7 +130,7 @@ module ActionController
# assert_select "li"
# end
#
- # The selector may be a CSS selector expression (+String+), an expression
+ # The selector may be a CSS selector expression (String), an expression
# with substitution values, or an HTML::Selector object.
#
# === Equality Tests
@@ -356,16 +356,16 @@ module ActionController
#
# === Using blocks
#
- # Without a block, #assert_select_rjs merely asserts that the response
+ # Without a block, +assert_select_rjs+ merely asserts that the response
# contains one or more RJS statements that replace or update content.
#
- # With a block, #assert_select_rjs also selects all elements used in
+ # With a block, +assert_select_rjs+ also selects all elements used in
# these statements and passes them to the block. Nested assertions are
# supported.
#
- # Calling #assert_select_rjs with no arguments and using nested asserts
+ # Calling +assert_select_rjs+ with no arguments and using nested asserts
# asserts that the HTML content is returned by one or more RJS statements.
- # Using #assert_select directly makes the same assertion on the content,
+ # Using +assert_select+ directly makes the same assertion on the content,
# but without distinguishing whether the content is returned in an HTML
# or JavaScript.
#
@@ -601,7 +601,7 @@ module ActionController
RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
end
- # #assert_select and #css_select call this to obtain the content in the HTML
+ # +assert_select+ and +css_select+ call this to obtain the content in the HTML
# page, or from all the RJS statements, depending on the type of response.
def response_from_page_or_rjs()
content_type = @response.content_type
diff --git a/actionpack/lib/action_controller/assertions/tag_assertions.rb b/actionpack/lib/action_controller/assertions/tag_assertions.rb
index 4ac489520a..90ba3668fb 100644
--- a/actionpack/lib/action_controller/assertions/tag_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/tag_assertions.rb
@@ -91,7 +91,7 @@ module ActionController
# :descendant => { :tag => "span",
# :child => /hello world/ }
#
- # <b>Please note</b>: #assert_tag and #assert_no_tag only work
+ # <b>Please note</b>: +assert_tag+ and +assert_no_tag+ only work
# with well-formed XHTML. They recognize a few tags as implicitly self-closing
# (like br and hr and such) but will not work correctly with tags
# that allow optional closing tags (p, li, td). <em>You must explicitly
@@ -104,8 +104,8 @@ module ActionController
end
end
- # Identical to #assert_tag, but asserts that a matching tag does _not_
- # exist. (See #assert_tag for a full discussion of the syntax.)
+ # Identical to +assert_tag+, but asserts that a matching tag does _not_
+ # exist. (See +assert_tag+ for a full discussion of the syntax.)
#
# === Examples
# # Assert that there is not a "div" containing a "p"
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index ea55fe42ce..6222c3205d 100755
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -104,7 +104,7 @@ module ActionController #:nodoc:
# end
#
# Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
- # after executing code in the action. For example, the +index+ action of the +GuestBookController+ would render the
+ # after executing code in the action. For example, the +index+ action of the GuestBookController would render the
# template <tt>app/views/guestbook/index.erb</tt> by default after populating the <tt>@entries</tt> instance variable.
#
# Unlike index, the sign action will not render a template. After performing its main purpose (creating a
@@ -118,10 +118,10 @@ module ActionController #:nodoc:
#
# Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters.
# This value should hold the name of the action to be performed. Once the action has been identified, the remaining
- # request parameters, the session (if one is available), and the full request with all the http headers are made available to
+ # request parameters, the session (if one is available), and the full request with all the HTTP headers are made available to
# the action through instance variables. Then the action is performed.
#
- # The full request object is available with the request accessor and is primarily used to query for http headers. These queries
+ # The full request object is available with the request accessor and is primarily used to query for HTTP headers. These queries
# are made by accessing the environment hash, like this:
#
# def server_ip
@@ -291,10 +291,10 @@ module ActionController #:nodoc:
cattr_accessor :allow_concurrency
# Modern REST web services often need to submit complex data to the web application.
- # The param_parsers hash lets you register handlers which will process the http body and add parameters to the
- # <tt>params</tt> hash. These handlers are invoked for post and put requests.
+ # The <tt>@@param_parsers</tt> hash lets you register handlers which will process the HTTP body and add parameters to the
+ # <tt>params</tt> hash. These handlers are invoked for POST and PUT requests.
#
- # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instantiated
+ # By default <tt>application/xml</tt> is enabled. A XmlSimple class with the same param name as the root will be instantiated
# in the <tt>params</tt>. This allows XML requests to mask themselves as regular form submissions, so you can have one
# action serve both regular forms and web service requests.
#
@@ -307,7 +307,7 @@ module ActionController #:nodoc:
#
# Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the
# root node for such requests. The new default is to keep the root, such that "<r><name>David</name></r>" results
- # in params[:r][:name] for "David" instead of params[:name]. To get the old behavior, you can
+ # in <tt>params[:r][:name]</tt> for "David" instead of <tt>params[:name]</tt>. To get the old behavior, you can
# re-register XmlSimple as application/xml handler ike this:
#
# ActionController::Base.param_parsers[Mime::XML] =
@@ -347,7 +347,7 @@ module ActionController #:nodoc:
cattr_accessor :optimise_named_routes
self.optimise_named_routes = true
- # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
+ # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
class_inheritable_accessor :allow_forgery_protection
self.allow_forgery_protection = true
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index e4d8678a07..578e031a17 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -2,7 +2,7 @@ module ActionController #:nodoc:
module Caching
# Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when
# certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple
- # parties. The caching is doing using the cache helper available in the Action View. A template with caching might look something like:
+ # parties. The caching is done using the cache helper available in the Action View. A template with caching might look something like:
#
# <b>Hello <%= @name %></b>
# <% cache do %>
@@ -98,6 +98,17 @@ module ActionController #:nodoc:
end
end
+ # Check if a cached fragment from the location signified by <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
+ def fragment_exist?(key, options = nil)
+ return unless cache_configured?
+
+ key = fragment_cache_key(key)
+
+ self.class.benchmark "Cached fragment exists?: #{key}" do
+ cache_store.exist?(key, options)
+ end
+ end
+
# Name can take one of three forms:
# * String: This would normally take the form of a path like "pages/45/notes"
# * Hash: Is treated as an implicit call to url_for, like { :controller => "pages", :action => "notes", :id => 45 }
@@ -124,4 +135,4 @@ module ActionController #:nodoc:
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/cgi_ext/cookie.rb b/actionpack/lib/action_controller/cgi_ext/cookie.rb
index a244e2a39a..009ddd1c64 100644
--- a/actionpack/lib/action_controller/cgi_ext/cookie.rb
+++ b/actionpack/lib/action_controller/cgi_ext/cookie.rb
@@ -6,25 +6,24 @@ class CGI #:nodoc:
attr_accessor :name, :value, :path, :domain, :expires
attr_reader :secure, :http_only
- # Create a new CGI::Cookie object.
+ # Creates a new CGI::Cookie object.
#
# The contents of the cookie can be specified as a +name+ and one
# or more +value+ arguments. Alternatively, the contents can
# be specified as a single hash argument. The possible keywords of
# this hash are as follows:
#
- # name:: the name of the cookie. Required.
- # value:: the cookie's value or list of values.
- # path:: the path for which this cookie applies. Defaults to the
- # base directory of the CGI script.
- # domain:: the domain for which this cookie applies.
- # expires:: the time at which this cookie expires, as a +Time+ object.
- # secure:: whether this cookie is a secure cookie or not (default to
- # false). Secure cookies are only transmitted to HTTPS
- # servers.
- # http_only:: whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP
- # More details: http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx
- # Defaults to false.
+ # * <tt>:name</tt> - The name of the cookie. Required.
+ # * <tt>:value</tt> - The cookie's value or list of values.
+ # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the
+ # base directory of the CGI script.
+ # * <tt>:domain</tt> - The domain for which this cookie applies.
+ # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
+ # * <tt>:secure</tt> - Whether this cookie is a secure cookie or not (defaults to
+ # +false+). Secure cookies are only transmitted to HTTPS servers.
+ # * <tt>:http_only</tt> - Whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP.
+ # More details in http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx. Defaults to +false+.
+ #
# These keywords correspond to attributes of the cookie object.
def initialize(name = '', *value)
if name.kind_of?(String)
@@ -56,17 +55,17 @@ class CGI #:nodoc:
super(@value)
end
- # Set whether the Cookie is a secure cookie or not.
+ # Sets whether the Cookie is a secure cookie or not.
def secure=(val)
@secure = val == true
end
- # Set whether the Cookie is an HTTP only cookie or not.
+ # Sets whether the Cookie is an HTTP only cookie or not.
def http_only=(val)
@http_only = val == true
end
- # Convert the Cookie to its string representation.
+ # Converts the Cookie to its string representation.
def to_s
buf = ''
buf << @name << '='
@@ -79,11 +78,17 @@ class CGI #:nodoc:
buf
end
- # Parse a raw cookie string into a hash of cookie-name=>Cookie
+ # FIXME: work around broken 1.8.7 DelegateClass#respond_to?
+ def respond_to?(method, include_private = false)
+ return true if super(method)
+ return __getobj__.respond_to?(method, include_private)
+ end
+
+ # Parses a raw cookie string into a hash of <tt>cookie-name => cookie-object</tt>
# pairs.
#
# cookies = CGI::Cookie::parse("raw_cookie_string")
- # # { "name1" => cookie1, "name2" => cookie2, ... }
+ # # => { "name1" => cookie1, "name2" => cookie2, ... }
#
def self.parse(raw_cookie)
cookies = Hash.new([])
diff --git a/actionpack/lib/action_controller/cgi_ext/stdinput.rb b/actionpack/lib/action_controller/cgi_ext/stdinput.rb
index b0ca63ef2f..5e9b6784af 100644
--- a/actionpack/lib/action_controller/cgi_ext/stdinput.rb
+++ b/actionpack/lib/action_controller/cgi_ext/stdinput.rb
@@ -16,6 +16,7 @@ module ActionController
def initialize_with_stdinput(type = nil, stdinput = $stdin)
@stdinput = stdinput
+ @stdinput.set_encoding(Encoding::BINARY) if @stdinput.respond_to?(:set_encoding)
initialize_without_stdinput(type || 'query')
end
end
diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb
index b529db8af4..8bc5e4c3a7 100644
--- a/actionpack/lib/action_controller/cgi_process.rb
+++ b/actionpack/lib/action_controller/cgi_process.rb
@@ -15,7 +15,7 @@ module ActionController #:nodoc:
# * <tt>:new_session</tt> - if true, force creation of a new session. If not set, a new session is only created if none currently
# exists. If false, a new session is never created, and if none currently exists and the +session_id+ option is not set,
# an ArgumentError is raised.
- # * <tt>:session_expires</tt> - the time the current session expires, as a +Time+ object. If not set, the session will continue
+ # * <tt>:session_expires</tt> - the time the current session expires, as a Time object. If not set, the session will continue
# indefinitely.
# * <tt>:session_domain</tt> - the hostname domain for which this session is valid. If not set, defaults to the hostname of the
# server.
@@ -65,6 +65,7 @@ module ActionController #:nodoc:
# variable is already set, wrap it in a StringIO.
def body
if raw_post = env['RAW_POST_DATA']
+ raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
StringIO.new(raw_post)
else
@cgi.stdinput
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index 6e1e7a261f..b40f1ba9be 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -96,7 +96,7 @@ module ActionController
include ActiveSupport::Callbacks
define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
- def initialize(output, request = nil, response = nil)
+ def initialize(output = $stdout, request = nil, response = nil)
@output, @request, @response = output, request, response
end
@@ -123,6 +123,12 @@ module ActionController
failsafe_rescue exception
end
+ def call(env)
+ @request = RackRequest.new(env)
+ @response = RackResponse.new
+ dispatch
+ end
+
def reload_application
# Run prepare callbacks before every request in development mode
run_callbacks :prepare_dispatch
diff --git a/actionpack/lib/action_controller/filters.rb b/actionpack/lib/action_controller/filters.rb
index 6d0c83eb40..60d92d9b98 100644
--- a/actionpack/lib/action_controller/filters.rb
+++ b/actionpack/lib/action_controller/filters.rb
@@ -100,10 +100,10 @@ module ActionController #:nodoc:
#
# Around filters wrap an action, executing code both before and after.
# They may be declared as method references, blocks, or objects responding
- # to #filter or to both #before and #after.
+ # to +filter+ or to both +before+ and +after+.
#
- # To use a method as an around_filter, pass a symbol naming the Ruby method.
- # Yield (or block.call) within the method to run the action.
+ # To use a method as an +around_filter+, pass a symbol naming the Ruby method.
+ # Yield (or <tt>block.call</tt>) within the method to run the action.
#
# around_filter :catch_exceptions
#
@@ -115,9 +115,9 @@ module ActionController #:nodoc:
# raise
# end
#
- # To use a block as an around_filter, pass a block taking as args both
+ # To use a block as an +around_filter+, pass a block taking as args both
# the controller and the action block. You can't call yield directly from
- # an around_filter block; explicitly call the action block instead:
+ # an +around_filter+ block; explicitly call the action block instead:
#
# around_filter do |controller, action|
# logger.debug "before #{controller.action_name}"
@@ -125,7 +125,7 @@ module ActionController #:nodoc:
# logger.debug "after #{controller.action_name}"
# end
#
- # To use a filter object with around_filter, pass an object responding
+ # To use a filter object with +around_filter+, pass an object responding
# to <tt>:filter</tt> or both <tt>:before</tt> and <tt>:after</tt>. With a
# filter method, yield to the block as above:
#
@@ -137,7 +137,7 @@ module ActionController #:nodoc:
# end
# end
#
- # With before and after methods:
+ # With +before+ and +after+ methods:
#
# around_filter Authorizer.new
#
@@ -154,9 +154,9 @@ module ActionController #:nodoc:
# end
# end
#
- # If the filter has before and after methods, the before method will be
- # called before the action. If before renders or redirects, the filter chain is
- # halted and after will not be run. See Filter Chain Halting below for
+ # If the filter has +before+ and +after+ methods, the +before+ method will be
+ # called before the action. If +before+ renders or redirects, the filter chain is
+ # halted and +after+ will not be run. See Filter Chain Halting below for
# an example.
#
# == Filter chain skipping
@@ -215,7 +215,7 @@ module ActionController #:nodoc:
#
# <tt>before_filter</tt> and <tt>around_filter</tt> may halt the request
# before a controller action is run. This is useful, for example, to deny
- # access to unauthenticated users or to redirect from http to https.
+ # access to unauthenticated users or to redirect from HTTP to HTTPS.
# Simply call render or redirect. After filters will not be executed if the filter
# chain is halted.
#
@@ -241,10 +241,10 @@ module ActionController #:nodoc:
# . /
# #after (actual filter code is run, unless the around filter does not yield)
#
- # If #around returns before yielding, #after will still not be run. The #before
- # filter and controller action will not be run. If #before renders or redirects,
- # the second half of #around and will still run but #after and the
- # action will not. If #around fails to yield, #after will not be run.
+ # If +around+ returns before yielding, +after+ will still not be run. The +before+
+ # filter and controller action will not be run. If +before+ renders or redirects,
+ # the second half of +around+ and will still run but +after+ and the
+ # action will not. If +around+ fails to yield, +after+ will not be run.
class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
def append_filter_to_chain(filters, filter_type, &block)
@@ -471,7 +471,7 @@ module ActionController #:nodoc:
# Shorthand for append_after_filter since it's the most common.
alias :after_filter :append_after_filter
- # If you append_around_filter A.new, B.new, the filter chain looks like
+ # If you <tt>append_around_filter A.new, B.new</tt>, the filter chain looks like
#
# B#before
# A#before
@@ -479,13 +479,13 @@ module ActionController #:nodoc:
# A#after
# B#after
#
- # With around filters which yield to the action block, #before and #after
+ # With around filters which yield to the action block, +before+ and +after+
# are the code before and after the yield.
def append_around_filter(*filters, &block)
filter_chain.append_filter_to_chain(filters, :around, &block)
end
- # If you prepend_around_filter A.new, B.new, the filter chain looks like:
+ # If you <tt>prepend_around_filter A.new, B.new</tt>, the filter chain looks like:
#
# A#before
# B#before
@@ -493,13 +493,13 @@ module ActionController #:nodoc:
# B#after
# A#after
#
- # With around filters which yield to the action block, #before and #after
+ # With around filters which yield to the action block, +before+ and +after+
# are the code before and after the yield.
def prepend_around_filter(*filters, &block)
filter_chain.prepend_filter_to_chain(filters, :around, &block)
end
- # Shorthand for append_around_filter since it's the most common.
+ # Shorthand for +append_around_filter+ since it's the most common.
alias :around_filter :append_around_filter
# Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference
diff --git a/actionpack/lib/action_controller/helpers.rb b/actionpack/lib/action_controller/helpers.rb
index a8bead4d34..ce5e8be54c 100644
--- a/actionpack/lib/action_controller/helpers.rb
+++ b/actionpack/lib/action_controller/helpers.rb
@@ -20,7 +20,7 @@ module ActionController #:nodoc:
end
# The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
- # +numbers+ and +ActiveRecord+ objects, to name a few. These helpers are available to all templates
+ # +numbers+ and Active Record objects, to name a few. These helpers are available to all templates
# by default.
#
# In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
@@ -32,7 +32,7 @@ module ActionController #:nodoc:
# controller which inherits from it.
#
# ==== Examples
- # The +to_s+ method from the +Time+ class can be wrapped in a helper method to display a custom message if
+ # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if
# the Time object is blank:
#
# module FormattedTimeHelper
@@ -41,7 +41,7 @@ module ActionController #:nodoc:
# end
# end
#
- # +FormattedTimeHelper+ can now be included in a controller, using the +helper+ class method:
+ # FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
#
# class EventsController < ActionController::Base
# helper FormattedTimeHelper
@@ -74,22 +74,22 @@ module ActionController #:nodoc:
# The +helper+ class method can take a series of helper module names, a block, or both.
#
- # * <tt>*args</tt>: One or more +Modules+, +Strings+ or +Symbols+, or the special symbol <tt>:all</tt>.
+ # * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>.
# * <tt>&block</tt>: A block defining helper methods.
#
# ==== Examples
- # When the argument is a +String+ or +Symbol+, the method will provide the "_helper" suffix, require the file
+ # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
# and include the module in the template class. The second form illustrates how to include custom helpers
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
# in one of Rails' standard load paths:
# helper :foo # => requires 'foo_helper' and includes FooHelper
# helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
#
- # When the argument is a +Module+, it will be included directly in the template class.
+ # When the argument is a module it will be included directly in the template class.
# helper FooHelper # => includes FooHelper
#
# When the argument is the symbol <tt>:all</tt>, the controller will include all helpers from
- # <tt>app/helpers/**/*.rb</tt> under +RAILS_ROOT+.
+ # <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT.
# helper :all
#
# Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index a4bbee9c54..12e1b4d493 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -58,7 +58,7 @@ module ActionController
class MultiPartNeededException < Exception
end
- # Create and initialize a new +Session+ instance.
+ # Create and initialize a new Session instance.
def initialize
reset!
end
@@ -100,7 +100,7 @@ module ActionController
@https = flag
end
- # Return +true+ if the session is mimicing a secure HTTPS request.
+ # Return +true+ if the session is mimicking a secure HTTPS request.
#
# if session.https?
# ...
@@ -136,25 +136,25 @@ module ActionController
end
# Performs a GET request, following any subsequent redirect.
- # See #request_via_redirect() for more information.
+ # See +request_via_redirect+ for more information.
def get_via_redirect(path, parameters = nil, headers = nil)
request_via_redirect(:get, path, parameters, headers)
end
# Performs a POST request, following any subsequent redirect.
- # See #request_via_redirect() for more information.
+ # See +request_via_redirect+ for more information.
def post_via_redirect(path, parameters = nil, headers = nil)
request_via_redirect(:post, path, parameters, headers)
end
# Performs a PUT request, following any subsequent redirect.
- # See #request_via_redirect() for more information.
+ # See +request_via_redirect+ for more information.
def put_via_redirect(path, parameters = nil, headers = nil)
request_via_redirect(:put, path, parameters, headers)
end
# Performs a DELETE request, following any subsequent redirect.
- # See #request_via_redirect() for more information.
+ # See +request_via_redirect+ for more information.
def delete_via_redirect(path, parameters = nil, headers = nil)
request_via_redirect(:delete, path, parameters, headers)
end
@@ -166,12 +166,12 @@ module ActionController
# Performs a GET request with the given parameters. The parameters may
# be +nil+, a Hash, or a string that is appropriately encoded
- # (application/x-www-form-urlencoded or multipart/form-data). The headers
- # should be a hash. The keys will automatically be upcased, with the
+ # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
+ # The headers should be a hash. The keys will automatically be upcased, with the
# prefix 'HTTP_' added if needed.
#
- # You can also perform POST, PUT, DELETE, and HEAD requests with #post,
- # #put, #delete, and #head.
+ # You can also perform POST, PUT, DELETE, and HEAD requests with +post+,
+ # +put+, +delete+, and +head+.
def get(path, parameters = nil, headers = nil)
process :get, path, parameters, headers
end
@@ -228,6 +228,8 @@ module ActionController
super
+ stdinput.set_encoding(Encoding::BINARY) if stdinput.respond_to?(:set_encoding)
+ stdinput.force_encoding(Encoding::BINARY) if stdinput.respond_to?(:force_encoding)
@stdinput = stdinput.is_a?(IO) ? stdinput : StringIO.new(stdinput || '')
end
end
@@ -382,6 +384,8 @@ module ActionController
multipart_requestify(params).map do |key, value|
if value.respond_to?(:original_filename)
File.open(value.path) do |f|
+ f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
+
<<-EOF
--#{boundary}\r
Content-Disposition: form-data; name="#{key}"; filename="#{CGI.escape(value.original_filename)}"\r
diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb
index a17782cafc..1dbd8b9e6f 100644
--- a/actionpack/lib/action_controller/mime_responds.rb
+++ b/actionpack/lib/action_controller/mime_responds.rb
@@ -92,7 +92,7 @@ module ActionController #:nodoc:
# with the remaining data.
#
# Note that you can define your own XML parameter parser which would allow you to describe multiple entities
- # in a single request (i.e., by wrapping them all in a single root note), but if you just go with the flow
+ # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
# and accept Rails' defaults, life will be much easier.
#
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb
index f43e2ba06d..fa123f7808 100644
--- a/actionpack/lib/action_controller/mime_type.rb
+++ b/actionpack/lib/action_controller/mime_type.rb
@@ -104,7 +104,7 @@ module Mime
list[text_xml].name = Mime::XML.to_s
end
- # Look for more specific xml-based types and sort them ahead of app/xml
+ # Look for more specific XML-based types and sort them ahead of app/xml
if app_xml
idx = app_xml
@@ -158,7 +158,7 @@ module Mime
end
end
- # Returns true if ActionPack should check requests using this Mime Type for possible request forgery. See
+ # Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
# ActionController::RequestForgerProtection.
def verify_request?
!@@unverifiable_types.include?(to_sym)
diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb
index e2b7716aa2..509fa6a08e 100644
--- a/actionpack/lib/action_controller/polymorphic_routes.rb
+++ b/actionpack/lib/action_controller/polymorphic_routes.rb
@@ -1,6 +1,6 @@
module ActionController
# Polymorphic URL helpers are methods for smart resolution to a named route call when
- # given an ActiveRecord model instance. They are to be used in combination with
+ # given an Active Record model instance. They are to be used in combination with
# ActionController::Resources.
#
# These methods are useful when you want to generate correct URL or path to a RESTful
@@ -9,7 +9,9 @@ module ActionController
# Nested resources and/or namespaces are also supported, as illustrated in the example:
#
# polymorphic_url([:admin, @article, @comment])
- # #-> results in:
+ #
+ # results in:
+ #
# admin_article_comment_url(@article, @comment)
#
# == Usage within the framework
@@ -38,11 +40,8 @@ module ActionController
#
# Example usage:
#
- # edit_polymorphic_path(@post)
- # #=> /posts/1/edit
- #
- # formatted_polymorphic_path([@post, :pdf])
- # #=> /posts/1.pdf
+ # edit_polymorphic_path(@post) # => "/posts/1/edit"
+ # formatted_polymorphic_path([@post, :pdf]) # => "/posts/1.pdf"
module PolymorphicRoutes
# Constructs a call to a named RESTful route for the given record and returns the
# resulting URL string. For example:
diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb
new file mode 100644
index 0000000000..f42212e740
--- /dev/null
+++ b/actionpack/lib/action_controller/rack_process.rb
@@ -0,0 +1,321 @@
+require 'action_controller/cgi_ext'
+require 'action_controller/session/cookie_store'
+
+module ActionController #:nodoc:
+ class RackRequest < AbstractRequest #:nodoc:
+ attr_accessor :env, :session_options
+
+ class SessionFixationAttempt < StandardError #:nodoc:
+ end
+
+ DEFAULT_SESSION_OPTIONS = {
+ :database_manager => CGI::Session::CookieStore, # store data in cookie
+ :prefix => "ruby_sess.", # prefix session file names
+ :session_path => "/", # available to all paths in app
+ :session_key => "_session_id",
+ :cookie_only => true
+ } unless const_defined?(:DEFAULT_SESSION_OPTIONS)
+
+ def initialize(env, session_options = DEFAULT_SESSION_OPTIONS)
+ @session_options = session_options
+ @env = env
+ @cgi = CGIWrapper.new(self)
+ super()
+ end
+
+ # The request body is an IO input stream. If the RAW_POST_DATA environment
+ # variable is already set, wrap it in a StringIO.
+ def body
+ if raw_post = env['RAW_POST_DATA']
+ StringIO.new(raw_post)
+ else
+ @env['rack.input']
+ end
+ end
+
+ def key?(key)
+ @env.key? key
+ end
+
+ def query_parameters
+ @query_parameters ||= self.class.parse_query_parameters(query_string)
+ end
+
+ def request_parameters
+ @request_parameters ||= parse_formatted_request_parameters
+ end
+
+ def cookies
+ return {} unless @env["HTTP_COOKIE"]
+
+ if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
+ @env["rack.request.cookie_hash"]
+ else
+ @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
+ # According to RFC 2109:
+ # If multiple cookies satisfy the criteria above, they are ordered in
+ # the Cookie header such that those with more specific Path attributes
+ # precede those with less specific. Ordering with respect to other
+ # attributes (e.g., Domain) is unspecified.
+ @env["rack.request.cookie_hash"] =
+ parse_query(@env["rack.request.cookie_string"], ';,').inject({}) { |h, (k,v)|
+ h[k] = Array === v ? v.first : v
+ h
+ }
+ end
+ end
+
+ def host_with_port_without_standard_port_handling
+ if forwarded = @env["HTTP_X_FORWARDED_HOST"]
+ forwarded.split(/,\s?/).last
+ elsif http_host = @env['HTTP_HOST']
+ http_host
+ elsif server_name = @env['SERVER_NAME']
+ server_name
+ else
+ "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
+ end
+ end
+
+ def host
+ host_with_port_without_standard_port_handling.sub(/:\d+$/, '')
+ end
+
+ def port
+ if host_with_port_without_standard_port_handling =~ /:(\d+)$/
+ $1.to_i
+ else
+ standard_port
+ end
+ end
+
+ def remote_addr
+ @env['REMOTE_ADDR']
+ end
+
+ def session
+ unless defined?(@session)
+ if @session_options == false
+ @session = Hash.new
+ else
+ stale_session_check! do
+ if cookie_only? && query_parameters[session_options_with_string_keys['session_key']]
+ raise SessionFixationAttempt
+ end
+ case value = session_options_with_string_keys['new_session']
+ when true
+ @session = new_session
+ when false
+ begin
+ @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+ # CGI::Session raises ArgumentError if 'new_session' == false
+ # and no session cookie or query param is present.
+ rescue ArgumentError
+ @session = Hash.new
+ end
+ when nil
+ @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+ else
+ raise ArgumentError, "Invalid new_session option: #{value}"
+ end
+ @session['__valid_session']
+ end
+ end
+ end
+ @session
+ end
+
+ def reset_session
+ @session.delete if defined?(@session) && @session.is_a?(CGI::Session)
+ @session = new_session
+ end
+
+ private
+ # Delete an old session if it exists then create a new one.
+ def new_session
+ if @session_options == false
+ Hash.new
+ else
+ CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
+ CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
+ end
+ end
+
+ def cookie_only?
+ session_options_with_string_keys['cookie_only']
+ end
+
+ def stale_session_check!
+ yield
+ rescue ArgumentError => argument_error
+ if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
+ begin
+ # Note that the regexp does not allow $1 to end with a ':'
+ $1.constantize
+ rescue LoadError, NameError => const_error
+ raise ActionController::SessionRestoreError, <<-end_msg
+Session contains objects whose class definition isn\'t available.
+Remember to require the classes for all objects kept in the session.
+(Original exception: #{const_error.message} [#{const_error.class}])
+end_msg
+ end
+
+ retry
+ else
+ raise
+ end
+ end
+
+ def session_options_with_string_keys
+ @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
+ end
+
+ # From Rack::Utils
+ def parse_query(qs, d = '&;')
+ params = {}
+ (qs || '').split(/[#{d}] */n).inject(params) { |h,p|
+ k, v = unescape(p).split('=',2)
+ if cur = params[k]
+ if cur.class == Array
+ params[k] << v
+ else
+ params[k] = [cur, v]
+ end
+ else
+ params[k] = v
+ end
+ }
+
+ return params
+ end
+
+ def unescape(s)
+ s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
+ [$1.delete('%')].pack('H*')
+ }
+ end
+ end
+
+ class RackResponse < AbstractResponse #:nodoc:
+ attr_accessor :status
+
+ def initialize
+ @writer = lambda { |x| @body << x }
+ @block = nil
+ super()
+ end
+
+ def out(output = $stdout, &block)
+ @block = block
+ normalize_headers(@headers)
+ if [204, 304].include?(@status.to_i)
+ @headers.delete "Content-Type"
+ [status.to_i, @headers.to_hash, []]
+ else
+ [status.to_i, @headers.to_hash, self]
+ end
+ end
+ alias to_a out
+
+ def each(&callback)
+ if @body.respond_to?(:call)
+ @writer = lambda { |x| callback.call(x) }
+ @body.call(self, self)
+ else
+ @body.each(&callback)
+ end
+
+ @writer = callback
+ @block.call(self) if @block
+ end
+
+ def write(str)
+ @writer.call str.to_s
+ str
+ end
+
+ def close
+ @body.close if @body.respond_to?(:close)
+ end
+
+ def empty?
+ @block == nil && @body.empty?
+ end
+
+ private
+ def normalize_headers(options = "text/html")
+ if options.is_a?(String)
+ headers['Content-Type'] = options unless headers['Content-Type']
+ else
+ headers['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
+
+ headers['Content-Type'] = options.delete('type') || "text/html"
+ headers['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
+
+ headers['Content-Language'] = options.delete('language') if options['language']
+ headers['Expires'] = options.delete('expires') if options['expires']
+
+ @status = options.delete('Status') if options['Status']
+ @status ||= 200
+ # Convert 'cookie' header to 'Set-Cookie' headers.
+ # Because Set-Cookie header can appear more the once in the response body,
+ # we store it in a line break separated string that will be translated to
+ # multiple Set-Cookie header by the handler.
+ if cookie = options.delete('cookie')
+ cookies = []
+
+ case cookie
+ when Array then cookie.each { |c| cookies << c.to_s }
+ when Hash then cookie.each { |_, c| cookies << c.to_s }
+ else cookies << cookie.to_s
+ end
+
+ @output_cookies.each { |c| cookies << c.to_s } if @output_cookies
+
+ headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].compact.join("\n")
+ end
+
+ options.each { |k,v| headers[k] = v }
+ end
+
+ ""
+ end
+ end
+
+ class CGIWrapper < ::CGI
+ def initialize(request, *args)
+ @request = request
+ @args = *args
+ @input = request.body
+
+ super *args
+ end
+
+ def params
+ @params ||= @request.params
+ end
+
+ def cookies
+ @request.cookies
+ end
+
+ def query_string
+ @request.query_string
+ end
+
+ # Used to wrap the normal args variable used inside CGI.
+ def args
+ @args
+ end
+
+ # Used to wrap the normal env_table variable used inside CGI.
+ def env_table
+ @request.env
+ end
+
+ # Used to wrap the normal stdinput variable used inside CGI.
+ def stdinput
+ @input
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index d5ecbd9d29..914163a709 100755
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -61,7 +61,7 @@ module ActionController
request_method == :head
end
- # Provides acccess to the request's HTTP headers, for example:
+ # Provides access to the request's HTTP headers, for example:
# request.headers["Content-Type"] # => "text/plain"
def headers
@headers ||= ActionController::Http::Headers.new(@env)
@@ -231,7 +231,7 @@ EOM
parts[0..-(tld_length+2)]
end
- # Return the query string, accounting for server idiosyncracies.
+ # Return the query string, accounting for server idiosyncrasies.
def query_string
if uri = @env['REQUEST_URI']
uri.split('?', 2)[1] || ''
@@ -240,7 +240,7 @@ EOM
end
end
- # Return the request URI, accounting for server idiosyncracies.
+ # Return the request URI, accounting for server idiosyncrasies.
# WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
def request_uri
if uri = @env['REQUEST_URI']
@@ -466,8 +466,8 @@ EOM
parser.result
end
- def parse_multipart_form_parameters(body, boundary, content_length, env)
- parse_request_parameters(read_multipart(body, boundary, content_length, env))
+ def parse_multipart_form_parameters(body, boundary, body_size, env)
+ parse_request_parameters(read_multipart(body, boundary, body_size, env))
end
def extract_multipart_boundary(content_type_with_parameters)
@@ -519,7 +519,7 @@ EOM
EOL = "\015\012"
- def read_multipart(body, boundary, content_length, env)
+ def read_multipart(body, boundary, body_size, env)
params = Hash.new([])
boundary = "--" + boundary
quoted_boundary = Regexp.quote(boundary)
@@ -529,8 +529,14 @@ EOM
# start multipart/form-data
body.binmode if defined? body.binmode
+ case body
+ when File
+ body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding)
+ when StringIO
+ body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding)
+ end
boundary_size = boundary.size + EOL.size
- content_length -= boundary_size
+ body_size -= boundary_size
status = body.read(boundary_size)
if nil == status
raise EOFError, "no content body"
@@ -541,7 +547,7 @@ EOM
loop do
head = nil
content =
- if 10240 < content_length
+ if 10240 < body_size
UploadedTempfile.new("CGI")
else
UploadedStringIO.new
@@ -563,24 +569,24 @@ EOM
buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
end
- c = if bufsize < content_length
+ c = if bufsize < body_size
body.read(bufsize)
else
- body.read(content_length)
+ body.read(body_size)
end
if c.nil? || c.empty?
raise EOFError, "bad content body"
end
buf.concat(c)
- content_length -= c.size
+ body_size -= c.size
end
buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
content.print $1
if "--" == $2
- content_length = -1
+ body_size = -1
end
- boundary_end = $2.dup
+ boundary_end = $2.dup
""
end
@@ -607,7 +613,7 @@ EOM
else
params[name] = [content]
end
- break if content_length == -1
+ break if body_size == -1
end
raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb
index 02c9d59d07..05a6d8bb79 100644
--- a/actionpack/lib/action_controller/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/request_forgery_protection.rb
@@ -17,7 +17,7 @@ module ActionController #:nodoc:
# forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all
# forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
- # scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
+ # scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway.
#
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
diff --git a/actionpack/lib/action_controller/request_profiler.rb b/actionpack/lib/action_controller/request_profiler.rb
index 8a18d194bd..a6471d0c08 100755
--- a/actionpack/lib/action_controller/request_profiler.rb
+++ b/actionpack/lib/action_controller/request_profiler.rb
@@ -17,13 +17,13 @@ module ActionController
reset!
end
- def benchmark(n)
+ def benchmark(n, profiling = false)
@quiet = true
print ' '
result = Benchmark.realtime do
n.times do |i|
- run
+ run(profiling)
print_progress(i)
end
end
@@ -43,8 +43,15 @@ module ActionController
script = File.read(script_path)
source = <<-end_source
- def run
- #{script}
+ def run(profiling = false)
+ if profiling
+ RubyProf.resume do
+ #{script}
+ end
+ else
+ #{script}
+ end
+
old_request_count = request_count
reset!
self.request_count = old_request_count
@@ -91,21 +98,22 @@ module ActionController
def profile(sandbox)
load_ruby_prof
- results = RubyProf.profile { benchmark(sandbox) }
+ benchmark(sandbox, true)
+ results = RubyProf.stop
show_profile_results results
results
end
- def benchmark(sandbox)
+ def benchmark(sandbox, profiling = false)
sandbox.request_count = 0
- elapsed = sandbox.benchmark(options[:n]).to_f
+ elapsed = sandbox.benchmark(options[:n], profiling).to_f
count = sandbox.request_count.to_i
puts '%.2f sec, %d requests, %d req/sec' % [elapsed, count, count / elapsed]
end
def warmup(sandbox)
- Benchmark.realtime { sandbox.run }
+ Benchmark.realtime { sandbox.run(false) }
end
def default_options
@@ -136,6 +144,7 @@ module ActionController
protected
def load_ruby_prof
begin
+ gem 'ruby-prof', '>= 0.6.1'
require 'ruby-prof'
if mode = options[:measure]
RubyProf.measure_mode = RubyProf.const_get(mode.upcase)
diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb
index 26f75780c1..9fb1f9fa39 100644
--- a/actionpack/lib/action_controller/resources.rb
+++ b/actionpack/lib/action_controller/resources.rb
@@ -191,7 +191,7 @@ module ActionController
# end
# end
#
- # Along with the routes themselves, #resources generates named routes for use in
+ # Along with the routes themselves, +resources+ generates named routes for use in
# controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers:
#
# Named Route Helpers
@@ -208,7 +208,7 @@ module ActionController
# edit_message edit_message_url(id), hash_for_edit_message_url(id),
# edit_message_path(id), hash_for_edit_message_path(id)
#
- # You can use these helpers instead of #url_for or methods that take #url_for parameters. For example:
+ # You can use these helpers instead of +url_for+ or methods that take +url_for+ parameters. For example:
#
# redirect_to :controller => 'messages', :action => 'index'
# # and
@@ -406,7 +406,7 @@ module ActionController
# end
# end
#
- # Along with the routes themselves, #resource generates named routes for
+ # Along with the routes themselves, +resource+ generates named routes for
# use in controllers and views. <tt>map.resource :account</tt> produces
# these named routes and helpers:
#
diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb
index b1a98d1a51..4740113ed0 100644
--- a/actionpack/lib/action_controller/routing/builder.rb
+++ b/actionpack/lib/action_controller/routing/builder.rb
@@ -23,9 +23,9 @@ module ActionController
# Accepts a "route path" (a string defining a route), and returns the array
# of segments that corresponds to it. Note that the segment array is only
# partially initialized--the defaults and requirements, for instance, need
- # to be set separately, via the #assign_route_options method, and the
- # #optional? method for each segment will not be reliable until after
- # #assign_route_options is called, as well.
+ # to be set separately, via the +assign_route_options+ method, and the
+ # <tt>optional?</tt> method for each segment will not be reliable until after
+ # +assign_route_options+ is called, as well.
def segments_for_route_path(path)
rest, segments = path, []
diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb
index b142d18b47..864e068004 100644
--- a/actionpack/lib/action_controller/routing/segments.rb
+++ b/actionpack/lib/action_controller/routing/segments.rb
@@ -249,7 +249,7 @@ module ActionController
end
def extract_value
- "#{local_name} = hash[:#{key}] && hash[:#{key}].collect { |path_component| URI.escape(path_component, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
+ "#{local_name} = hash[:#{key}] && hash[:#{key}].collect { |path_component| URI.escape(path_component.to_param, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
end
def default
diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb
index ada1862c3e..b477c1f7da 100644
--- a/actionpack/lib/action_controller/session/cookie_store.rb
+++ b/actionpack/lib/action_controller/session/cookie_store.rb
@@ -34,7 +34,7 @@ require 'openssl' # to generate the HMAC message digest
# such as 'MD5', 'RIPEMD160', 'SHA256', etc.
#
# To generate a secret key for an existing application, run
-# `rake secret` and set the key in config/environment.rb.
+# "rake secret" and set the key in config/environment.rb.
#
# Note that changing digest or secret invalidates all existing sessions!
class CGI::Session::CookieStore
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 77c6f26eac..f26e65ba34 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -15,6 +15,27 @@ module ActionController
end
end
+ # Superclass for Action Controller functional tests. Infers the controller under test from the test class name,
+ # and creates @controller, @request, @response instance variables.
+ #
+ # class WidgetsControllerTest < ActionController::TestCase
+ # def test_index
+ # get :index
+ # end
+ # end
+ #
+ # * @controller - WidgetController.new
+ # * @request - ActionController::TestRequest.new
+ # * @response - ActionController::TestResponse.new
+ #
+ # (Earlier versions of Rails required each functional test to subclass Test::Unit::TestCase and define
+ # @controller, @request, @response in +setup+.)
+ #
+ # If the controller cannot be inferred from the test class name, you can explicity set it with +tests+.
+ #
+ # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
+ # tests WidgetController
+ # end
class TestCase < ActiveSupport::TestCase
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
# (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
@@ -41,6 +62,8 @@ module ActionController
@@controller_class = nil
class << self
+ # Sets the controller class name. Useful if the name can't be inferred from test class.
+ # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
def tests(controller_class)
self.controller_class = controller_class
end
diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb
index dcb6cdf4ca..f179d9b1c7 100644
--- a/actionpack/lib/action_controller/test_process.rb
+++ b/actionpack/lib/action_controller/test_process.rb
@@ -3,7 +3,7 @@ require 'action_controller/test_case'
module ActionController #:nodoc:
class Base
- # Process a test request called with a +TestRequest+ object.
+ # Process a test request called with a TestRequest object.
def self.process_test(request)
new.process_test(request)
end
@@ -49,7 +49,7 @@ module ActionController #:nodoc:
# Either the RAW_POST_DATA environment variable or the URL-encoded request
# parameters.
def raw_post
- env['RAW_POST_DATA'] ||= url_encoded_request_parameters
+ env['RAW_POST_DATA'] ||= returning(url_encoded_request_parameters) { |b| b.force_encoding(Encoding::BINARY) if b.respond_to?(:force_encoding) }
end
def port=(number)
@@ -222,7 +222,7 @@ module ActionController #:nodoc:
!rendered_file.nil?
end
- # A shortcut to the flash. Returns an empyt hash if no session flash exists.
+ # A shortcut to the flash. Returns an empty hash if no session flash exists.
def flash
session['flash'] || {}
end
@@ -340,6 +340,7 @@ module ActionController #:nodoc:
@content_type = content_type
@original_filename = path.sub(/^.*#{File::SEPARATOR}([^#{File::SEPARATOR}]+)$/) { $1 }
@tempfile = Tempfile.new(@original_filename)
+ @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
@tempfile.binmode if binary
FileUtils.copy_file(path, @tempfile.path)
end
@@ -357,7 +358,7 @@ module ActionController #:nodoc:
module TestProcess
def self.included(base)
- # execute the request simulating a specific http method and set/volley the response
+ # execute the request simulating a specific HTTP method and set/volley the response
%w( get post put delete head ).each do |method|
base.class_eval <<-EOV, __FILE__, __LINE__
def #{method}(action, parameters = nil, session = nil, flash = nil)
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 1a3c770254..376bb87409 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
@@ -64,7 +64,7 @@ module HTML
#
# When using a combination of the above, the element name comes first
# followed by identifier, class names, attributes, pseudo classes and
- # negation in any order. Do not seprate these parts with spaces!
+ # negation in any order. Do not separate these parts with spaces!
# Space separation is used for descendant selectors.
#
# For example:
@@ -158,7 +158,7 @@ module HTML
# * <tt>:not(selector)</tt> -- Match the element only if the element does not
# match the simple selector.
#
- # As you can see, <tt>:nth-child<tt> pseudo class and its varient can get quite
+ # As you can see, <tt>:nth-child<tt> pseudo class and its variant can get quite
# tricky and the CSS specification doesn't do a much better job explaining it.
# But after reading the examples and trying a few combinations, it's easy to
# figure out.
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index 70fc1ced8c..c67654d9a8 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -1,8 +1,8 @@
module ActionPack #:nodoc:
module VERSION #:nodoc:
MAJOR = 2
- MINOR = 0
- TINY = 991
+ MINOR = 1
+ TINY = 0
STRING = [MAJOR, MINOR, TINY].join('.')
end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 4840b2526d..c236666dcd 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -168,12 +168,12 @@ module ActionView #:nodoc:
# Specify whether file modification times should be checked to see if a template needs recompilation
@@cache_template_loading = false
cattr_accessor :cache_template_loading
-
- # Specify whether file extension lookup should be cached, and whether template base path lookup should be cached.
- # Should be +false+ for development environments. Defaults to +true+.
- @@cache_template_extensions = true
- cattr_accessor :cache_template_extensions
+ def self.cache_template_extensions=(*args)
+ ActiveSupport::Deprecation.warn("config.action_view.cache_template_extensions option has been deprecated and has no effect. " <<
+ "Please remove it from your config files.", caller)
+ end
+
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
@@debug_rjs = false
diff --git a/actionpack/lib/action_view/compiled_templates.rb b/actionpack/lib/action_view/compiled_templates.rb
deleted file mode 100644
index 5a286432e3..0000000000
--- a/actionpack/lib/action_view/compiled_templates.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-module ActionView
-
- # CompiledTemplates modules hold methods that have been compiled.
- # Templates are compiled into these methods so that they do not need to be
- # read and parsed for each request.
- #
- # Each template may be compiled into one or more methods. Each method accepts a given
- # set of parameters which is used to implement local assigns passing.
- #
- # To use a compiled template module, create a new instance and include it into the class
- # in which you want the template to be rendered.
- class CompiledTemplates < Module
- attr_reader :method_names
-
- def initialize
- @method_names = Hash.new do |hash, key|
- hash[key] = "__compiled_method_#{(hash.length + 1)}"
- end
- @mtimes = {}
- end
-
- # Return the full key for the given identifier and argument names
- def full_key(identifier, arg_names)
- [identifier, arg_names]
- end
-
- # Return the selector for this method or nil if it has not been compiled
- def selector(identifier, arg_names)
- key = full_key(identifier, arg_names)
- method_names.key?(key) ? method_names[key] : nil
- end
- alias :compiled? :selector
-
- # Return the time at which the method for the given identifier and argument names was compiled.
- def mtime(identifier, arg_names)
- @mtimes[full_key(identifier, arg_names)]
- end
-
- # Compile the provided source code for the given argument names and with the given initial line number.
- # The identifier should be unique to this source.
- #
- # The file_name, if provided will appear in backtraces. If not provided, the file_name defaults
- # to the identifier.
- #
- # This method will return the selector for the compiled version of this method.
- def compile_source(identifier, arg_names, source, initial_line_number = 0, file_name = nil)
- file_name ||= identifier
- name = method_names[full_key(identifier, arg_names)]
- arg_desc = arg_names.empty? ? '' : "(#{arg_names * ', '})"
- fake_file_name = "#{file_name}#{arg_desc}" # Include the arguments for this version (for now)
-
- method_def = wrap_source(name, arg_names, source)
-
- begin
- module_eval(method_def, fake_file_name, initial_line_number)
- @mtimes[full_key(identifier, arg_names)] = Time.now
- rescue Exception => e # errors from compiled source
- e.blame_file! identifier
- raise
- end
- name
- end
-
- # Wrap the provided source in a def ... end block.
- def wrap_source(name, arg_names, source)
- "def #{name}(#{arg_names * ', '})\n#{source}\nend"
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb
index f3f204cc97..e788ebf359 100644
--- a/actionpack/lib/action_view/helpers/active_record_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_record_helper.rb
@@ -141,7 +141,7 @@ module ActionView
#
# error_messages_for 'user_common', 'user', :object_name => 'user'
#
- # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> paremeter 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):
#
# error_messages_for 'user', :object => @question.user
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index dfc7e2b3ed..e5a95a961c 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -11,8 +11,8 @@ module ActionView
# === Using asset hosts
# By default, Rails links to these assets on the current host in the public
# folder, but you can direct Rails to link to assets from a dedicated assets server by
- # setting ActionController::Base.asset_host in your environment.rb. For example,
- # let's say your asset host is assets.example.com.
+ # setting ActionController::Base.asset_host in your <tt>config/environment.rb</tt>. For example,
+ # let's say your asset host is <tt>assets.example.com</tt>.
#
# ActionController::Base.asset_host = "assets.example.com"
# image_tag("rails.png")
@@ -22,8 +22,8 @@ module ActionView
#
# This is useful since browsers typically open at most two connections to a single host,
# which means your assets often wait in single file for their turn to load. You can
- # alleviate this by using a %d wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com")
- # to automatically distribute asset requests among four hosts (e.g., assets0.example.com through assets3.example.com)
+ # alleviate this by using a <tt>%d</tt> wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com")
+ # to automatically distribute asset requests among four hosts (e.g., "assets0.example.com" through "assets3.example.com")
# so browsers will open eight connections rather than two.
#
# image_tag("rails.png")
@@ -293,9 +293,9 @@ module ActionView
end
# Computes the path to a stylesheet asset in the public stylesheets directory.
- # If the +source+ filename has no extension, .css will be appended.
+ # If the +source+ filename has no extension, <tt>.css</tt> will be appended.
# Full paths from the document root will be passed through.
- # Used internally by stylesheet_link_tag to build the stylesheet path.
+ # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
#
# ==== Examples
# stylesheet_path "style" # => /stylesheets/style.css
@@ -309,7 +309,7 @@ module ActionView
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
# Returns a stylesheet link tag for the sources specified as arguments. If
- # you don't specify an extension, .css will be appended automatically.
+ # you don't specify an extension, <tt>.css</tt> will be appended automatically.
# You can modify the link attributes by passing a hash as the last argument.
#
# ==== Examples
@@ -379,7 +379,7 @@ module ActionView
# Computes the path to an image asset in the public images directory.
# Full paths from the document root will be passed through.
- # Used internally by image_tag to build the image path.
+ # Used internally by +image_tag+ to build the image path.
#
# ==== Examples
# image_path("edit") # => /images/edit
@@ -454,8 +454,8 @@ module ActionView
end
end
- # Add the .ext if not present. Return full URLs otherwise untouched.
- # Prefix with /dir/ if lacking a leading /. Account for relative URL
+ # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
+ # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
# roots. Rewrite the asset path for cache-busting asset ids. Include
# asset host, if configured, with the correct request protocol.
def compute_public_path(source, dir, ext = nil, include_host = true)
@@ -502,9 +502,9 @@ module ActionView
end
end
- # Pick an asset host for this source. Returns nil if no host is set,
+ # Pick an asset host for this source. Returns +nil+ if no host is set,
# the host if no wildcard is set, the host interpolated with the
- # numbers 0-3 if it contains %d (the number is the source hash mod 4),
+ # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
# or the value returned from invoking the proc if it's a proc.
def compute_asset_host(source)
if host = ActionController::Base.asset_host
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 8a9c8044ae..7ed6272898 100755
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -689,7 +689,7 @@ module ActionView
default[key] ||= time.send(key)
end
- Time.utc(default[:year], default[:month], default[:day], default[:hour], default[:min], default[:sec])
+ Time.utc_time(default[:year], default[:month], default[:day], default[:hour], default[:min], default[:sec])
end
end
end
diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb
index 20de7e465f..ea70a697de 100644
--- a/actionpack/lib/action_view/helpers/debug_helper.rb
+++ b/actionpack/lib/action_view/helpers/debug_helper.rb
@@ -2,21 +2,28 @@ module ActionView
module Helpers
# Provides a set of methods for making it easier to debug Rails objects.
module DebugHelper
- # Returns a <pre>-tag that has +object+ dumped by YAML. This creates a very
- # readable way to inspect an object.
+ # Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
+ # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
+ # Useful for inspecting an object at the time of rendering.
#
# ==== Example
- # my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]}
- # debug(my_hash)
#
- # => <pre class='debug_dump'>---
- # first: 1
- # second: two
- # third:
- # - 1
- # - 2
- # - 3
- # </pre>
+ # @user = User.new({ :username => 'testing', :password => 'xyz', :age => 42}) %>
+ # debug(@user)
+ # # =>
+ # <pre class='debug_dump'>--- !ruby/object:User
+ # attributes:
+ # &nbsp; updated_at:
+ # &nbsp; username: testing
+ #
+ # &nbsp; age: 42
+ # &nbsp; password: xyz
+ # &nbsp; created_at:
+ # attributes_cache: {}
+ #
+ # new_record: true
+ # </pre>
+
def debug(object)
begin
Marshal::dump(object)
@@ -28,4 +35,4 @@ module ActionView
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index b8600fe445..7d85799038 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -73,30 +73,81 @@ module ActionView
# 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.
#
- # <% form_for :person, @person, :url => { :action => "update" } do |f| %>
+ # Rails provides succinct resource-oriented form generation with +form_for+
+ # like this:
+ #
+ # <% form_for @offer do |f| %>
+ # <%= f.label :version, 'Version' %>:
+ # <%= f.text_field :version %><br />
+ # <%= f.label :author, 'Author' %>:
+ # <%= 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.
+ #
+ # === Generic form_for
+ #
+ # 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 %>
- # First name: <%= f.text_field :first_name %>
- # Last name : <%= f.text_field :last_name %>
- # Biography : <%= f.text_area :biography %>
- # Admin? : <%= f.check_box :admin %>
+ # First name: <%= f.text_field :first_name %><br />
+ # Last name : <%= f.text_field :last_name %><br />
+ # Biography : <%= f.text_area :biography %><br />
+ # Admin? : <%= f.check_box :admin %><br />
# <% end %>
#
- # 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 worth noting is that form_for yields a <tt>form_builder</tt> object, in this example as <tt>f</tt>, which emulates
- # the API for the stand-alone FormHelper methods, but without the object name. So instead of <tt>text_field :person, :name</tt>,
- # you get away with <tt>f.text_field :name</tt>. Notice that you can even do <tt><%= f.error_messages %></tt> to display the
- # error messsages of the model object in question.
+ # 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.
+ #
+ # The form builder acts as a regular form helper that somehow carries the
+ # model. Thus, the idea is that
+ #
+ # <%= f.text_field :first_name %>
+ #
+ # gets expanded to
#
- # Even further, the form_for method allows you to more easily escape the instance variable convention. So while the stand-alone
- # approach would require <tt>text_field :person, :name, :object => person</tt>
- # to work with local variables instead of instance ones, the form_for calls remain the same. You simply declare once with
- # <tt>:person, person</tt> and all subsequent field calls save <tt>:person</tt> and <tt>:object => person</tt>.
+ # <%= text_field :person, :first_name %>
+ #
+ # If the instance variable is not <tt>@person</tt> you can pass the actual
+ # record as the second argument:
+ #
+ # <% form_for :person, person, :url => { :action => "update" } do |f| %>
+ # ...
+ # <% end %>
#
- # 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:
+ # In that case you can think
+ #
+ # <%= f.text_field :first_name %>
+ #
+ # gets expanded to
+ #
+ # <%= text_field :person, :first_name, :object => person %>
+ #
+ # You can even display error messages of the wrapped model this way:
+ #
+ # <%= f.error_messages %>
+ #
+ # 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>: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>.
+ #
+ # 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:
#
# <% form_for :person, @person, :url => { :action => "update" } do |f| %>
# First name: <%= f.text_field :first_name %>
@@ -105,42 +156,38 @@ module ActionView
# Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %>
# <% end %>
#
- # Note: 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.
- #
- # HTML attributes for the form tag can be given as <tt>:html => {...}</tt>. For example:
- #
- # <% form_for :person, @person, :html => {:id => 'person_form'} do |f| %>
- # ...
- # <% 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.
#
- # The above form will then have the <tt>id</tt> attribute with the value </tt>person_form</tt>, which you can then
- # style with CSS or manipulate with JavaScript.
+ # === Resource-oriented style
#
- # === Relying on record identification
+ # 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.
#
- # In addition to manually configuring the form_for call, you can also rely on record identification, which will use
- # the conventions and named routes of that approach. Examples:
+ # For example, if <tt>@post</tt> is an existing record you want to edit
#
- # <% form_for(@post) do |f| %>
+ # <% form_for @post do |f| %>
# ...
# <% end %>
#
- # This will expand to be the same as:
+ # is equivalent to something like:
#
# <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
# ...
# <% end %>
#
- # And for new records:
+ # And for new records
#
# <% form_for(Post.new) do |f| %>
# ...
# <% end %>
#
- # This will expand to be the same as:
+ # expands to
#
- # <% form_for :post, @post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
+ # <% form_for :post, Post.new, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
# ...
# <% end %>
#
@@ -150,7 +197,7 @@ module ActionView
# ...
# <% end %>
#
- # And for namespaced routes, like admin_post_url:
+ # And for namespaced routes, like +admin_post_url+:
#
# <% form_for([:admin, @post]) do |f| %>
# ...
@@ -277,13 +324,13 @@ module ActionView
#
# ==== Examples
# label(:post, :title)
- # #=> <label for="post_title">Title</label>
+ # # => <label for="post_title">Title</label>
#
# label(:post, :title, "A short title")
- # #=> <label for="post_title">A short title</label>
+ # # => <label for="post_title">A short title</label>
#
# label(:post, :title, "A short title", :class => "title_label")
- # #=> <label for="post_title" class="title_label">A short title</label>
+ # # => <label for="post_title" class="title_label">A short title</label>
#
def label(object_name, method, text = nil, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_label_tag(text, options)
@@ -588,6 +635,8 @@ module ActionView
value != 0
when String
value == checked_value
+ when Array
+ value.include?(checked_value)
else
value.to_i != 0
end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index bf65fe5574..c0cba24be4 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -119,7 +119,7 @@ module ActionView
# end
# end
#
- # Sample usage (selecting the associated +Author+ for an instance of +Post+, <tt>@post</tt>):
+ # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
# collection_select(:post, :author_id, Author.find(:all), :id, :name_with_initial, {:prompt => true})
#
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
@@ -144,10 +144,16 @@ module ActionView
# In addition to the <tt>:include_blank</tt> option documented above,
# this method also supports a <tt>:model</tt> option, which defaults
# to TimeZone. This may be used by users to specify a different time
- # zone model object. (See #time_zone_options_for_select for more
+ # zone model object. (See +time_zone_options_for_select+ for more
# information.)
+ #
+ # You can also supply an array of TimeZone objects
+ # as +priority_zones+, so that they will be listed above the rest of the
+ # (long) list. (You can use TimeZone.us_zones as a convenience for
+ # obtaining a list of the US time zones.)
+ #
# Finally, this method supports a <tt>:default</tt> option, which selects
- # a default TimeZone if the object's time zone is nil.
+ # a default TimeZone if the object's time zone is +nil+.
#
# Examples:
# time_zone_select( "user", "time_zone", nil, :include_blank => true)
@@ -156,6 +162,8 @@ module ActionView
#
# time_zone_select( "user", 'time_zone', TimeZone.us_zones, :default => "Pacific Time (US & Canada)")
#
+ # time_zone_select( "user", 'time_zone', [ TimeZone['Alaska'], TimeZone['Hawaii'] ])
+ #
# time_zone_select( "user", "time_zone", TZInfo::Timezone.all.sort, :model => TZInfo::Timezone)
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
InstanceTag.new(object, method, self, nil, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
@@ -164,7 +172,7 @@ module ActionView
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
# the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
- # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +Selected+
+ # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
# may also be an array of values to be selected when using a multiple select.
#
# Examples (call, result):
@@ -209,24 +217,22 @@ module ActionView
options_for_select(options, selected)
end
- # Returns a string of <tt><option></tt> tags, like <tt>#options_from_collection_for_select</tt>, but
+ # Returns a string of <tt><option></tt> tags, like <tt>options_from_collection_for_select</tt>, but
# groups them by <tt><optgroup></tt> tags based on the object relationships of the arguments.
#
# Parameters:
- # +collection+:: An array of objects representing the <tt><optgroup></tt> tags
- # +group_method+:: The name of a method which, when called on a member of +collection+, returns an
- # array of child objects representing the <tt><option></tt> tags
- # +group_label_method+:: The name of a method which, when called on a member of +collection+, returns a
- # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag
- # +option_key_method+:: The name of a method which, when called on a child object of a member of
- # +collection+, returns a value to be used as the +value+ attribute for its
- # <tt><option></tt> tag
- # +option_value_method+:: The name of a method which, when called on a child object of a member of
- # +collection+, returns a value to be used as the contents of its
- # <tt><option></tt> tag
- # +selected_key+:: A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
- # which will have the +selected+ attribute set. Corresponds to the return value
- # of one of the calls to +option_key_method+. If +nil+, no selection is made.
+ # * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
+ # * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
+ # array of child objects representing the <tt><option></tt> tags.
+ # * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
+ # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
+ # * +option_key_method+ - The name of a method which, when called on a child object of a member of
+ # +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
+ # * +option_value_method+ - The name of a method which, when called on a child object of a member of
+ # +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
+ # * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
+ # which will have the +selected+ attribute set. Corresponds to the return value of one of the calls
+ # to +option_key_method+. If +nil+, no selection is made.
#
# Example object structure for use with this method:
# class Continent < ActiveRecord::Base
@@ -265,9 +271,11 @@ module ActionView
end
end
- # Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to
- # have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so
- # that they will be listed above the rest of the (long) list.
+ # Returns a string of option tags for most countries in the
+ # world (as defined in COUNTRIES). Supply a country name as
+ # +selected+ to have it marked as the selected option tag. You
+ # can also supply an array of countries as +priority_countries+,
+ # so that they will be listed above the rest of the (long) list.
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
def country_options_for_select(selected = nil, priority_countries = nil)
@@ -292,8 +300,8 @@ module ActionView
# a TimeZone.
#
# By default, +model+ is the TimeZone constant (which can be obtained
- # in ActiveRecord as a value object). The only requirement is that the
- # +model+ parameter be an object that responds to #all, and returns
+ # in Active Record as a value object). The only requirement is that the
+ # +model+ parameter be an object that responds to +all+, and returns
# an array of objects that represent time zones.
#
# NOTE: Only the option tags are returned, you have to wrap this call in
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 922a0662fe..ca58f4ba26 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -3,7 +3,7 @@ require 'action_view/helpers/tag_helper'
module ActionView
module Helpers
- # Provides a number of methods for creating form tags that doesn't rely on an ActiveRecord object assigned to the template like
+ # Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like
# FormHelper does. Instead, you provide the names and values manually.
#
# NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
@@ -14,9 +14,9 @@ module ActionView
#
# ==== Options
# * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
- # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
- # If "put", "delete", or another verb is used, a hidden input with name _method
- # is added to simulate the verb over post.
+ # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
+ # If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
+ # is added to simulate the verb over post.
# * A list of parameters to feed to the URL the form will be posted to.
#
# ==== Examples
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 1ea3cbd74e..ed931e064f 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -43,14 +43,23 @@ module ActionView
end
include PrototypeHelper
-
- # Returns a link that will trigger a JavaScript +function+ using the
+
+ # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the
# onclick handler and return false after the fact.
#
+ # The first argument +name+ is used as the link text.
+ #
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ #
# The +function+ argument can be omitted in favor of an +update_page+
# block, which evaluates to a string when the template is rendered
# (instead of making an Ajax request first).
#
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ #
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+ #
+ #
# Examples:
# link_to_function "Greeting", "alert('Hello world!')"
# Produces:
@@ -94,13 +103,21 @@ module ActionView
)
end
- # Returns a button that'll trigger a JavaScript +function+ using the
+ # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
# onclick handler.
#
+ # The first argument +name+ is used as the button's value or display text.
+ #
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ #
# The +function+ argument can be omitted in favor of an +update_page+
# block, which evaluates to a string when the template is rendered
# (instead of making an Ajax request first).
#
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ #
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+ #
# Examples:
# button_to_function "Greeting", "alert('Hello world!')"
# button_to_function "Delete", "if (confirm('Really?')) do_delete()"
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 1a0e660d52..c731824f18 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -61,7 +61,7 @@ module ActionView
#
# == Designing your Rails actions for Ajax
# When building your action handlers (that is, the Rails actions that receive your background requests), it's
- # important to remember a few things. First, whatever your action would normall return to the browser, it will
+ # important to remember a few things. First, whatever your action would normally return to the browser, it will
# return to the Ajax call. As such, you typically don't want to render with a layout. This call will cause
# the layout to be transmitted back to your page, and, if you have a full HTML/CSS, will likely mess a lot of things up.
# You can turn the layout off on particular actions by doing the following:
@@ -595,8 +595,8 @@ module ActionView
# JavaScript sent with a Content-type of "text/javascript".
#
# Create new instances with PrototypeHelper#update_page or with
- # ActionController::Base#render, then call #insert_html, #replace_html,
- # #remove, #show, #hide, #visual_effect, or any other of the built-in
+ # ActionController::Base#render, then call +insert_html+, +replace_html+,
+ # +remove+, +show+, +hide+, +visual_effect+, or any other of the built-in
# methods on the yielded generator in any order you like to modify the
# content and appearance of the current page.
#
@@ -687,7 +687,7 @@ module ActionView
end
end
- # Returns an object whose <tt>#to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript
+ # Returns an object whose <tt>to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript
# expression as an argument to another JavaScriptGenerator method.
def literal(code)
ActiveSupport::JSON::Variable.new(code.to_s)
@@ -1068,7 +1068,7 @@ module ActionView
def build_observer(klass, name, options = {})
if options[:with] && (options[:with] !~ /[\{=(.]/)
- options[:with] = "'#{options[:with]}=' + value"
+ options[:with] = "'#{options[:with]}=' + encodeURIComponent(value)"
else
options[:with] ||= 'value' unless options[:function]
end
@@ -1173,7 +1173,7 @@ module ActionView
super(generator)
end
- # The JSON Encoder calls this to check for the #to_json method
+ # The JSON Encoder calls this to check for the +to_json+ method
# Since it's a blank slate object, I suppose it responds to anything.
def respond_to?(method)
true
diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb
index 40b66be79f..66c596f3a9 100644
--- a/actionpack/lib/action_view/helpers/record_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb
@@ -2,7 +2,7 @@ module ActionView
module Helpers
module RecordTagHelper
# Produces a wrapper DIV element with id and class parameters that
- # relate to the specified ActiveRecord object. Usage example:
+ # relate to the specified Active Record object. Usage example:
#
# <% div_for(@person, :class => "foo") do %>
# <%=h @person.name %>
@@ -17,7 +17,7 @@ module ActionView
end
# content_tag_for creates an HTML element with id and class parameters
- # that relate to the specified ActiveRecord object. For example:
+ # that relate to the specified Active Record object. For example:
#
# <% content_tag_for(:tr, @person) do %>
# <td><%=h @person.first_name %></td>
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index 6c0a7ec25c..c3c03394ee 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -57,7 +57,7 @@ module ActionView
self.class.white_list_sanitizer.sanitize(html, options)
end
- # Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
+ # Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
def sanitize_css(style)
self.class.white_list_sanitizer.sanitize_css(style)
end
@@ -111,8 +111,8 @@ module ActionView
end
end
- # Gets the HTML::FullSanitizer instance used by strip_tags. Replace with
- # any object that responds to #sanitize
+ # Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with
+ # any object that responds to +sanitize+.
#
# Rails::Initializer.run do |config|
# config.action_view.full_sanitizer = MySpecialSanitizer.new
@@ -122,8 +122,8 @@ module ActionView
@full_sanitizer ||= HTML::FullSanitizer.new
end
- # Gets the HTML::LinkSanitizer instance used by strip_links. Replace with
- # any object that responds to #sanitize
+ # Gets the HTML::LinkSanitizer instance used by +strip_links+. Replace with
+ # any object that responds to +sanitize+.
#
# Rails::Initializer.run do |config|
# config.action_view.link_sanitizer = MySpecialSanitizer.new
@@ -133,8 +133,8 @@ module ActionView
@link_sanitizer ||= HTML::LinkSanitizer.new
end
- # Gets the HTML::WhiteListSanitizer instance used by sanitize and sanitize_css.
- # Replace with any object that responds to #sanitize
+ # Gets the HTML::WhiteListSanitizer instance used by sanitize and +sanitize_css+.
+ # Replace with any object that responds to +sanitize+.
#
# Rails::Initializer.run do |config|
# config.action_view.white_list_sanitizer = MySpecialSanitizer.new
@@ -144,7 +144,7 @@ module ActionView
@white_list_sanitizer ||= HTML::WhiteListSanitizer.new
end
- # Adds valid HTML attributes that the #sanitize helper checks for URIs.
+ # Adds valid HTML attributes that the +sanitize+ helper checks for URIs.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_uri_attributes = 'lowsrc', 'target'
@@ -154,7 +154,7 @@ module ActionView
HTML::WhiteListSanitizer.uri_attributes.merge(attributes)
end
- # Adds to the Set of 'bad' tags for the #sanitize helper.
+ # Adds to the Set of 'bad' tags for the +sanitize+ helper.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_bad_tags = 'embed', 'object'
@@ -163,7 +163,8 @@ module ActionView
def sanitized_bad_tags=(attributes)
HTML::WhiteListSanitizer.bad_tags.merge(attributes)
end
- # Adds to the Set of allowed tags for the #sanitize helper.
+
+ # Adds to the Set of allowed tags for the +sanitize+ helper.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
@@ -173,7 +174,7 @@ module ActionView
HTML::WhiteListSanitizer.allowed_tags.merge(attributes)
end
- # Adds to the Set of allowed html attributes for the #sanitize helper.
+ # Adds to the Set of allowed HTML attributes for the +sanitize+ helper.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_attributes = 'onclick', 'longdesc'
@@ -183,7 +184,7 @@ module ActionView
HTML::WhiteListSanitizer.allowed_attributes.merge(attributes)
end
- # Adds to the Set of allowed css properties for the #sanitize and #sanitize_css heleprs.
+ # Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_css_properties = 'expression'
@@ -193,7 +194,7 @@ module ActionView
HTML::WhiteListSanitizer.allowed_css_properties.merge(attributes)
end
- # Adds to the Set of allowed css keywords for the #sanitize and #sanitize_css helpers.
+ # Adds to the Set of allowed CSS keywords for the +sanitize+ and +sanitize_css+ helpers.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_css_keywords = 'expression'
@@ -203,7 +204,7 @@ module ActionView
HTML::WhiteListSanitizer.allowed_css_keywords.merge(attributes)
end
- # Adds to the Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers.
+ # Adds to the Set of allowed shorthand CSS properties for the +sanitize+ and +sanitize_css+ helpers.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_shorthand_css_properties = 'expression'
@@ -213,7 +214,7 @@ module ActionView
HTML::WhiteListSanitizer.shorthand_css_properties.merge(attributes)
end
- # Adds to the Set of allowed protocols for the #sanitize helper.
+ # Adds to the Set of allowed protocols for the +sanitize+ helper.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_protocols = 'ssh', 'feed'
diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
index 12b4cfd3f8..b938c1a801 100644
--- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
+++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
@@ -26,9 +26,9 @@ module ActionView
# :url => { :action => "reload" },
# :complete => visual_effect(:highlight, "posts", :duration => 0.5)
#
- # If no element_id is given, it assumes "element" which should be a local
+ # If no +element_id+ is given, it assumes "element" which should be a local
# variable in the generated JavaScript execution context. This can be
- # used for example with drop_receiving_element:
+ # used for example with +drop_receiving_element+:
#
# <%= drop_receiving_element (...), :loading => visual_effect(:fade) %>
#
@@ -67,6 +67,7 @@ module ActionView
# element as parameters.
#
# Example:
+ #
# <%= sortable_element("my_list", :url => { :action => "order" }) %>
#
# In the example, the action gets a "my_list" array parameter
@@ -79,60 +80,56 @@ module ActionView
#
# Additional +options+ are:
#
- # <tt>:format</tt>:: A regular expression to determine what to send
- # as the serialized id to the server (the default
- # is <tt>/^[^_]*_(.*)$/</tt>).
- #
- # <tt>:constraint</tt>:: Whether to constrain the dragging to either <tt>:horizontal</tt>
- # or <tt>:vertical</tt> (or false to make it unconstrained).
- #
- # <tt>:overlap</tt>:: Calculate the item overlap in the <tt>:horizontal</tt> or
- # <tt>:vertical</tt> direction.
- #
- # <tt>:tag</tt>:: Which children of the container element to treat as
- # sortable (default is <tt>li</tt>).
- #
- # <tt>:containment</tt>:: Takes an element or array of elements to treat as
- # potential drop targets (defaults to the original
- # target element).
- #
- # <tt>:only</tt>:: A CSS class name or arry of class names used to filter
- # out child elements as candidates.
- #
- # <tt>:scroll</tt>:: Determines whether to scroll the list during drag
- # operations if the list runs past the visual border.
- #
- # <tt>:tree</tt>:: Determines whether to treat nested lists as part of the
- # main sortable list. This means that you can create multi-
- # layer lists, and not only sort items at the same level,
- # but drag and sort items between levels.
- #
- # <tt>:hoverclass</tt>:: If set, the Droppable will have this additional CSS class
- # when an accepted Draggable is hovered over it.
- #
- # <tt>:handle</tt>:: Sets whether the element should only be draggable by an
- # embedded handle. The value may be a string referencing a
- # CSS class value (as of script.aculo.us V1.5). The first
- # child/grandchild/etc. element found within the element
- # that has this CSS class value will be used as the handle.
- #
- # <tt>:ghosting</tt>:: Clones the element and drags the clone, leaving the original
- # in place until the clone is dropped (default is <tt>false</tt>).
- #
- # <tt>:dropOnEmpty</tt>:: If set to true, the Sortable container will be made into
- # a Droppable, that can receive a Draggable (as according to
- # the containment rules) as a child element when there are no
- # more elements inside (default is <tt>false</tt>).
- #
- # <tt>:onChange</tt>:: Called whenever the sort order changes while dragging. When
- # dragging from one Sortable to another, the callback is
- # called once on each Sortable. Gets the affected element as
- # its parameter.
- #
- # <tt>:onUpdate</tt>:: Called when the drag ends and the Sortable's order is
- # changed in any way. When dragging from one Sortable to
- # another, the callback is called once on each Sortable. Gets
- # the container as its parameter.
+ # * <tt>:format</tt> - A regular expression to determine what to send as the
+ # serialized id to the server (the default is <tt>/^[^_]*_(.*)$/</tt>).
+ #
+ # * <tt>:constraint</tt> - Whether to constrain the dragging to either
+ # <tt>:horizontal</tt> or <tt>:vertical</tt> (or false to make it unconstrained).
+ #
+ # * <tt>:overlap</tt> - Calculate the item overlap in the <tt>:horizontal</tt>
+ # or <tt>:vertical</tt> direction.
+ #
+ # * <tt>:tag</tt> - Which children of the container element to treat as
+ # sortable (default is <tt>li</tt>).
+ #
+ # * <tt>:containment</tt> - Takes an element or array of elements to treat as
+ # potential drop targets (defaults to the original target element).
+ #
+ # * <tt>:only</tt> - A CSS class name or arry of class names used to filter
+ # out child elements as candidates.
+ #
+ # * <tt>:scroll</tt> - Determines whether to scroll the list during drag
+ # operations if the list runs past the visual border.
+ #
+ # * <tt>:tree</tt> - Determines whether to treat nested lists as part of the
+ # main sortable list. This means that you can create multi-layer lists,
+ # and not only sort items at the same level, but drag and sort items
+ # between levels.
+ #
+ # * <tt>:hoverclass</tt> - If set, the Droppable will have this additional CSS class
+ # when an accepted Draggable is hovered over it.
+ #
+ # * <tt>:handle</tt> - Sets whether the element should only be draggable by an
+ # embedded handle. The value may be a string referencing a CSS class value
+ # (as of script.aculo.us V1.5). The first child/grandchild/etc. element
+ # found within the element that has this CSS class value will be used as
+ # the handle.
+ #
+ # * <tt>:ghosting</tt> - Clones the element and drags the clone, leaving
+ # the original in place until the clone is dropped (default is <tt>false</tt>).
+ #
+ # * <tt>:dropOnEmpty</tt> - If true the Sortable container will be made into
+ # a Droppable, that can receive a Draggable (as according to the containment
+ # rules) as a child element when there are no more elements inside (default
+ # is <tt>false</tt>).
+ #
+ # * <tt>:onChange</tt> - Called whenever the sort order changes while dragging. When
+ # dragging from one Sortable to another, the callback is called once on each
+ # Sortable. Gets the affected element as its parameter.
+ #
+ # * <tt>:onUpdate</tt> - Called when the drag ends and the Sortable's order is
+ # changed in any way. When dragging from one Sortable to another, the callback
+ # is called once on each Sortable. Gets the container as its parameter.
#
# See http://script.aculo.us for more documentation.
def sortable_element(element_id, options = {})
@@ -170,8 +167,8 @@ module ActionView
end
# Makes the element with the DOM ID specified by +element_id+ receive
- # dropped draggable elements (created by draggable_element).
- # and make an AJAX call By default, the action called gets the DOM ID
+ # dropped draggable elements (created by +draggable_element+).
+ # and make an AJAX call. By default, the action called gets the DOM ID
# of the element as parameter.
#
# Example:
@@ -182,32 +179,30 @@ module ActionView
# http://script.aculo.us for more documentation.
#
# Some of these +options+ include:
- # <tt>:accept</tt>:: Set this to a string or an array of strings describing the
- # allowable CSS classes that the draggable_element must have in order
- # to be accepted by this drop_receiving_element.
- #
- # <tt>:confirm</tt>:: Adds a confirmation dialog.
- #
- # Example:
- # :confirm => "Are you sure you want to do this?"
- #
- # <tt>:hoverclass</tt>:: If set, the drop_receiving_element will have this additional CSS class
- # when an accepted draggable_element is hovered over it.
- #
- # <tt>:onDrop</tt>:: Called when a draggable_element is dropped onto this element.
- # Override this callback with a javascript expression to
- # change the default drop behavour.
- #
- # Example:
- # :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }"
- #
- # This callback gets three parameters:
- # The +Draggable+ element, the +Droppable+ element and the
- # +Event+ object. You can extract additional information about the
- # drop - like if the Ctrl or Shift keys were pressed - from the +Event+ object.
- #
- # <tt>:with</tt>:: A JavaScript expression specifying the parameters for the XMLHttpRequest.
- # Any expressions should return a valid URL query string.
+ # * <tt>:accept</tt> - Set this to a string or an array of strings describing the
+ # allowable CSS classes that the +draggable_element+ must have in order
+ # to be accepted by this +drop_receiving_element+.
+ #
+ # * <tt>:confirm</tt> - Adds a confirmation dialog. Example:
+ #
+ # :confirm => "Are you sure you want to do this?"
+ #
+ # * <tt>:hoverclass</tt> - If set, the +drop_receiving_element+ will have
+ # this additional CSS class when an accepted +draggable_element+ is
+ # hovered over it.
+ #
+ # * <tt>:onDrop</tt> - Called when a +draggable_element+ is dropped onto
+ # this element. Override this callback with a JavaScript expression to
+ # change the default drop behaviour. Example:
+ #
+ # :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }"
+ #
+ # This callback gets three parameters: The Draggable element, the Droppable
+ # element and the Event object. You can extract additional information about
+ # the drop - like if the Ctrl or Shift keys were pressed - from the Event object.
+ #
+ # * <tt>:with</tt> - A JavaScript expression specifying the parameters for
+ # the XMLHttpRequest. Any expressions should return a valid URL query string.
def drop_receiving_element(element_id, options = {})
javascript_tag(drop_receiving_element_js(element_id, options).chop!)
end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 9d220c546a..669a285424 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -464,11 +464,11 @@ module ActionView
[-\w]+ # subdomain or domain
(?:\.[-\w]+)* # remaining subdomains or domain
(?::\d+)? # port
- (?:/(?:(?:[~\w\+@%=-]|(?:[,.;:][^\s$]))+)?)* # path
+ (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:][^\s$]))+)?)* # path
(?:\?[\w\+@%&=.;-]+)? # query string
(?:\#[\w\-]*)? # trailing anchor
)
- ([[:punct:]]|\s|<|$) # trailing text
+ ([[:punct:]]|<|$|) # trailing text
}x unless const_defined?(:AUTO_LINK_RE)
# Turns all urls into clickable links. If a block is given, each url
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 38c8d18cb0..7bb189420b 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -10,7 +10,7 @@ module ActionView
include JavaScriptHelper
# Returns the URL for the set of +options+ provided. This takes the
- # same options as url_for in ActionController (see the
+ # same options as +url_for+ in Action Controller (see the
# documentation for ActionController::Base#url_for). Note that by default
# <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative /controller/action
# instead of the fully qualified URL like http://example.com/controller/action.
@@ -173,7 +173,7 @@ module ActionView
# link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
# # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
#
- # The three options specfic to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
+ # The three options specific to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
#
# link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
# # => <a href="http://www.rubyonrails.org/" onclick="return confirm('Are you sure?');">Visit Other Site</a>
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 2cda3d94b5..369526188f 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -93,9 +93,9 @@ module ActionView #:nodoc:
# Register a class that knows how to handle template files with the given
# extension. This can be used to implement new template types.
# The constructor for the class must take the ActiveView::Base instance
- # as a parameter, and the class must implement a #render method that
+ # as a parameter, and the class must implement a +render+ method that
# takes the contents of the template to render as well as the Hash of
- # local assigns available to the template. The #render method ought to
+ # local assigns available to the template. The +render+ method ought to
# return the rendered template as a string.
def self.register_template_handler(extension, klass)
@@template_handlers[extension.to_sym] = klass
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index b2e6589d81..16fedd9732 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -1,14 +1,6 @@
require 'active_support/test_case'
module ActionView
- class NonInferrableHelperError < ActionViewError
- def initialize(name)
- super "Unable to determine the helper to test from #{name}. " +
- "You'll need to specify it using tests YourHelper in your " +
- "test case definition"
- end
- end
-
class TestCase < ActiveSupport::TestCase
class_inheritable_accessor :helper_class
@@helper_class = nil
@@ -29,7 +21,7 @@ module ActionView
def determine_default_helper_class(name)
name.sub(/Test$/, '').constantize
rescue NameError
- raise NonInferrableHelperError.new(name)
+ nil
end
end
@@ -42,7 +34,9 @@ module ActionView
setup :setup_with_helper_class
def setup_with_helper_class
- self.class.send(:include, helper_class)
+ if helper_class && !self.class.ancestors.include?(helper_class)
+ self.class.send(:include, helper_class)
+ end
end
class TestController < ActionController::Base
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 1db057580b..f152b1d19c 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -131,6 +131,10 @@ class AssertResponseWithUnexpectedErrorController < ActionController::Base
def index
raise 'FAIL'
end
+
+ def show
+ render :text => "Boom", :status => 500
+ end
end
module Admin
@@ -483,6 +487,16 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
rescue Test::Unit::AssertionFailedError => e
assert e.message.include?('FAIL')
end
+
+ def test_assert_response_failure_response_with_no_exception
+ @controller = AssertResponseWithUnexpectedErrorController.new
+ get :show
+ assert_response :success
+ flunk 'Expected non-success response'
+ rescue Test::Unit::AssertionFailedError
+ rescue
+ flunk "assert_response failed to handle failure response with missing, but optional, exception."
+ end
end
class ActionPackHeaderTest < Test::Unit::TestCase
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 4aacb4a78a..f9b6b87bc6 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -232,7 +232,7 @@ class ActionCacheTest < Test::Unit::TestCase
get :index
cached_time = content_to_cache
assert_equal cached_time, @response.body
- assert_cache_exists 'hostname.com/action_caching_test'
+ assert fragment_exist?('hostname.com/action_caching_test')
reset!
get :index
@@ -243,7 +243,7 @@ class ActionCacheTest < Test::Unit::TestCase
get :destroy
cached_time = content_to_cache
assert_equal cached_time, @response.body
- assert_cache_does_not_exist 'hostname.com/action_caching_test/destroy'
+ assert !fragment_exist?('hostname.com/action_caching_test/destroy')
reset!
get :destroy
@@ -254,7 +254,7 @@ class ActionCacheTest < Test::Unit::TestCase
get :with_layout
cached_time = content_to_cache
assert_not_equal cached_time, @response.body
- assert_cache_exists 'hostname.com/action_caching_test/with_layout'
+ assert fragment_exist?('hostname.com/action_caching_test/with_layout')
reset!
get :with_layout
@@ -266,14 +266,14 @@ class ActionCacheTest < Test::Unit::TestCase
def test_action_cache_conditional_options
@request.env['HTTP_ACCEPT'] = 'application/json'
get :index
- assert_cache_does_not_exist 'hostname.com/action_caching_test'
+ assert !fragment_exist?('hostname.com/action_caching_test')
end
def test_action_cache_with_custom_cache_path
get :show
cached_time = content_to_cache
assert_equal cached_time, @response.body
- assert_cache_exists 'test.host/custom/show'
+ assert fragment_exist?('test.host/custom/show')
reset!
get :show
@@ -282,11 +282,11 @@ class ActionCacheTest < Test::Unit::TestCase
def test_action_cache_with_custom_cache_path_in_block
get :edit
- assert_cache_exists 'test.host/edit'
+ assert fragment_exist?('test.host/edit')
reset!
get :edit, :id => 1
- assert_cache_exists 'test.host/1;edit'
+ assert fragment_exist?('test.host/1;edit')
end
def test_cache_expiration
@@ -395,18 +395,8 @@ class ActionCacheTest < Test::Unit::TestCase
@request.host = 'hostname.com'
end
- def assert_cache_exists(path)
- full_path = cache_path(path)
- assert File.exist?(full_path), "#{full_path.inspect} does not exist."
- end
-
- def assert_cache_does_not_exist(path)
- full_path = cache_path(path)
- assert !File.exist?(full_path), "#{full_path.inspect} should not exist."
- end
-
- def cache_path(path)
- File.join(FILE_STORE_PATH, 'views', path + '.cache')
+ def fragment_exist?(path)
+ @controller.fragment_exist?(path)
end
def read_fragment(path)
@@ -450,6 +440,19 @@ class FragmentCachingTest < Test::Unit::TestCase
assert_nil @controller.read_fragment('name')
end
+ def test_fragment_exist__with_caching_enabled
+ @store.write('views/name', 'value')
+ assert @controller.fragment_exist?('name')
+ assert !@controller.fragment_exist?('other_name')
+ end
+
+ def test_fragment_exist__with_caching_disabled
+ ActionController::Base.perform_caching = false
+ @store.write('views/name', 'value')
+ assert !@controller.fragment_exist?('name')
+ assert !@controller.fragment_exist?('other_name')
+ end
+
def test_write_fragment__with_caching_enabled
assert_nil @store.read('views/name')
assert_equal 'value', @controller.write_fragment('name', 'value')
@@ -494,7 +497,6 @@ class FragmentCachingTest < Test::Unit::TestCase
assert_equal 'generated till now -> ', buffer
end
-
def test_fragment_for
@store.write('views/expensive', 'fragment content')
fragment_computed = false
diff --git a/actionpack/test/controller/cgi_test.rb b/actionpack/test/controller/cgi_test.rb
index 87f72fda77..f0f3a4b826 100755
--- a/actionpack/test/controller/cgi_test.rb
+++ b/actionpack/test/controller/cgi_test.rb
@@ -114,3 +114,36 @@ class CgiRequestNeedsRewoundTest < BaseCgiTest
assert_equal 0, request.body.pos
end
end
+
+class CgiResponseTest < BaseCgiTest
+ def setup
+ super
+ @fake_cgi.expects(:header).returns("HTTP/1.0 200 OK\nContent-Type: text/html\n")
+ @response = ActionController::CgiResponse.new(@fake_cgi)
+ @output = StringIO.new('')
+ end
+
+ def test_simple_output
+ @response.body = "Hello, World!"
+
+ @response.out(@output)
+ assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\nHello, World!", @output.string
+ end
+
+ def test_head_request
+ @fake_cgi.env_table['REQUEST_METHOD'] = 'HEAD'
+ @response.body = "Hello, World!"
+
+ @response.out(@output)
+ assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n", @output.string
+ end
+
+ def test_streaming_block
+ @response.body = Proc.new do |response, output|
+ 5.times { |n| output.write(n) }
+ end
+
+ @response.out(@output)
+ assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n01234", @output.string
+ end
+end
diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb
index 6dc77a4aaf..83e3b085e7 100644
--- a/actionpack/test/controller/helper_test.rb
+++ b/actionpack/test/controller/helper_test.rb
@@ -85,7 +85,7 @@ class HelperTest < Test::Unit::TestCase
def test_helper_block_include
assert_equal expected_helper_methods, missing_methods
assert_nothing_raised {
- @controller_class.helper { include TestHelper }
+ @controller_class.helper { include HelperTest::TestHelper }
}
assert [], missing_methods
end
diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb
new file mode 100644
index 0000000000..cd4151783e
--- /dev/null
+++ b/actionpack/test/controller/rack_test.rb
@@ -0,0 +1,150 @@
+require 'abstract_unit'
+require 'action_controller/rack_process'
+
+class BaseRackTest < Test::Unit::TestCase
+ def setup
+ @env = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"}
+ # some Nokia phone browsers omit the space after the semicolon separator.
+ # some developers have grown accustomed to using comma in cookie values.
+ @alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"}
+ @request = ActionController::RackRequest.new(@env)
+ end
+
+ def default_test; end
+end
+
+
+class RackRequestTest < BaseRackTest
+ def test_proxy_request
+ assert_equal 'glu.ttono.us', @request.host_with_port
+ end
+
+ def test_http_host
+ @env.delete "HTTP_X_FORWARDED_HOST"
+ @env['HTTP_HOST'] = "rubyonrails.org:8080"
+ assert_equal "rubyonrails.org:8080", @request.host_with_port
+
+ @env['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
+ assert_equal "www.secondhost.org", @request.host
+ end
+
+ def test_http_host_with_default_port_overrides_server_port
+ @env.delete "HTTP_X_FORWARDED_HOST"
+ @env['HTTP_HOST'] = "rubyonrails.org"
+ assert_equal "rubyonrails.org", @request.host_with_port
+ end
+
+ def test_host_with_port_defaults_to_server_name_if_no_host_headers
+ @env.delete "HTTP_X_FORWARDED_HOST"
+ @env.delete "HTTP_HOST"
+ assert_equal "glu.ttono.us:8007", @request.host_with_port
+ end
+
+ def test_host_with_port_falls_back_to_server_addr_if_necessary
+ @env.delete "HTTP_X_FORWARDED_HOST"
+ @env.delete "HTTP_HOST"
+ @env.delete "SERVER_NAME"
+ assert_equal "207.7.108.53:8007", @request.host_with_port
+ end
+
+ def test_host_with_port_if_http_standard_port_is_specified
+ @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80"
+ assert_equal "glu.ttono.us", @request.host_with_port
+ end
+
+ def test_host_with_port_if_https_standard_port_is_specified
+ @env['HTTP_X_FORWARDED_PROTO'] = "https"
+ @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443"
+ assert_equal "glu.ttono.us", @request.host_with_port
+ end
+
+ def test_host_if_ipv6_reference
+ @env.delete "HTTP_X_FORWARDED_HOST"
+ @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]"
+ assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
+ end
+
+ def test_host_if_ipv6_reference_with_port
+ @env.delete "HTTP_X_FORWARDED_HOST"
+ @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008"
+ assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
+ end
+
+ def test_cookie_syntax_resilience
+ cookies = CGI::Cookie::parse(@env["HTTP_COOKIE"]);
+ assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect
+ assert_equal ["yes"], cookies["is_admin"], cookies.inspect
+
+ alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]);
+ assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
+ assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect
+ end
+end
+
+
+class RackRequestParamsParsingTest < BaseRackTest
+ def test_doesnt_break_when_content_type_has_charset
+ data = 'flamenco=love'
+ @request.env['CONTENT_LENGTH'] = data.length
+ @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+ @request.env['RAW_POST_DATA'] = data
+ assert_equal({"flamenco"=> "love"}, @request.request_parameters)
+ end
+
+ def test_doesnt_interpret_request_uri_as_query_string_when_missing
+ @request.env['REQUEST_URI'] = 'foo'
+ assert_equal({}, @request.query_parameters)
+ end
+end
+
+
+class RackRequestNeedsRewoundTest < BaseRackTest
+ def test_body_should_be_rewound
+ data = 'foo'
+ @env['rack.input'] = StringIO.new(data)
+ @env['CONTENT_LENGTH'] = data.length
+ @env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+
+ # Read the request body by parsing params.
+ request = ActionController::RackRequest.new(@env)
+ request.request_parameters
+
+ # Should have rewound the body.
+ assert_equal 0, request.body.pos
+ end
+end
+
+
+class RackResponseTest < BaseRackTest
+ def setup
+ super
+ @response = ActionController::RackResponse.new
+ @output = StringIO.new('')
+ end
+
+ def test_simple_output
+ @response.body = "Hello, World!"
+
+ status, headers, body = @response.out(@output)
+ assert_equal 200, status
+ assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers)
+
+ parts = []
+ body.each { |part| parts << part }
+ assert_equal ["Hello, World!"], parts
+ end
+
+ def test_streaming_block
+ @response.body = Proc.new do |response, output|
+ 5.times { |n| output.write(n) }
+ end
+
+ status, headers, body = @response.out(@output)
+ assert_equal 200, status
+ assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers)
+
+ parts = []
+ body.each { |part| parts << part }
+ assert_equal ["0", "1", "2", "3", "4"], parts
+ end
+end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index b28f7bcdff..5e5503fd52 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -50,6 +50,13 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase
:additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"] }
assert_equal options, @set.recognize_path("/controller/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2")
end
+
+ def test_route_generation_allows_passing_non_string_values_to_generated_helper
+ assert_equal "/controller/action/variable/1/2", @set.generate(:controller => "controller",
+ :action => "action",
+ :variable => "variable",
+ :additional => [1, 2])
+ end
end
class LegacyRouteSetTests < Test::Unit::TestCase
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb
index ba6c7f4299..38898a1f75 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_test.rb
@@ -511,16 +511,26 @@ XML
FILES_DIR = File.dirname(__FILE__) + '/../fixtures/multipart'
+ if RUBY_VERSION < '1.9'
+ READ_BINARY = 'rb'
+ READ_PLAIN = 'r'
+ else
+ READ_BINARY = 'rb:binary'
+ READ_PLAIN = 'r:binary'
+ end
+
def test_test_uploaded_file
filename = 'mona_lisa.jpg'
path = "#{FILES_DIR}/#{filename}"
content_type = 'image/png'
+ expected = File.read(path)
+ expected.force_encoding(Encoding::BINARY) if expected.respond_to?(:force_encoding)
file = ActionController::TestUploadedFile.new(path, content_type)
assert_equal filename, file.original_filename
assert_equal content_type, file.content_type
assert_equal file.path, file.local_path
- assert_equal File.read(path), file.read
+ assert_equal expected, file.read
end
def test_test_uploaded_file_with_binary
@@ -529,10 +539,10 @@ XML
content_type = 'image/png'
binary_uploaded_file = ActionController::TestUploadedFile.new(path, content_type, :binary)
- assert_equal File.open(path, 'rb').read, binary_uploaded_file.read
+ assert_equal File.open(path, READ_BINARY).read, binary_uploaded_file.read
plain_uploaded_file = ActionController::TestUploadedFile.new(path, content_type)
- assert_equal File.open(path, 'r').read, plain_uploaded_file.read
+ assert_equal File.open(path, READ_PLAIN).read, plain_uploaded_file.read
end
def test_fixture_file_upload_with_binary
@@ -541,10 +551,10 @@ XML
content_type = 'image/jpg'
binary_file_upload = fixture_file_upload(path, content_type, :binary)
- assert_equal File.open(path, 'rb').read, binary_file_upload.read
+ assert_equal File.open(path, READ_BINARY).read, binary_file_upload.read
plain_file_upload = fixture_file_upload(path, content_type)
- assert_equal File.open(path, 'r').read, plain_file_upload.read
+ assert_equal File.open(path, READ_PLAIN).read, plain_file_upload.read
end
def test_fixture_file_upload
diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb
deleted file mode 100644
index 73e7ec1d76..0000000000
--- a/actionpack/test/template/compiled_templates_test.rb
+++ /dev/null
@@ -1,192 +0,0 @@
-require 'abstract_unit'
-require 'action_view/helpers/date_helper'
-require 'action_view/compiled_templates'
-
-class CompiledTemplateTests < Test::Unit::TestCase
- def setup
- @ct = ActionView::CompiledTemplates.new
- @v = Class.new
- @v.send :include, @ct
- @a = './test_compile_template_a.rhtml'
- @b = './test_compile_template_b.rhtml'
- @s = './test_compile_template_link.rhtml'
- end
- def teardown
- [@a, @b, @s].each do |f|
- FileUtils.rm(f) if File.exist?(f) || File.symlink?(f)
- end
- end
- attr_reader :ct, :v
-
- def test_name_allocation
- hi_world = ct.method_names['hi world']
- hi_sexy = ct.method_names['hi sexy']
- wish_upon_a_star = ct.method_names['I love seeing decent error messages']
-
- assert_equal hi_world, ct.method_names['hi world']
- assert_equal hi_sexy, ct.method_names['hi sexy']
- assert_equal wish_upon_a_star, ct.method_names['I love seeing decent error messages']
- assert_equal 3, [hi_world, hi_sexy, wish_upon_a_star].uniq.length
- end
-
- def test_wrap_source
- assert_equal(
- "def aliased_assignment(value)\nself.value = value\nend",
- @ct.wrap_source(:aliased_assignment, [:value], 'self.value = value')
- )
-
- assert_equal(
- "def simple()\nnil\nend",
- @ct.wrap_source(:simple, [], 'nil')
- )
- end
-
- def test_compile_source_single_method
- selector = ct.compile_source('doubling method', [:a], 'a + a')
- assert_equal 2, @v.new.send(selector, 1)
- assert_equal 4, @v.new.send(selector, 2)
- assert_equal -4, @v.new.send(selector, -2)
- assert_equal 0, @v.new.send(selector, 0)
- selector
- end
-
- def test_compile_source_two_method
- sel1 = test_compile_source_single_method # compile the method in the other test
- sel2 = ct.compile_source('doubling method', [:a, :b], 'a + b + a + b')
- assert_not_equal sel1, sel2
-
- assert_equal 2, @v.new.send(sel1, 1)
- assert_equal 4, @v.new.send(sel1, 2)
-
- assert_equal 6, @v.new.send(sel2, 1, 2)
- assert_equal 32, @v.new.send(sel2, 15, 1)
- end
-
- def test_mtime
- t1 = Time.now
-
- test_compile_source_single_method
- mtime = ct.mtime('doubling method', [:a])
-
- assert mtime < Time.now
- assert mtime > t1
- end
-
- uses_mocha 'test_compile_time' do
-
- def test_compile_time
- t = Time.now
-
- File.open(@a, "w"){|f| f.puts @a}
- File.open(@b, "w"){|f| f.puts @b}
- # windows doesn't support symlinks (even under cygwin)
- windows = (RUBY_PLATFORM =~ /win32/)
- `ln -s #{@a} #{@s}` unless windows
-
- v = ActionView::Base.new
- v.base_path = '.'
- v.cache_template_loading = false
-
- ta = ActionView::Template.new(v, @a, false, {})
- tb = ActionView::Template.new(v, @b, false, {})
- ts = ActionView::Template.new(v, @s, false, {})
-
- @handler_class = ActionView::Template.handler_class_for_extension(:rhtml)
- @handler = @handler_class.new(v)
-
- # All templates were created at t+1
- File::Stat.any_instance.expects(:mtime).times(windows ? 2 : 3).returns(t + 1.second)
-
- # private methods template_changed_since? and compile_template?
- # should report true for all since they have not been compiled
- assert @handler.send(:template_changed_since?, @a, t)
- assert @handler.send(:template_changed_since?, @b, t)
- assert @handler.send(:template_changed_since?, @s, t) unless windows
-
- assert @handler.send(:compile_template?, ta)
- assert @handler.send(:compile_template?, tb)
- assert @handler.send(:compile_template?, ts) unless windows
-
- # All templates are rendered at t+2
- Time.expects(:now).times(windows ? 2 : 3).returns(t + 2.seconds)
- v.send(:render_template, ta)
- v.send(:render_template, tb)
- v.send(:render_template, ts) unless windows
- a_n = v.method_names[@a]
- b_n = v.method_names[@b]
- s_n = v.method_names[@s] unless windows
- # all of the files have changed since last compile
- assert @handler.compile_time[a_n] > t
- assert @handler.compile_time[b_n] > t
- assert @handler.compile_time[s_n] > t unless windows
-
- # private methods template_changed_since? and compile_template?
- # should report false for all since none have changed since compile
- File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 12).returns(t + 1.second)
- assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n])
- assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n])
- assert !@handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows
- assert !@handler.send(:compile_template?, ta)
- assert !@handler.send(:compile_template?, tb)
- assert !@handler.send(:compile_template?, ts) unless windows
- v.send(:render_template, ta)
- v.send(:render_template, tb)
- v.send(:render_template, ts) unless windows
- # none of the files have changed since last compile
- assert @handler.compile_time[a_n] < t + 3.seconds
- assert @handler.compile_time[b_n] < t + 3.seconds
- assert @handler.compile_time[s_n] < t + 3.seconds unless windows
-
- `rm #{@s}; ln -s #{@b} #{@s}` unless windows
- # private methods template_changed_since? and compile_template?
- # should report true for symlink since it has changed since compile
-
- # t + 3.seconds is for the symlink
- File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 9).returns(
- *(windows ? [ t + 1.second, t + 1.second ] :
- [ t + 1.second, t + 1.second, t + 3.second ]) * 3)
- assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n])
- assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n])
- assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows
- assert !@handler.send(:compile_template?, ta)
- assert !@handler.send(:compile_template?, tb)
- assert @handler.send(:compile_template?, ts) unless windows
-
- # Only the symlink template gets rendered at t+3
- Time.stubs(:now).returns(t + 3.seconds) unless windows
- v.send(:render_template, ta)
- v.send(:render_template, tb)
- v.send(:render_template, ts) unless windows
- # the symlink has changed since last compile
- assert @handler.compile_time[a_n] < t + 3.seconds
- assert @handler.compile_time[b_n] < t + 3.seconds
- assert_equal @handler.compile_time[s_n], t + 3.seconds unless windows
-
- FileUtils.touch @b
- # private methods template_changed_since? and compile_template?
- # should report true for symlink and file at end of symlink
- # since it has changed since last compile
- #
- # t+4 is for @b and also for the file that @s points to, which is @b
- File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 12).returns(
- *(windows ? [ t + 1.second, t + 4.seconds ] :
- [ t + 1.second, t + 4.seconds, t + 3.second, t + 4.seconds ]) * 3)
- assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n])
- assert @handler.send(:template_changed_since?, @b, @handler.compile_time[b_n])
- assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows
- assert !@handler.send(:compile_template?, ta)
- assert @handler.send(:compile_template?, tb)
- assert @handler.send(:compile_template?, ts) unless windows
-
- Time.expects(:now).times(windows ? 1 : 2).returns(t + 5.seconds)
- v.send(:render_template, ta)
- v.send(:render_template, tb)
- v.send(:render_template, ts) unless windows
- # the file at the end of the symlink has changed since last compile
- # both the symlink and the file at the end of it should be recompiled
- assert @handler.compile_time[a_n] < t + 5.seconds
- assert_equal @handler.compile_time[b_n], t + 5.seconds
- assert_equal @handler.compile_time[s_n], t + 5.seconds unless windows
- end
- end
-end
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index ae83c7bf47..0a7b19ba96 100755
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -1722,6 +1722,12 @@ class DateHelperTest < ActionView::TestCase
assert_equal 2, dummy_instance_tag.send!(:default_time_from_options, :hour => 2).hour
end
end
+
+ def test_instance_tag_default_time_from_options_handles_far_future_date
+ dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3)
+ time = dummy_instance_tag.send!(:default_time_from_options, :year => 2050, :month => 2, :day => 10, :hour => 15, :min => 30, :sec => 45)
+ assert_equal 2050, time.year
+ end
end
protected
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 4538b6dc6f..af99e6243d 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -181,6 +181,17 @@ class FormHelperTest < ActionView::TestCase
'<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
check_box("post", "secret?")
)
+
+ @post.secret = ['0']
+ assert_dom_equal(
+ '<input id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+ check_box("post", "secret")
+ )
+ @post.secret = ['1']
+ assert_dom_equal(
+ '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
+ check_box("post", "secret")
+ )
end
def test_check_box_with_explicit_checked_and_unchecked_values
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
index 9a1079b297..53a250f9d5 100644
--- a/actionpack/test/template/prototype_helper_test.rb
+++ b/actionpack/test/template/prototype_helper_test.rb
@@ -25,8 +25,6 @@ class Author::Nested < Author; end
class PrototypeHelperBaseTest < ActionView::TestCase
- tests ActionView::Helpers::PrototypeHelper
-
attr_accessor :template_format
def setup
@@ -219,9 +217,9 @@ class PrototypeHelperTest < PrototypeHelperBaseTest
end
def test_observe_field_using_with_option
- expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:'id=' + value})})\n//]]>\n</script>)
+ expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(value)})})\n//]]>\n</script>)
assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id')
- assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + value")
+ assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + encodeURIComponent(value)")
end
def test_observe_field_using_json_in_with_option
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index 06e1fd1929..62cdca03d1 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -186,6 +186,7 @@ class TextHelperTest < ActionView::TestCase
http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007
http://www.mail-archive.com/rails@lists.rubyonrails.org/
http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1
+ http://en.wikipedia.org/wiki/Sprite_(computer_graphics)
)
urls.each do |url|
@@ -262,6 +263,8 @@ class TextHelperTest < ActionView::TestCase
assert_equal email2_result, auto_link(email2_raw)
assert_equal '', auto_link(nil)
assert_equal '', auto_link('')
+ assert_equal "#{link_result} #{link_result} #{link_result}", auto_link("#{link_raw} #{link_raw} #{link_raw}")
+ assert_equal '<a href="http://www.rubyonrails.com">Ruby On Rails</a>', auto_link('<a href="http://www.rubyonrails.com">Ruby On Rails</a>')
end
def test_auto_link_at_eol