aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG.md12
-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.rb27
-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.rb63
-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, 651 insertions, 236 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 9f1c44ca66..99602f14d9 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,17 @@
## 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
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
index 369741fb12..374645c9ce 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 7db7d270e5..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.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 0c717c8503..196f655de3 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 03b7396d5b..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
@@ -579,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
@@ -687,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
@@ -848,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