aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG.md19
-rw-r--r--actionpack/lib/action_controller/metal/data_streaming.rb30
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb10
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb11
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb7
-rw-r--r--actionpack/lib/action_controller/test_case.rb29
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb19
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb7
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb18
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb1
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb84
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb35
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/tags/check_box.rb6
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb16
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb31
-rw-r--r--actionpack/test/activerecord/active_record_store_test.rb7
-rw-r--r--actionpack/test/controller/render_json_test.rb2
-rw-r--r--actionpack/test/controller/send_file_test.rb11
-rw-r--r--actionpack/test/controller/test_case_test.rb83
-rw-r--r--actionpack/test/dispatch/cookies_test.rb44
-rw-r--r--actionpack/test/dispatch/routing_test.rb60
-rw-r--r--actionpack/test/fixtures/test/_b_layout_for_partial_with_object.html.erb1
-rw-r--r--actionpack/test/fixtures/test/_b_layout_for_partial_with_object_counter.html.erb1
-rw-r--r--actionpack/test/fixtures/translations/templates/default.erb1
-rw-r--r--actionpack/test/template/date_helper_i18n_test.rb41
-rw-r--r--actionpack/test/template/date_helper_test.rb122
-rw-r--r--actionpack/test/template/form_helper_test.rb117
-rw-r--r--actionpack/test/template/javascript_helper_test.rb30
-rw-r--r--actionpack/test/template/render_test.rb19
-rw-r--r--actionpack/test/template/tag_helper_test.rb5
-rw-r--r--actionpack/test/template/translation_helper_test.rb25
34 files changed, 679 insertions, 237 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index b3d6ae0ac7..99602f14d9 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,24 @@
## Rails 4.0.0 (unreleased) ##
+* Replace `include_seconds` boolean argument with `:include_seconds => true` option
+ in `distance_of_time_in_words` and `time_ago_in_words` signature. *Dmitriy Kiriyenko*
+
+* Remove `button_to_function` and `link_to_function` helpers. *Rafael Mendonça França*
+
+* Make current object and counter (when it applies) variables accessible when
+ rendering templates with :object / :collection. *Carlos Antonio da Silva*
+
+* JSONP now uses mimetype application/javascript instead of application/json *omjokine*
+
+* Allow to lazy load `default_form_builder` by passing a `String` instead of a constant. *Piotr Sarnacki*
+
+* Session arguments passed to `process` calls in functional tests are now merged into
+ the existing session, whereas previously they would replace the existing session.
+ This change may break some existing tests if they are asserting the exact contents of
+ the session but should not break existing tests that only assert individual keys.
+
+ *Andrew White*
+
* Add `index` method to FormBuilder class. *Jorge Bejar*
* Remove the leading \n added by textarea on assert_select. *Santiago Pastorino*
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
index 3140327d87..379ff97048 100644
--- a/actionpack/lib/action_controller/metal/data_streaming.rb
+++ b/actionpack/lib/action_controller/metal/data_streaming.rb
@@ -8,10 +8,8 @@ module ActionController #:nodoc:
include ActionController::Rendering
- DEFAULT_SEND_FILE_OPTIONS = {
- :type => 'application/octet-stream'.freeze,
- :disposition => 'attachment'.freeze,
- }.freeze
+ DEFAULT_SEND_FILE_TYPE = 'application/octet-stream'.freeze #:nodoc:
+ DEFAULT_SEND_FILE_DISPOSITION = 'attachment'.freeze #:nodoc:
protected
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
@@ -127,7 +125,7 @@ module ActionController #:nodoc:
#
# See +send_file+ for more information on HTTP Content-* headers and caching.
def send_data(data, options = {}) #:doc:
- send_file_headers! options.dup
+ send_file_headers! options
render options.slice(:status, :content_type).merge(:text => data)
end
@@ -135,15 +133,8 @@ module ActionController #:nodoc:
def send_file_headers!(options)
type_provided = options.has_key?(:type)
- options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
- [:type, :disposition].each do |arg|
- raise ArgumentError, ":#{arg} option required" if options[arg].nil?
- end
-
- disposition = options[:disposition]
- disposition += %(; filename="#{options[:filename]}") if options[:filename]
-
- content_type = options[:type]
+ content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
+ raise ArgumentError, ":type option required" if content_type.nil?
if content_type.is_a?(Symbol)
extension = Mime[content_type]
@@ -157,10 +148,13 @@ module ActionController #:nodoc:
self.content_type = content_type
end
- headers.merge!(
- 'Content-Disposition' => disposition,
- 'Content-Transfer-Encoding' => 'binary'
- )
+ disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
+ unless disposition.nil?
+ disposition += %(; filename="#{options[:filename]}") if options[:filename]
+ headers['Content-Disposition'] = disposition
+ end
+
+ headers['Content-Transfer-Encoding'] = 'binary'
response.sending_file = true
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 35ded8a56c..5e7bd44562 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -45,6 +45,16 @@ module ActionController
# integer, or a symbol representing the downcased, underscored and symbolized description.
# Note that the status code must be a 3xx HTTP code, or redirection will not occur.
#
+ # If you are using XHR requests other than GET or POST and redirecting after the
+ # request then some browsers will follow the redirect using the original request
+ # method. This may lead to undesirable behavior such as a double DELETE. To work
+ # around this you can return a <tt>303 See Other</tt> status code which will be
+ # followed using a GET request.
+ #
+ # Examples:
+ # redirect_to posts_url, :status => :see_other
+ # redirect_to :action => 'index', :status => 303
+ #
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
#
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 6e9ce450ac..4a0c1c7dd7 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -91,9 +91,14 @@ module ActionController
add :json do |json, options|
json = json.to_json(options) unless json.kind_of?(String)
- json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
- self.content_type ||= Mime::JSON
- json
+
+ if options[:callback].present?
+ self.content_type ||= Mime::JS
+ "#{options[:callback]}(#{json})"
+ else
+ self.content_type ||= Mime::JSON
+ json
+ end
end
add :js do |js, options|
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index 5c40889f6b..e7af3f5b8d 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -75,12 +75,7 @@ module ActionController
def record_key_for_dom_id(record)
record = record.to_model if record.respond_to?(:to_model)
key = record.to_key
- key ? sanitize_dom_id(key.join('_')) : key
- end
-
- # Replaces characters that are invalid in HTML DOM ids with valid ones.
- def sanitize_dom_id(candidate_id)
- candidate_id # TODO implement conversion to valid DOM id values
+ key ? key.join('_') : key
end
end
end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 66a4808e36..21997c4d79 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -147,17 +147,23 @@ module ActionController
extra_keys = routes.extra_keys(parameters)
non_path_parameters = get? ? query_parameters : request_parameters
parameters.each do |key, value|
- if value.is_a? Fixnum
- value = value.to_s
- elsif value.is_a? Array
- value = Result.new(value.map { |v| v.is_a?(String) ? v.dup : v })
- elsif value.is_a? String
+ if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
+ value = value.map{ |v| v.duplicable? ? v.dup : v }
+ elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? })
+ value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }]
+ elsif value.frozen? && value.duplicable?
value = value.dup
end
if extra_keys.include?(key.to_sym)
non_path_parameters[key] = value
else
+ if value.is_a?(Array)
+ value = Result.new(value.map(&:to_param))
+ else
+ value = value.to_param
+ end
+
path_parameters[key.to_s] = value
end
end
@@ -453,7 +459,7 @@ module ActionController
# Ensure that numbers and symbols passed as params are converted to
# proper params, as is the case when engaging rack.
- parameters = paramify_values(parameters)
+ parameters = paramify_values(parameters) if html_format?(parameters)
@request.recycle!
@response.recycle!
@@ -466,14 +472,13 @@ module ActionController
parameters ||= {}
controller_class_name = @controller.class.anonymous? ?
- "anonymous_controller" :
+ "anonymous" :
@controller.class.name.underscore.sub(/_controller$/, '')
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
- @request.session = ActionController::TestSession.new(session) if session
+ @request.session.update(session) if session
@request.session["flash"] = @request.flash.update(flash || {})
- @request.session["flash"].sweep
@controller.request = @request
build_request_uri(action, parameters)
@@ -546,6 +551,12 @@ module ActionController
@request.env["QUERY_STRING"] = query_string || ""
end
end
+
+ def html_format?(parameters)
+ return true unless parameters.is_a?(Hash)
+ format = Mime[parameters[:format]]
+ format.nil? || format.html?
+ end
end
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 25d842c572..2c0c31de9d 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -82,7 +82,7 @@ module ActionDispatch
TOKEN_KEY = "action_dispatch.secret_token".freeze
# Raised when storing more than 4K of session data.
- class CookieOverflow < StandardError; end
+ CookieOverflow = Class.new StandardError
class CookieJar #:nodoc:
include Enumerable
@@ -117,7 +117,6 @@ module ActionDispatch
@delete_cookies = {}
@host = host
@secure = secure
- @closed = false
@cookies = {}
end
@@ -154,7 +153,7 @@ module ActionDispatch
end
elsif options[:domain].is_a? Array
# if host matches one of the supplied domains without a dot in front of it
- options[:domain] = options[:domain].find {|domain| @host.include? domain[/^\.?(.*)$/, 1] }
+ options[:domain] = options[:domain].find {|domain| @host.include? domain.sub(/^\./, '') }
end
end
@@ -169,12 +168,14 @@ module ActionDispatch
options = { :value => value }
end
- @cookies[key.to_s] = value
-
handle_options(options)
- @set_cookies[key.to_s] = options
- @delete_cookies.delete(key.to_s)
+ if @cookies[key.to_s] != value or options[:expires]
+ @cookies[key.to_s] = value
+ @set_cookies[key.to_s] = options
+ @delete_cookies.delete(key.to_s)
+ end
+
value
end
@@ -182,8 +183,9 @@ module ActionDispatch
# and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
def delete(key, options = {})
- options.symbolize_keys!
+ return unless @cookies.has_key? key.to_s
+ options.symbolize_keys!
handle_options(options)
value = @cookies.delete(key.to_s)
@@ -339,7 +341,6 @@ module ActionDispatch
end
def call(env)
- cookie_jar = nil
status, headers, body = @app.call(env)
if cookie_jar = env['action_dispatch.cookies']
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index c92c91df65..fd0ce62a3b 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -4,7 +4,7 @@ module ActionDispatch
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash
- @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new)
+ @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new).tap(&:sweep)
end
end
@@ -79,7 +79,6 @@ module ActionDispatch
def initialize #:nodoc:
@discard = Set.new
- @closed = false
@flashes = {}
@now = nil
end
@@ -217,10 +216,6 @@ module ActionDispatch
end
def call(env)
- if (session = env['rack.session']) && (flash = session['flash'])
- flash.sweep
- end
-
@app.call(env)
ensure
session = env['rack.session'] || {}
@@ -237,7 +232,8 @@ module ActionDispatch
env[KEY] = new_hash
end
- if session.key?('flash') && session['flash'].empty?
+ if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
+ session.key?('flash') && session['flash'].empty?
session.delete('flash')
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 6a8e690d18..45a00b5056 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -74,6 +74,13 @@ module ActionDispatch
class AbstractStore < Rack::Session::Abstract::ID
include Compatibility
include StaleSessionCheck
+
+ private
+
+ def set_cookie(env, session_id, cookie)
+ request = ActionDispatch::Request.new(env)
+ request.cookie_jar[key] = cookie
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index ae01781013..444a79c50d 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -1,4 +1,6 @@
require 'action_dispatch/http/request'
+require 'active_support/core_ext/uri'
+require 'rack/utils'
module ActionDispatch
module Routing
@@ -46,8 +48,17 @@ module ActionDispatch
:params => request.query_parameters
}.merge options
+ if !params.empty? && url_options[:path].match(/%\{\w*\}/)
+ url_options[:path] = (url_options[:path] % escape_path(params))
+ end
+
ActionDispatch::Http::URL.url_for url_options
end
+
+ private
+ def escape_path(params)
+ Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }]
+ end
end
module Redirection
@@ -96,13 +107,18 @@ module ActionDispatch
path = args.shift
block = lambda { |params, request|
- (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params)
+ (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % escape(params))
} if String === path
block = path if path.respond_to? :call
raise ArgumentError, "redirection argument not supported" unless block
Redirect.new status, block
end
+
+ private
+ def escape(params)
+ Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
index dd4e9ae4cc..35f91cec18 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
@@ -1,5 +1,6 @@
require 'thread'
require 'active_support/core_ext/file'
+require 'active_support/core_ext/module/attribute_accessors'
module ActionView
module Helpers
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 81f856feda..6bd8e62e0d 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -19,7 +19,7 @@ module ActionView
# of \date[month].
module DateHelper
# Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds.
- # Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs.
+ # Pass <tt>:include_seconds => true</tt> if you want more detailed approximations when distance < 1 min, 29 secs.
# Distances are reported based on the following table:
#
# 0 <-> 29 secs # => less than a minute
@@ -29,14 +29,15 @@ module ActionView
# 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
# 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
# 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
- # 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month
+ # 29 days, 23 hrs, 59 mins, 30 secs <-> 44 days, 23 hrs, 59 mins, 29 secs # => about 1 month
+ # 44 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 2 months
# 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
# 1 yr <-> 1 yr, 3 months # => about 1 year
# 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
# 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
# 2 yrs <-> max time or date # => (same rules as 1 yr)
#
- # With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds:
+ # With <tt>:include_seconds => true</tt> and the difference < 1 minute 29 seconds:
# 0-4 secs # => less than 5 seconds
# 5-9 secs # => less than 10 seconds
# 10-19 secs # => less than 20 seconds
@@ -46,28 +47,33 @@ module ActionView
#
# ==== Examples
# from_time = Time.now
- # distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
- # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
- # distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
- # distance_of_time_in_words(from_time, from_time + 15.seconds, true) # => less than 20 seconds
- # distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
- # distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
- # distance_of_time_in_words(from_time, from_time + 45.seconds, true) # => less than a minute
- # distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute
- # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
- # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
- # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
+ # distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
+ # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
+ # distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
+ # distance_of_time_in_words(from_time, from_time + 15.seconds, :include_seconds => true) # => less than 20 seconds
+ # distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
+ # distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
+ # distance_of_time_in_words(from_time, from_time + 45.seconds, :include_seconds => true) # => less than a minute
+ # distance_of_time_in_words(from_time, from_time - 45.seconds, :include_seconds => true) # => less than a minute
+ # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
+ # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
+ # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
# distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
#
# to_time = Time.now + 6.years + 19.days
- # distance_of_time_in_words(from_time, to_time, true) # => about 6 years
- # distance_of_time_in_words(to_time, from_time, true) # => about 6 years
- # distance_of_time_in_words(Time.now, Time.now) # => less than a minute
- #
- # distance_of_time_in_words(70) # => 1 minute
- # distance_of_time_in_words(60*60) # => about 1 hour
- #
- def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {})
+ # distance_of_time_in_words(from_time, to_time, :include_seconds => true) # => about 6 years
+ # distance_of_time_in_words(to_time, from_time, :include_seconds => true) # => about 6 years
+ # distance_of_time_in_words(Time.now, Time.now) # => less than a minute
+ #
+ def distance_of_time_in_words(from_time, to_time = 0, include_seconds_or_options = {}, options = {})
+ unless include_seconds_or_options.is_a?(Hash)
+ ActiveSupport::Deprecation.warn "distance_of_time_in_words and time_ago_in_words now accept :include_seconds " +
+ "as a part of options hash, not a boolean argument", caller
+ options[:include_seconds] ||= !!include_seconds_or_options
+ else
+ options = include_seconds_or_options
+ end
+
from_time = from_time.to_time if from_time.respond_to?(:to_time)
to_time = to_time.to_time if to_time.respond_to?(:to_time)
from_time, to_time = to_time, from_time if from_time > to_time
@@ -79,7 +85,7 @@ module ActionView
when 0..1
return distance_in_minutes == 0 ?
locale.t(:less_than_x_minutes, :count => 1) :
- locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds
+ locale.t(:x_minutes, :count => distance_in_minutes) unless options[:include_seconds]
case distance_in_seconds
when 0..4 then locale.t :less_than_x_seconds, :count => 5
@@ -90,13 +96,18 @@ module ActionView
else locale.t :x_minutes, :count => 1
end
- when 2..44 then locale.t :x_minutes, :count => distance_in_minutes
- when 45..89 then locale.t :about_x_hours, :count => 1
- when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
- when 1440..2519 then locale.t :x_days, :count => 1
- when 2520..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
- when 43200..86399 then locale.t :about_x_months, :count => 1
- when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
+ when 2...45 then locale.t :x_minutes, :count => distance_in_minutes
+ when 45...90 then locale.t :about_x_hours, :count => 1
+ # 90 mins up to 24 hours
+ when 90...1440 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
+ # 24 hours up to 42 hours
+ when 1440...2520 then locale.t :x_days, :count => 1
+ # 42 hours up to 30 days
+ when 2520...43200 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
+ # 30 days up to 60 days
+ when 43200...86400 then locale.t :about_x_months, :count => (distance_in_minutes.to_f / 43200.0).round
+ # 60 days up to 365 days
+ when 86400...525600 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
else
if from_time.acts_like?(:time) && to_time.acts_like?(:time)
fyear = from_time.year
@@ -130,15 +141,16 @@ module ActionView
# Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
#
# ==== Examples
- # time_ago_in_words(3.minutes.from_now) # => 3 minutes
- # time_ago_in_words(Time.now - 15.hours) # => about 15 hours
- # time_ago_in_words(Time.now) # => less than a minute
+ # time_ago_in_words(3.minutes.from_now) # => 3 minutes
+ # time_ago_in_words(Time.now - 15.hours) # => about 15 hours
+ # time_ago_in_words(Time.now) # => less than a minute
+ # time_ago_in_words(Time.now, :include_seconds => true) # => less than 5 seconds
#
# from_time = Time.now - 3.days - 14.minutes - 25.seconds
# time_ago_in_words(from_time) # => 3 days
#
- def time_ago_in_words(from_time, include_seconds = false)
- distance_of_time_in_words(from_time, Time.now, include_seconds)
+ def time_ago_in_words(from_time, include_seconds_or_options = {})
+ distance_of_time_in_words(from_time, Time.now, include_seconds_or_options)
end
alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
@@ -1005,6 +1017,8 @@ module ActionView
# Returns the separator for a given datetime component.
def separator(type)
+ return "" if @options[:use_hidden]
+
case type
when :year, :month, :day
@options[:"discard_#{type}"] ? "" : @options[:date_separator]
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 8e7224937d..67f2abe509 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -10,6 +10,7 @@ require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/array/extract_options'
require 'active_support/deprecation'
+require 'active_support/core_ext/string/inflections'
module ActionView
# = Action View Form Helpers
@@ -1039,9 +1040,14 @@ module ActionView
object_name = ActiveModel::Naming.param_key(object)
end
- builder = options[:builder] || ActionView::Base.default_form_builder
+ builder = options[:builder] || default_form_builder
builder.new(object_name, object, self, options)
end
+
+ def default_form_builder
+ builder = ActionView::Base.default_form_builder
+ builder.respond_to?(:constantize) ? builder.constantize : builder
+ end
end
class FormBuilder
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 5a869d6303..cc20518b93 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -15,7 +15,6 @@ module ActionView
JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '&#x2028;'
JS_ESCAPE_MAP["\342\200\251".force_encoding('UTF-8').encode!] = '&#x2029;'
-
# Escapes carriage returns and single and double quotes for JavaScript segments.
#
@@ -68,40 +67,6 @@ module ActionView
def javascript_cdata_section(content) #:nodoc:
"\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
end
-
- # Returns a button whose +onclick+ handler triggers the passed JavaScript.
- #
- # The helper receives a name, JavaScript code, and an optional hash of HTML options. The
- # name is used as button label and the JavaScript code goes into its +onclick+ attribute.
- # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+.
- #
- # button_to_function "Greeting", "alert('Hello world!')", :class => "ok"
- # # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" />
- #
- def button_to_function(name, function=nil, html_options={})
- onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
-
- tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
- end
-
- # Returns a link whose +onclick+ handler triggers the passed JavaScript.
- #
- # The helper receives a name, JavaScript code, and an optional hash of HTML options. The
- # name is used as the link text and the JavaScript code goes into the +onclick+ attribute.
- # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all
- # the JavaScript is set, the helper appends "; return false;".
- #
- # The +href+ attribute of the tag is set to "#" unless +html_options+ has one.
- #
- # link_to_function "Greeting", "alert('Hello world!')", :class => "nav_link"
- # # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
- #
- def link_to_function(name, function, html_options={})
- onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
- href = html_options[:href] || '#'
-
- content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
- end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 23777721f6..f7afa48256 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -109,8 +109,12 @@ module ActionView
#
# cdata_section(File.read("hello_world.txt"))
# # => <![CDATA[<hello from a text file]]>
+ #
+ # cdata_section("hello]]>world")
+ # # => <![CDATA[hello]]]]><![CDATA[>world]]>
def cdata_section(content)
- "<![CDATA[#{content}]]>".html_safe
+ splitted = content.gsub(']]>', ']]]]><![CDATA[>')
+ "<![CDATA[#{splitted}]]>".html_safe
end
# Returns an escaped version of +html+ without affecting existing escaped entities.
diff --git a/actionpack/lib/action_view/helpers/tags/check_box.rb b/actionpack/lib/action_view/helpers/tags/check_box.rb
index 1a4aebb936..9d17a1dde3 100644
--- a/actionpack/lib/action_view/helpers/tags/check_box.rb
+++ b/actionpack/lib/action_view/helpers/tags/check_box.rb
@@ -41,17 +41,15 @@ module ActionView
def checked?(value)
case value
when TrueClass, FalseClass
- value
+ value == !!@checked_value
when NilClass
false
- when Integer
- value != 0
when String
value == @checked_value
when Array
value.include?(@checked_value)
else
- value.to_i != 0
+ value.to_i == @checked_value.to_i
end
end
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index cc74eff53a..fd06bfa2a8 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -45,6 +45,7 @@ module ActionView
# you know what kind of output to expect when you call translate in a template.
def translate(key, options = {})
options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
+ options[:default] = wrap_translate_defaults(options[:default]) if options[:default]
if html_safe_translation_key?(key)
html_safe_options = options.dup
options.except(*I18n::RESERVED_KEYS).each do |name, value|
@@ -83,6 +84,21 @@ module ActionView
def html_safe_translation_key?(key)
key.to_s =~ /(\b|_|\.)html$/
end
+
+ def wrap_translate_defaults(defaults)
+ new_defaults = []
+ defaults = Array(defaults)
+ while key = defaults.shift
+ if key.is_a?(Symbol)
+ new_defaults << lambda { |_, options| translate key, options.merge(:default => defaults) }
+ break
+ else
+ new_defautls << key
+ end
+ end
+
+ new_defaults
+ end
end
end
end
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 34ea06c9cf..c5d5540510 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -158,8 +158,8 @@ module ActionView
# Name: <%= user.name %>
# </div>
#
- # If a collection is given, the layout will be rendered once for each item in the collection. Just think
- # these two snippets have the same output:
+ # If a collection is given, the layout will be rendered once for each item in
+ # the collection. Just think these two snippets have the same output:
#
# <%# app/views/users/_user.html.erb %>
# Name: <%= user.name %>
@@ -184,7 +184,7 @@ module ActionView
# <%= render :partial => "user", :layout => "li_layout", :collection => users %>
# </ul>
#
- # Given two users whose names are Alice and Bob, these snippets return:
+ # Given two users whose names are Alice and Bob, these snippets return:
#
# <ul>
# <li>
@@ -195,6 +195,10 @@ module ActionView
# </li>
# </ul>
#
+ # The current object being rendered, as well as the object_counter, will be
+ # available as local variables inside the layout template under the same names
+ # as available in the partial.
+ #
# You can also apply a layout to a block within any template:
#
# <%# app/views/users/_chief.html.erb &>
@@ -282,14 +286,7 @@ module ActionView
spacer = find_template(@options[:spacer_template]).render(@view, @locals)
end
- if layout = @options[:layout]
- layout = find_template(layout)
- end
-
result = @template ? collection_with_template : collection_without_template
-
- result.map!{|content| layout.render(@view, @locals) { content } } if layout
-
result.join(spacer).html_safe
end
@@ -298,7 +295,7 @@ module ActionView
object, as = @object, @variable
if !block && (layout = @options[:layout])
- layout = find_template(layout)
+ layout = find_template(layout, @locals.keys + [@variable])
end
object ||= locals[as]
@@ -384,17 +381,23 @@ module ActionView
segments, locals, template = [], @locals, @template
as, counter = @variable, @variable_counter
+ if layout = @options[:layout]
+ layout = find_template(layout, @locals.keys + [@variable, @variable_counter])
+ end
+
locals[counter] = -1
@collection.each do |object|
locals[counter] += 1
locals[as] = object
- segments << template.render(@view, locals)
+
+ content = template.render(@view, locals)
+ content = layout.render(@view, locals) { content } if layout
+ segments << content
end
-
+
segments
end
-
def collection_without_template
segments, locals, collection_data = [], @locals, @collection_data
diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb
index fceebe1858..275f25f597 100644
--- a/actionpack/test/activerecord/active_record_store_test.rb
+++ b/actionpack/test/activerecord/active_record_store_test.rb
@@ -254,6 +254,13 @@ class ActiveRecordStoreTest < ActionDispatch::IntegrationTest
end
end
+ def test_session_store_with_all_domains
+ with_test_route_set(:domain => :all) do
+ get '/set_session_value'
+ assert_response :success
+ end
+ end
+
private
def with_test_route_set(options = {})
diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb
index 75fed8e933..7c0a6bd67e 100644
--- a/actionpack/test/controller/render_json_test.rb
+++ b/actionpack/test/controller/render_json_test.rb
@@ -102,7 +102,7 @@ class RenderJsonTest < ActionController::TestCase
def test_render_json_with_callback
get :render_json_hello_world_with_callback
assert_equal 'alert({"hello":"world"})', @response.body
- assert_equal 'application/json', @response.content_type
+ assert_equal 'text/javascript', @response.content_type
end
def test_render_json_with_custom_content_type
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index 3af17f495c..6fc3556e31 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -154,6 +154,17 @@ class SendFileTest < ActionController::TestCase
end
end
+ def test_send_file_with_default_content_disposition_header
+ process('data')
+ assert_equal 'attachment', @controller.headers['Content-Disposition']
+ end
+
+ def test_send_file_without_content_disposition_header
+ @controller.options = {:disposition => nil}
+ process('data')
+ assert_nil @controller.headers['Content-Disposition']
+ end
+
%w(file data).each do |method|
define_method "test_send_#{method}_status" do
@controller.options = { :stream => false, :status => 500 }
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index e8411bae0c..0d6d303b51 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -45,6 +45,10 @@ class TestCaseTest < ActionController::TestCase
render :text => request.fullpath
end
+ def test_format
+ render :text => request.format
+ end
+
def test_query_string
render :text => request.query_string
end
@@ -224,6 +228,26 @@ XML
assert_equal 'value2', session[:symbol]
end
+ def test_process_merges_session_arg
+ session[:foo] = 'bar'
+ get :no_op, nil, { :bar => 'baz' }
+ assert_equal 'bar', session[:foo]
+ assert_equal 'baz', session[:bar]
+ end
+
+ def test_merged_session_arg_is_retained_across_requests
+ get :no_op, nil, { :foo => 'bar' }
+ assert_equal 'bar', session[:foo]
+ get :no_op
+ assert_equal 'bar', session[:foo]
+ end
+
+ def test_process_overwrites_existing_session_arg
+ session[:foo] = 'bar'
+ get :no_op, nil, { :foo => 'baz' }
+ assert_equal 'baz', session[:foo]
+ end
+
def test_session_is_cleared_from_controller_after_reset_session
process :set_session
process :reset_the_session
@@ -559,14 +583,34 @@ XML
)
end
+ def test_params_passing_with_fixnums_when_not_html_request
+ get :test_params, :format => 'json', :count => 999
+ parsed_params = eval(@response.body)
+ assert_equal(
+ {'controller' => 'test_case_test/test', 'action' => 'test_params',
+ 'format' => 'json', 'count' => 999 },
+ parsed_params
+ )
+ end
+
+ def test_params_passing_path_parameter_is_string_when_not_html_request
+ get :test_params, :format => 'json', :id => 1
+ parsed_params = eval(@response.body)
+ assert_equal(
+ {'controller' => 'test_case_test/test', 'action' => 'test_params',
+ 'format' => 'json', 'id' => '1' },
+ parsed_params
+ )
+ end
+
def test_params_passing_with_frozen_values
assert_nothing_raised do
- get :test_params, :frozen => 'icy'.freeze, :frozens => ['icy'.freeze].freeze
+ get :test_params, :frozen => 'icy'.freeze, :frozens => ['icy'.freeze].freeze, :deepfreeze => { :frozen => 'icy'.freeze }.freeze
end
parsed_params = eval(@response.body)
assert_equal(
{'controller' => 'test_case_test/test', 'action' => 'test_params',
- 'frozen' => 'icy', 'frozens' => ['icy']},
+ 'frozen' => 'icy', 'frozens' => ['icy'], 'deepfreeze' => { 'frozen' => 'icy' }},
parsed_params
)
end
@@ -667,6 +711,20 @@ XML
assert_equal "http://", @response.body
end
+ def test_request_format
+ get :test_format, :format => 'html'
+ assert_equal 'text/html', @response.body
+
+ get :test_format, :format => 'json'
+ assert_equal 'application/json', @response.body
+
+ get :test_format, :format => 'xml'
+ assert_equal 'application/xml', @response.body
+
+ get :test_format
+ assert_equal 'text/html', @response.body
+ end
+
def test_should_have_knowledge_of_client_side_cookie_state_even_if_they_are_not_set
cookies['foo'] = 'bar'
get :no_op
@@ -828,3 +886,24 @@ class NamedRoutesControllerTest < ActionController::TestCase
end
end
end
+
+class AnonymousControllerTest < ActionController::TestCase
+ def setup
+ @controller = Class.new(ActionController::Base) do
+ def index
+ render :text => params[:controller]
+ end
+ end.new
+
+ @routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
+ r.draw do
+ get ':controller(/:action(/:id))'
+ end
+ end
+ end
+
+ def test_controller_name
+ get :index
+ assert_equal 'anonymous', @response.body
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index 3e48d97e67..2467654a70 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -38,6 +38,8 @@ class CookiesTest < ActionController::TestCase
head :ok
end
+ alias delete_cookie logout
+
def delete_cookie_with_path
cookies.delete("user_name", :path => '/beaten')
head :ok
@@ -179,6 +181,18 @@ class CookiesTest < ActionController::TestCase
assert_equal({"user_name" => "david"}, @response.cookies)
end
+ def test_setting_the_same_value_to_cookie
+ request.cookies[:user_name] = 'david'
+ get :authenticate
+ assert response.cookies.empty?
+ end
+
+ def test_setting_the_same_value_to_permanent_cookie
+ request.cookies[:user_name] = 'Jamie'
+ get :set_permanent_cookie
+ assert response.cookies, 'user_name' => 'Jamie'
+ end
+
def test_setting_with_escapable_characters
get :set_with_with_escapable_characters
assert_cookie_header "that+%26+guy=foo+%26+bar+%3D%3E+baz; path=/"
@@ -235,23 +249,33 @@ class CookiesTest < ActionController::TestCase
end
def test_expiring_cookie
+ request.cookies[:user_name] = 'Joe'
get :logout
assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
assert_equal({"user_name" => nil}, @response.cookies)
end
def test_delete_cookie_with_path
+ request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_path
assert_cookie_header "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"
end
+ def test_delete_unexisting_cookie
+ request.cookies.clear
+ get :delete_cookie
+ assert @response.cookies.empty?
+ end
+
def test_deleted_cookie_predicate
+ cookies[:user_name] = 'Joe'
cookies.delete("user_name")
assert cookies.deleted?("user_name")
assert_equal false, cookies.deleted?("another")
end
def test_deleted_cookie_predicate_with_mismatching_options
+ cookies[:user_name] = 'Joe'
cookies.delete("user_name", :path => "/path")
assert_equal false, cookies.deleted?("user_name", :path => "/different")
end
@@ -284,6 +308,7 @@ class CookiesTest < ActionController::TestCase
end
def test_delete_and_set_cookie
+ request.cookies[:user_name] = 'Joe'
get :delete_and_set_cookie
assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"
assert_equal({"user_name" => "david"}, @response.cookies)
@@ -387,6 +412,7 @@ class CookiesTest < ActionController::TestCase
end
def test_deleting_cookie_with_all_domain_option
+ request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_domain
assert_response :success
assert_cookie_header "user_name=; domain=.nextangle.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
@@ -413,6 +439,7 @@ class CookiesTest < ActionController::TestCase
end
def test_deleting_cookie_with_all_domain_option_and_tld_length
+ request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_domain_and_tld
assert_response :success
assert_cookie_header "user_name=; domain=.nextangle.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
@@ -441,6 +468,7 @@ class CookiesTest < ActionController::TestCase
def test_deletings_cookie_with_several_preset_domains_using_one_of_these_domains
@request.host = "example2.com"
+ request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_domains
assert_response :success
assert_cookie_header "user_name=; domain=example2.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
@@ -448,19 +476,19 @@ class CookiesTest < ActionController::TestCase
def test_deletings_cookie_with_several_preset_domains_using_other_domain
@request.host = "other-domain.com"
+ request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_domains
assert_response :success
assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
end
-
def test_cookies_hash_is_indifferent_access
- get :symbol_key
- assert_equal "david", cookies[:user_name]
- assert_equal "david", cookies['user_name']
- get :string_key
- assert_equal "dhh", cookies[:user_name]
- assert_equal "dhh", cookies['user_name']
+ get :symbol_key
+ assert_equal "david", cookies[:user_name]
+ assert_equal "david", cookies['user_name']
+ get :string_key
+ assert_equal "dhh", cookies[:user_name]
+ assert_equal "dhh", cookies['user_name']
end
@@ -575,4 +603,4 @@ class CookiesTest < ActionController::TestCase
assert_not_equal expected.split("\n"), header
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 463dd6cb85..4b8d308043 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -63,8 +63,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
get 'secure', :to => redirect("/secure/login")
get 'mobile', :to => redirect(:subdomain => 'mobile')
+ get 'documentation', :to => redirect(:domain => 'example-documentation.com', :path => '')
+ get 'new_documentation', :to => redirect(:path => '/documentation/new')
get 'super_new_documentation', :to => redirect(:host => 'super-docs.com')
+ get 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
+ get 'stores/:name(*rest)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{rest}')
+
get 'youtube_favorites/:youtube_id/:name', :to => redirect(YoutubeFavoritesRedirector)
constraints(lambda { |req| true }) do
@@ -693,11 +698,31 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
verify_redirect 'http://mobile.example.com/mobile'
end
+ def test_redirect_hash_with_domain_and_path
+ get '/documentation'
+ verify_redirect 'http://www.example-documentation.com'
+ end
+
+ def test_redirect_hash_with_path
+ get '/new_documentation'
+ verify_redirect 'http://www.example.com/documentation/new'
+ end
+
def test_redirect_hash_with_host
get '/super_new_documentation?section=top'
verify_redirect 'http://super-docs.com/super_new_documentation?section=top'
end
+ def test_redirect_hash_path_substitution
+ get '/stores/iernest'
+ verify_redirect 'http://stores.example.com/iernest'
+ end
+
+ def test_redirect_hash_path_substitution_with_catch_all
+ get '/stores/iernest/products'
+ verify_redirect 'http://stores.example.com/iernest/products'
+ end
+
def test_redirect_class
get '/youtube_favorites/oHg5SJYRHA0/rick-rolld'
verify_redirect 'http://www.youtube.com/watch?v=oHg5SJYRHA0'
@@ -2452,3 +2477,38 @@ class TestTildeAndMinusPaths < ActionDispatch::IntegrationTest
end
end
+
+class TestRedirectInterpolation < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
+
+ get "/foo/:id" => redirect("/foo/bar/%{id}")
+ get "/bar/:id" => redirect(:path => "/foo/bar/%{id}")
+ get "/foo/bar/:id" => ok
+ end
+ end
+
+ def app; Routes end
+
+ test "redirect escapes interpolated parameters with redirect proc" do
+ get "/foo/1%3E"
+ verify_redirect "http://www.example.com/foo/bar/1%3E"
+ end
+
+ test "redirect escapes interpolated parameters with option proc" do
+ get "/bar/1%3E"
+ verify_redirect "http://www.example.com/foo/bar/1%3E"
+ end
+
+private
+ def verify_redirect(url, status=301)
+ assert_equal status, @response.status
+ assert_equal url, @response.headers['Location']
+ assert_equal expected_redirect_body(url), @response.body
+ end
+
+ def expected_redirect_body(url)
+ %(<html><body>You are being <a href="#{ERB::Util.h(url)}">redirected</a>.</body></html>)
+ end
+end
diff --git a/actionpack/test/fixtures/test/_b_layout_for_partial_with_object.html.erb b/actionpack/test/fixtures/test/_b_layout_for_partial_with_object.html.erb
new file mode 100644
index 0000000000..bdd53014cd
--- /dev/null
+++ b/actionpack/test/fixtures/test/_b_layout_for_partial_with_object.html.erb
@@ -0,0 +1 @@
+<b class="<%= customer.name.downcase %>"><%= yield %></b> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_b_layout_for_partial_with_object_counter.html.erb b/actionpack/test/fixtures/test/_b_layout_for_partial_with_object_counter.html.erb
new file mode 100644
index 0000000000..44d6121297
--- /dev/null
+++ b/actionpack/test/fixtures/test/_b_layout_for_partial_with_object_counter.html.erb
@@ -0,0 +1 @@
+<b data-counter="<%= customer_counter %>"><%= yield %></b> \ No newline at end of file
diff --git a/actionpack/test/fixtures/translations/templates/default.erb b/actionpack/test/fixtures/translations/templates/default.erb
new file mode 100644
index 0000000000..8b70031071
--- /dev/null
+++ b/actionpack/test/fixtures/translations/templates/default.erb
@@ -0,0 +1 @@
+<%= t('.missing', :default => :'.foo') %>
diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb
index 82b62e64f3..63066d40cd 100644
--- a/actionpack/test/template/date_helper_i18n_test.rb
+++ b/actionpack/test/template/date_helper_i18n_test.rb
@@ -12,24 +12,24 @@ class DateHelperDistanceOfTimeInWordsI18nTests < ActiveSupport::TestCase
def test_distance_of_time_in_words_calls_i18n
{ # with include_seconds
- [2.seconds, true] => [:'less_than_x_seconds', 5],
- [9.seconds, true] => [:'less_than_x_seconds', 10],
- [19.seconds, true] => [:'less_than_x_seconds', 20],
- [30.seconds, true] => [:'half_a_minute', nil],
- [59.seconds, true] => [:'less_than_x_minutes', 1],
- [60.seconds, true] => [:'x_minutes', 1],
+ [2.seconds, { :include_seconds => true }] => [:'less_than_x_seconds', 5],
+ [9.seconds, { :include_seconds => true }] => [:'less_than_x_seconds', 10],
+ [19.seconds, { :include_seconds => true }] => [:'less_than_x_seconds', 20],
+ [30.seconds, { :include_seconds => true }] => [:'half_a_minute', nil],
+ [59.seconds, { :include_seconds => true }] => [:'less_than_x_minutes', 1],
+ [60.seconds, { :include_seconds => true }] => [:'x_minutes', 1],
# without include_seconds
- [29.seconds, false] => [:'less_than_x_minutes', 1],
- [60.seconds, false] => [:'x_minutes', 1],
- [44.minutes, false] => [:'x_minutes', 44],
- [61.minutes, false] => [:'about_x_hours', 1],
- [24.hours, false] => [:'x_days', 1],
- [30.days, false] => [:'about_x_months', 1],
- [60.days, false] => [:'x_months', 2],
- [1.year, false] => [:'about_x_years', 1],
- [3.years + 6.months, false] => [:'over_x_years', 3],
- [3.years + 10.months, false] => [:'almost_x_years', 4]
+ [29.seconds, { :include_seconds => false }] => [:'less_than_x_minutes', 1],
+ [60.seconds, { :include_seconds => false }] => [:'x_minutes', 1],
+ [44.minutes, { :include_seconds => false }] => [:'x_minutes', 44],
+ [61.minutes, { :include_seconds => false }] => [:'about_x_hours', 1],
+ [24.hours, { :include_seconds => false }] => [:'x_days', 1],
+ [30.days, { :include_seconds => false }] => [:'about_x_months', 1],
+ [60.days, { :include_seconds => false }] => [:'x_months', 2],
+ [1.year, { :include_seconds => false }] => [:'about_x_years', 1],
+ [3.years + 6.months, { :include_seconds => false }] => [:'over_x_years', 3],
+ [3.years + 10.months, { :include_seconds => false }] => [:'almost_x_years', 4]
}.each do |passed, expected|
assert_distance_of_time_in_words_translates_key passed, expected
@@ -37,7 +37,7 @@ class DateHelperDistanceOfTimeInWordsI18nTests < ActiveSupport::TestCase
end
def assert_distance_of_time_in_words_translates_key(passed, expected)
- diff, include_seconds = *passed
+ diff, passed_options = *passed
key, count = *expected
to = @from + diff
@@ -45,7 +45,12 @@ class DateHelperDistanceOfTimeInWordsI18nTests < ActiveSupport::TestCase
options[:count] = count if count
I18n.expects(:t).with(key, options)
- distance_of_time_in_words(@from, to, include_seconds, :locale => 'en')
+ distance_of_time_in_words(@from, to, passed_options.merge(:locale => 'en'))
+ end
+
+ def test_time_ago_in_words_passes_locale
+ I18n.expects(:t).with(:less_than_x_minutes, :scope => :'datetime.distance_in_words', :count => 1, :locale => 'ru')
+ time_ago_in_words(15.seconds.ago, :locale => 'ru')
end
def test_distance_of_time_pluralizations
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index 4835bc578f..ff85a675a2 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -21,56 +21,80 @@ class DateHelperTest < ActionView::TestCase
def assert_distance_of_time_in_words(from, to=nil)
to ||= from
- # 0..1 with include_seconds
- assert_equal "less than 5 seconds", distance_of_time_in_words(from, to + 0.seconds, true)
- assert_equal "less than 5 seconds", distance_of_time_in_words(from, to + 4.seconds, true)
- assert_equal "less than 10 seconds", distance_of_time_in_words(from, to + 5.seconds, true)
- assert_equal "less than 10 seconds", distance_of_time_in_words(from, to + 9.seconds, true)
- assert_equal "less than 20 seconds", distance_of_time_in_words(from, to + 10.seconds, true)
- assert_equal "less than 20 seconds", distance_of_time_in_words(from, to + 19.seconds, true)
- assert_equal "half a minute", distance_of_time_in_words(from, to + 20.seconds, true)
- assert_equal "half a minute", distance_of_time_in_words(from, to + 39.seconds, true)
- assert_equal "less than a minute", distance_of_time_in_words(from, to + 40.seconds, true)
- assert_equal "less than a minute", distance_of_time_in_words(from, to + 59.seconds, true)
- assert_equal "1 minute", distance_of_time_in_words(from, to + 60.seconds, true)
- assert_equal "1 minute", distance_of_time_in_words(from, to + 89.seconds, true)
-
- # First case 0..1
+ # 0..1 minute with :include_seconds => true
+ assert_equal "less than 5 seconds", distance_of_time_in_words(from, to + 0.seconds, :include_seconds => true)
+ assert_equal "less than 5 seconds", distance_of_time_in_words(from, to + 4.seconds, :include_seconds => true)
+ assert_equal "less than 10 seconds", distance_of_time_in_words(from, to + 5.seconds, :include_seconds => true)
+ assert_equal "less than 10 seconds", distance_of_time_in_words(from, to + 9.seconds, :include_seconds => true)
+ assert_equal "less than 20 seconds", distance_of_time_in_words(from, to + 10.seconds, :include_seconds => true)
+ assert_equal "less than 20 seconds", distance_of_time_in_words(from, to + 19.seconds, :include_seconds => true)
+ assert_equal "half a minute", distance_of_time_in_words(from, to + 20.seconds, :include_seconds => true)
+ assert_equal "half a minute", distance_of_time_in_words(from, to + 39.seconds, :include_seconds => true)
+ assert_equal "less than a minute", distance_of_time_in_words(from, to + 40.seconds, :include_seconds => true)
+ assert_equal "less than a minute", distance_of_time_in_words(from, to + 59.seconds, :include_seconds => true)
+ assert_equal "1 minute", distance_of_time_in_words(from, to + 60.seconds, :include_seconds => true)
+ assert_equal "1 minute", distance_of_time_in_words(from, to + 89.seconds, :include_seconds => true)
+
+ # 0..1 minute with :include_seconds => false
+ assert_equal "less than a minute", distance_of_time_in_words(from, to + 0.seconds, :include_seconds => false)
+ assert_equal "less than a minute", distance_of_time_in_words(from, to + 4.seconds, :include_seconds => false)
+ assert_equal "less than a minute", distance_of_time_in_words(from, to + 5.seconds, :include_seconds => false)
+ assert_equal "less than a minute", distance_of_time_in_words(from, to + 9.seconds, :include_seconds => false)
+ assert_equal "less than a minute", distance_of_time_in_words(from, to + 10.seconds, :include_seconds => false)
+ assert_equal "less than a minute", distance_of_time_in_words(from, to + 19.seconds, :include_seconds => false)
+ assert_equal "less than a minute", distance_of_time_in_words(from, to + 20.seconds, :include_seconds => false)
+ assert_equal "1 minute", distance_of_time_in_words(from, to + 39.seconds, :include_seconds => false)
+ assert_equal "1 minute", distance_of_time_in_words(from, to + 40.seconds, :include_seconds => false)
+ assert_equal "1 minute", distance_of_time_in_words(from, to + 59.seconds, :include_seconds => false)
+ assert_equal "1 minute", distance_of_time_in_words(from, to + 60.seconds, :include_seconds => false)
+ assert_equal "1 minute", distance_of_time_in_words(from, to + 89.seconds, :include_seconds => false)
+
+ # Note that we are including a 30-second boundary around the interval we
+ # want to test. For instance, "1 minute" is actually 30s to 1m29s. The
+ # reason for doing this is simple -- in `distance_of_time_to_words`, when we
+ # take the distance between our two Time objects in seconds and convert it
+ # to minutes, we round the number. So 29s gets rounded down to 0m, 30s gets
+ # rounded up to 1m, and 1m29s gets rounded down to 1m. A similar thing
+ # happens with the other cases.
+
+ # First case 0..1 minute
assert_equal "less than a minute", distance_of_time_in_words(from, to + 0.seconds)
assert_equal "less than a minute", distance_of_time_in_words(from, to + 29.seconds)
assert_equal "1 minute", distance_of_time_in_words(from, to + 30.seconds)
assert_equal "1 minute", distance_of_time_in_words(from, to + 1.minutes + 29.seconds)
- # 2..44
+ # 2 minutes up to 45 minutes
assert_equal "2 minutes", distance_of_time_in_words(from, to + 1.minutes + 30.seconds)
assert_equal "44 minutes", distance_of_time_in_words(from, to + 44.minutes + 29.seconds)
- # 45..89
+ # 45 minutes up to 90 minutes
assert_equal "about 1 hour", distance_of_time_in_words(from, to + 44.minutes + 30.seconds)
assert_equal "about 1 hour", distance_of_time_in_words(from, to + 89.minutes + 29.seconds)
- # 90..1439
+ # 90 minutes up to 24 hours
assert_equal "about 2 hours", distance_of_time_in_words(from, to + 89.minutes + 30.seconds)
assert_equal "about 24 hours", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 29.seconds)
- # 1440..2519
+ # 24 hours up to 42 hours
assert_equal "1 day", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 30.seconds)
assert_equal "1 day", distance_of_time_in_words(from, to + 41.hours + 59.minutes + 29.seconds)
- # 2520..43199
+ # 42 hours up to 30 days
assert_equal "2 days", distance_of_time_in_words(from, to + 41.hours + 59.minutes + 30.seconds)
assert_equal "3 days", distance_of_time_in_words(from, to + 2.days + 12.hours)
assert_equal "30 days", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 29.seconds)
- # 43200..86399
+ # 30 days up to 60 days
assert_equal "about 1 month", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 30.seconds)
- assert_equal "about 1 month", distance_of_time_in_words(from, to + 59.days + 23.hours + 59.minutes + 29.seconds)
+ assert_equal "about 1 month", distance_of_time_in_words(from, to + 44.days + 23.hours + 59.minutes + 29.seconds)
+ assert_equal "about 2 months", distance_of_time_in_words(from, to + 44.days + 23.hours + 59.minutes + 30.seconds)
+ assert_equal "about 2 months", distance_of_time_in_words(from, to + 59.days + 23.hours + 59.minutes + 29.seconds)
- # 86400..525599
+ # 60 days up to 365 days
assert_equal "2 months", distance_of_time_in_words(from, to + 59.days + 23.hours + 59.minutes + 30.seconds)
assert_equal "12 months", distance_of_time_in_words(from, to + 1.years - 31.seconds)
- # > 525599
+ # >= 365 days
assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years - 30.seconds)
assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years + 3.months - 1.day)
assert_equal "over 1 year", distance_of_time_in_words(from, to + 1.years + 6.months)
@@ -95,7 +119,8 @@ class DateHelperTest < ActionView::TestCase
# test to < from
assert_equal "about 4 hours", distance_of_time_in_words(from + 4.hours, to)
- assert_equal "less than 20 seconds", distance_of_time_in_words(from + 19.seconds, to, true)
+ assert_equal "less than 20 seconds", distance_of_time_in_words(from + 19.seconds, to, :include_seconds => true)
+ assert_equal "less than a minute", distance_of_time_in_words(from + 19.seconds, to, :include_seconds => false)
end
def test_distance_in_words
@@ -103,6 +128,11 @@ class DateHelperTest < ActionView::TestCase
assert_distance_of_time_in_words(from)
end
+ def test_time_ago_in_words_passes_include_seconds
+ assert_equal "less than 20 seconds", time_ago_in_words(15.seconds.ago, :include_seconds => true)
+ assert_equal "less than a minute", time_ago_in_words(15.seconds.ago, :include_seconds => false)
+ end
+
def test_distance_in_words_with_time_zones
from = Time.mktime(2004, 6, 6, 21, 45, 0)
assert_distance_of_time_in_words(from.in_time_zone('Alaska'))
@@ -148,10 +178,10 @@ class DateHelperTest < ActionView::TestCase
assert_equal "about 1 hour", distance_of_time_in_words(60.minutes)
# include seconds
- assert_equal "half a minute", distance_of_time_in_words(39.seconds, 0, true)
- assert_equal "less than a minute", distance_of_time_in_words(40.seconds, 0, true)
- assert_equal "less than a minute", distance_of_time_in_words(59.seconds, 0, true)
- assert_equal "1 minute", distance_of_time_in_words(60.seconds, 0, true)
+ assert_equal "half a minute", distance_of_time_in_words(39.seconds, 0, :include_seconds => true)
+ assert_equal "less than a minute", distance_of_time_in_words(40.seconds, 0, :include_seconds => true)
+ assert_equal "less than a minute", distance_of_time_in_words(59.seconds, 0, :include_seconds => true)
+ assert_equal "1 minute", distance_of_time_in_words(60.seconds, 0, :include_seconds => true)
end
def test_time_ago_in_words
@@ -587,7 +617,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_html_options
- expected = expected = %(<select id="date_minute" name="date[minute]" class="selector" accesskey="M">\n)
+ expected = %(<select id="date_minute" name="date[minute]" class="selector" accesskey="M">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -968,6 +998,15 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :date_separator => " / ", :discard_month => true, :discard_day => true, :start_year => 2003, :end_year => 2005, :prefix => "date[first]"})
end
+ def test_select_date_with_hidden
+ expected = %(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003"/>\n)
+ expected << %(<input id="date_first_month" name="date[first][month]" type="hidden" value="8" />\n)
+ expected << %(<input id="date_first_day" name="date[first][day]" type="hidden" value="16" />\n)
+
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :prefix => "date[first]", :use_hidden => true })
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :date_separator => " / ", :prefix => "date[first]", :use_hidden => true })
+ end
+
def test_select_datetime
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -1204,6 +1243,18 @@ class DateHelperTest < ActionView::TestCase
:prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year', :hour => 'Choose hour', :minute => 'Choose minute'})
end
+ def test_select_datetime_with_hidden
+ expected = %(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n)
+ expected << %(<input id="date_first_month" name="date[first][month]" type="hidden" value="8" />\n)
+ expected << %(<input id="date_first_day" name="date[first][day]" type="hidden" value="16" />\n)
+ expected << %(<input id="date_first_hour" name="date[first][hour]" type="hidden" value="8" />\n)
+ expected << %(<input id="date_first_minute" name="date[first][minute]" type="hidden" value="4" />\n)
+
+ assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), :prefix => "date[first]", :use_hidden => true)
+ assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), :datetime_separator => "&mdash;", :date_separator => "/",
+ :time_separator => ":", :prefix => "date[first]", :use_hidden => true)
+ end
+
def test_select_time
expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
@@ -1379,6 +1430,17 @@ class DateHelperTest < ActionView::TestCase
:prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'})
end
+ def test_select_time_with_hidden
+ expected = %(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n)
+ expected << %(<input id="date_first_month" name="date[first][month]" type="hidden" value="8" />\n)
+ expected << %(<input id="date_first_day" name="date[first][day]" type="hidden" value="16" />\n)
+ expected << %(<input id="date_first_hour" name="date[first][hour]" type="hidden" value="8" />\n)
+ expected << %(<input id="date_first_minute" name="date[first][minute]" type="hidden" value="4" />\n)
+
+ assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :prefix => "date[first]", :use_hidden => true)
+ assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :time_separator => ":", :prefix => "date[first]", :use_hidden => true)
+ end
+
def test_date_select
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index a714264909..beb3ea752a 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -350,36 +350,52 @@ class FormHelperTest < ActionView::TestCase
text_field("user", "email", :type => "email")
end
- def test_check_box
+ def test_check_box_is_html_safe
assert check_box("post", "secret").html_safe?
+ end
+
+ def test_check_box_checked_if_object_value_is_same_that_check_value
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
check_box("post", "secret")
)
+ end
+
+ def test_check_box_not_checked_if_object_value_is_same_that_unchecked_value
@post.secret = 0
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input id="post_secret" name="post[secret]" type="checkbox" value="1" />',
check_box("post", "secret")
)
+ end
+
+ def test_check_box_checked_if_option_checked_is_present
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
check_box("post", "secret" ,{"checked"=>"checked"})
)
+ end
+
+ def test_check_box_checked_if_object_value_is_true
@post.secret = true
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
check_box("post", "secret")
)
+
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
check_box("post", "secret?")
)
+ end
+ def test_check_box_checked_if_object_value_includes_checked_value
@post.secret = ['0']
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input id="post_secret" name="post[secret]" type="checkbox" value="1" />',
check_box("post", "secret")
)
+
@post.secret = ['1']
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
@@ -392,12 +408,92 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal('<input id="post_secret" name="post[secret]" type="checkbox" value="1" />', check_box("post", "secret", :include_hidden => false))
end
- def test_check_box_with_explicit_checked_and_unchecked_values
+ def test_check_box_with_explicit_checked_and_unchecked_values_when_object_value_is_string
@post.secret = "on"
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="off" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="on" />',
check_box("post", "secret", {}, "on", "off")
)
+
+ @post.secret = "off"
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="off" /><input id="post_secret" name="post[secret]" type="checkbox" value="on" />',
+ check_box("post", "secret", {}, "on", "off")
+ )
+ end
+
+ def test_check_box_with_explicit_checked_and_unchecked_values_when_object_value_is_boolean
+ @post.secret = false
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="true" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="false" />',
+ check_box("post", "secret", {}, false, true)
+ )
+
+ @post.secret = true
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="true" /><input id="post_secret" name="post[secret]" type="checkbox" value="false" />',
+ check_box("post", "secret", {}, false, true)
+ )
+ end
+
+ def test_check_box_with_explicit_checked_and_unchecked_values_when_object_value_is_integer
+ @post.secret = 0
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="1" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="0" />',
+ check_box("post", "secret", {}, 0, 1)
+ )
+
+ @post.secret = 1
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="1" /><input id="post_secret" name="post[secret]" type="checkbox" value="0" />',
+ check_box("post", "secret", {}, 0, 1)
+ )
+
+ @post.secret = 2
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="1" /><input id="post_secret" name="post[secret]" type="checkbox" value="0" />',
+ check_box("post", "secret", {}, 0, 1)
+ )
+ end
+
+ def test_check_box_with_explicit_checked_and_unchecked_values_when_object_value_is_float
+ @post.secret = 0.0
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="1" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="0" />',
+ check_box("post", "secret", {}, 0, 1)
+ )
+
+ @post.secret = 1.1
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="1" /><input id="post_secret" name="post[secret]" type="checkbox" value="0" />',
+ check_box("post", "secret", {}, 0, 1)
+ )
+
+ @post.secret = 2.2
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="1" /><input id="post_secret" name="post[secret]" type="checkbox" value="0" />',
+ check_box("post", "secret", {}, 0, 1)
+ )
+ end
+
+ def test_check_box_with_explicit_checked_and_unchecked_values_when_object_value_is_big_decimal
+ @post.secret = BigDecimal.new(0)
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="1" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="0" />',
+ check_box("post", "secret", {}, 0, 1)
+ )
+
+ @post.secret = BigDecimal.new(1)
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="1" /><input id="post_secret" name="post[secret]" type="checkbox" value="0" />',
+ check_box("post", "secret", {}, 0, 1)
+ )
+
+ @post.secret = BigDecimal.new(2.2, 1)
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="1" /><input id="post_secret" name="post[secret]" type="checkbox" value="0" />',
+ check_box("post", "secret", {}, 0, 1)
+ )
end
def test_check_box_with_nil_unchecked_value
@@ -2169,6 +2265,23 @@ class FormHelperTest < ActionView::TestCase
ActionView::Base.default_form_builder = old_default_form_builder
end
+ def test_lazy_loading_default_form_builder
+ old_default_form_builder, ActionView::Base.default_form_builder =
+ ActionView::Base.default_form_builder, "FormHelperTest::LabelledFormBuilder"
+
+ form_for(@post) do |f|
+ concat f.text_field(:title)
+ end
+
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ ensure
+ ActionView::Base.default_form_builder = old_default_form_builder
+ end
+
def test_fields_for_with_labelled_builder
output_buffer = fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
concat f.text_field(:title)
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index 0b96985002..fe7607ee26 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -42,36 +42,6 @@ class JavaScriptHelperTest < ActionView::TestCase
assert_instance_of ActiveSupport::SafeBuffer, escape_javascript(ActiveSupport::SafeBuffer.new(given))
end
- def test_button_to_function
- assert_dom_equal %(<input type="button" onclick="alert('Hello world!');" value="Greeting" />),
- button_to_function("Greeting", "alert('Hello world!')")
- end
-
- def test_button_to_function_with_onclick
- assert_dom_equal "<input onclick=\"alert('Goodbye World :('); alert('Hello world!');\" type=\"button\" value=\"Greeting\" />",
- button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')")
- end
-
- def test_button_to_function_without_function
- assert_dom_equal "<input onclick=\";\" type=\"button\" value=\"Greeting\" />",
- button_to_function("Greeting")
- end
-
- def test_link_to_function
- assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>),
- link_to_function("Greeting", "alert('Hello world!')")
- end
-
- def test_link_to_function_with_existing_onclick
- assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>),
- link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')")
- end
-
- def test_function_with_href
- assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>),
- link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/')
- end
-
def test_javascript_tag
self.output_buffer = 'foo'
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 43b176df3c..cdaca56ef2 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -233,11 +233,26 @@ module RenderTestCases
def test_render_partial_with_nil_values_in_collection
assert_equal "Hello: davidHello: Anonymous", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), nil ])
end
-
+
def test_render_partial_with_layout_using_collection_and_template
assert_equal "<b>Hello: Amazon</b><b>Hello: Yahoo</b>", @view.render(:partial => "test/customer", :layout => 'test/b_layout_for_partial', :collection => [ Customer.new("Amazon"), Customer.new("Yahoo") ])
end
+ def test_render_partial_with_layout_using_collection_and_template_makes_current_item_available_in_layout
+ assert_equal '<b class="amazon">Hello: Amazon</b><b class="yahoo">Hello: Yahoo</b>',
+ @view.render(:partial => "test/customer", :layout => 'test/b_layout_for_partial_with_object', :collection => [ Customer.new("Amazon"), Customer.new("Yahoo") ])
+ end
+
+ def test_render_partial_with_layout_using_collection_and_template_makes_current_item_counter_available_in_layout
+ assert_equal '<b data-counter="0">Hello: Amazon</b><b data-counter="1">Hello: Yahoo</b>',
+ @view.render(:partial => "test/customer", :layout => 'test/b_layout_for_partial_with_object_counter', :collection => [ Customer.new("Amazon"), Customer.new("Yahoo") ])
+ end
+
+ def test_render_partial_with_layout_using_object_and_template_makes_object_available_in_layout
+ assert_equal '<b class="amazon">Hello: Amazon</b>',
+ @view.render(:partial => "test/customer", :layout => 'test/b_layout_for_partial_with_object', :object => Customer.new("Amazon"))
+ end
+
def test_render_partial_with_empty_array_should_return_nil
assert_nil @view.render(:partial => [])
end
@@ -310,7 +325,7 @@ module RenderTestCases
ActionView::Template.register_template_handler :foo, CustomHandler
assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)
end
-
+
def test_render_knows_about_types_registered_when_extensions_are_checked_earlier_in_initialization
ActionView::Template::Handlers.extensions
ActionView::Template.register_template_handler :foo, CustomHandler
diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb
index 32b33b4a55..7161d107b3 100644
--- a/actionpack/test/template/tag_helper_test.rb
+++ b/actionpack/test/template/tag_helper_test.rb
@@ -91,6 +91,11 @@ class TagHelperTest < ActionView::TestCase
assert_equal "<![CDATA[<hello world>]]>", cdata_section("<hello world>")
end
+ def test_cdata_section_splitted
+ assert_equal "<![CDATA[hello]]]]><![CDATA[>world]]>", cdata_section("hello]]>world")
+ assert_equal "<![CDATA[hello]]]]><![CDATA[>world]]]]><![CDATA[>again]]>", cdata_section("hello]]>world]]>again")
+ end
+
def test_escape_once
assert_equal '1 &lt; 2 &amp; 3', escape_once('1 < 2 &amp; 3')
end
diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index 397de9c2ce..97777ccff0 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -11,7 +11,8 @@ class TranslationHelperTest < ActiveSupport::TestCase
:translations => {
:templates => {
:found => { :foo => 'Foo' },
- :array => { :foo => { :bar => 'Foo Bar' } }
+ :array => { :foo => { :bar => 'Foo Bar' } },
+ :default => { :foo => 'Foo' }
},
:foo => 'Foo',
:hello => '<a>Hello World</a>',
@@ -71,6 +72,10 @@ class TranslationHelperTest < ActiveSupport::TestCase
assert_equal 'Foo Bar', @view.render(:file => 'translations/templates/array').strip
end
+ def test_default_lookup_scoped_by_partial
+ assert_equal 'Foo', view.render(:file => 'translations/templates/default').strip
+ end
+
def test_missing_translation_scoped_by_partial
expected = '<span class="translation_missing" title="translation missing: en.translations.templates.missing.missing">Missing</span>'
assert_equal expected, view.render(:file => 'translations/templates/missing').strip
@@ -102,4 +107,22 @@ class TranslationHelperTest < ActiveSupport::TestCase
def test_translation_returning_an_array_ignores_html_suffix
assert_equal ["foo", "bar"], translate(:'translations.array_html')
end
+
+ def test_translate_with_default_named_html
+ translation = translate(:'translations.missing', :default => :'translations.hello_html')
+ assert_equal '<a>Hello World</a>', translation
+ assert translation.html_safe?
+ end
+
+ def test_translate_with_two_defaults_named_html
+ translation = translate(:'translations.missing', :default => [:'translations.missing_html', :'translations.hello_html'])
+ assert_equal '<a>Hello World</a>', translation
+ assert translation.html_safe?
+ end
+
+ def test_translate_with_last_default_named_html
+ translation = translate(:'translations.missing', :default => [:'translations.missing', :'translations.hello_html'])
+ assert_equal '<a>Hello World</a>', translation
+ assert translation.html_safe?
+ end
end