aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
authorLeonel Galán <leonel@getstealz.com>2017-04-25 10:39:46 -0500
committerGitHub <noreply@github.com>2017-04-25 10:39:46 -0500
commitf5b2a0ef408f01dd9f12b456f5e4bb41ebb4cb76 (patch)
tree4afabfef205d5fbd546946a16bc6cf5eb36279e9 /activesupport
parent8648f289f01656b2258687126108c389127108e0 (diff)
parentb32d64df5e5160e25804c7019e2da76bd115f3a6 (diff)
downloadrails-f5b2a0ef408f01dd9f12b456f5e4bb41ebb4cb76.tar.gz
rails-f5b2a0ef408f01dd9f12b456f5e4bb41ebb4cb76.tar.bz2
rails-f5b2a0ef408f01dd9f12b456f5e4bb41ebb4cb76.zip
Merge branch 'master' into bug/filtered_parameters_class
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG.md20
-rw-r--r--activesupport/lib/active_support/callbacks.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/prepend_and_append.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb68
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb36
-rw-r--r--activesupport/lib/active_support/deprecation.rb2
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb29
-rw-r--r--activesupport/lib/active_support/deprecation/reporting.rb2
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb13
-rw-r--r--activesupport/lib/active_support/notifications.rb2
-rw-r--r--activesupport/lib/active_support/testing/time_helpers.rb1
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb36
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb3
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/libxmlsax.rb5
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogirisax.rb4
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb3
-rw-r--r--activesupport/test/core_ext/module_test.rb32
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb7
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb6
-rw-r--r--activesupport/test/deprecation_test.rb35
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb13
-rw-r--r--activesupport/test/xml_mini/xml_mini_engine_test.rb5
26 files changed, 270 insertions, 73 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 6f99155199..a4fc1e34eb 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1 +1,21 @@
+* Pass gem name and deprecation horizon to deprecation notifications.
+
+ *Willem van Bergen*
+
+* Add support for `:offset` and `:zone` to `ActiveSupport::TimeWithZone#change`
+
+ *Andrew White*
+
+* Add support for `:offset` to `Time#change`
+
+ Fixes #28723.
+
+ *Andrew White*
+
+* Add `fetch_values` for `HashWithIndifferentAccess`
+
+ The method was originally added to `Hash` in Ruby 2.3.0.
+
+ *Josh Pencheon*
+
Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/activesupport/CHANGELOG.md) for previous changes.
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index ea71569ca8..d771cab68b 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -512,7 +512,6 @@ module ActiveSupport
end
end
- # An Array with a compile method.
class CallbackChain #:nodoc:#
include Enumerable
diff --git a/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
index 16a6789f8d..88a34128c9 100644
--- a/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
+++ b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
@@ -1,7 +1,7 @@
class Array
# The human way of thinking about adding stuff to the end of a list is with append.
- alias_method :append, :<<
+ alias_method :append, :push unless [].respond_to?(:append)
# The human way of thinking about adding stuff to the beginning of a list is with prepend.
- alias_method :prepend, :unshift
+ alias_method :prepend, :unshift unless [].respond_to?(:prepend)
end
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index d553406dff..0f59c754fe 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -79,6 +79,9 @@ class Date
# date.to_time(:local) # => 2007-11-10 00:00:00 0800
#
# date.to_time(:utc) # => 2007-11-10 00:00:00 UTC
+ #
+ # NOTE: The :local timezone is Ruby's *process* timezone, i.e. ENV['TZ'].
+ # If the *application's* timezone is needed, then use +in_time_zone+ instead.
def to_time(form = :local)
raise ArgumentError, "Expected :local or :utc, got #{form.inspect}." unless [:local, :utc].include?(form)
::Time.send(form, year, month, day)
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index 90d7d2947f..3a4ae6cb8b 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -1,28 +1,50 @@
module Enumerable
- # Calculates a sum from the elements.
+ # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
+ # when we omit an identity.
#
- # payments.sum { |p| p.price * p.tax_rate }
- # payments.sum(&:price)
- #
- # The latter is a shortcut for:
- #
- # payments.inject(0) { |sum, p| sum + p.price }
- #
- # It can also calculate the sum without the use of a block.
- #
- # [5, 15, 10].sum # => 30
- # ['foo', 'bar'].sum # => "foobar"
- # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
- #
- # The default sum of an empty list is zero. You can override this default:
- #
- # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
- def sum(identity = nil, &block)
- if block_given?
- map(&block).sum(identity)
- else
- sum = identity ? inject(identity, :+) : inject(:+)
- sum || identity || 0
+ # We tried shimming it to attempt the fast native method, rescue TypeError,
+ # and fall back to the compatible implementation, but that's much slower than
+ # just calling the compat method in the first place.
+ if Enumerable.instance_methods(false).include?(:sum) && !((?a..?b).sum rescue false)
+ # We can't use Refinements here because Refinements with Module which will be prepended
+ # doesn't work well https://bugs.ruby-lang.org/issues/13446
+ alias :_original_sum_with_required_identity :sum
+ private :_original_sum_with_required_identity
+ # Calculates a sum from the elements.
+ #
+ # payments.sum { |p| p.price * p.tax_rate }
+ # payments.sum(&:price)
+ #
+ # The latter is a shortcut for:
+ #
+ # payments.inject(0) { |sum, p| sum + p.price }
+ #
+ # It can also calculate the sum without the use of a block.
+ #
+ # [5, 15, 10].sum # => 30
+ # ['foo', 'bar'].sum # => "foobar"
+ # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
+ #
+ # The default sum of an empty list is zero. You can override this default:
+ #
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
+ def sum(identity = nil, &block)
+ if identity
+ _original_sum_with_required_identity(identity, &block)
+ elsif block_given?
+ map(&block).sum(identity)
+ else
+ inject(:+) || 0
+ end
+ end
+ else
+ def sum(identity = nil, &block)
+ if block_given?
+ map(&block).sum(identity)
+ else
+ sum = identity ? inject(identity, :+) : inject(:+)
+ sum || identity || 0
+ end
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index cdf27f49ad..85ab739095 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -267,7 +267,10 @@ class Module
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def respond_to_missing?(name, include_private = false)
- #{target}.respond_to?(name, include_private)
+ # It may look like an oversight, but we deliberately do not pass
+ # +include_private+, because they do not get delegated.
+
+ #{target}.respond_to?(name) || super
end
def method_missing(method, *args, &block)
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 7b7aeef25a..d3f23f4663 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -107,21 +107,22 @@ class Time
# to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
# <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only
# the hour is passed, then minute, sec, usec and nsec is set to 0. If the hour
- # and minute is passed, then sec, usec and nsec is set to 0. The +options+
- # parameter takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>,
- # <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>
- # <tt>:nsec</tt>. Pass either <tt>:usec</tt> or <tt>:nsec</tt>, not both.
+ # and minute is passed, then sec, usec and nsec is set to 0. The +options+ parameter
+ # takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>,
+ # <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>,
+ # <tt>:offset</tt>. Pass either <tt>:usec</tt> or <tt>:nsec</tt>, not both.
#
# Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
def change(options)
- new_year = options.fetch(:year, year)
- new_month = options.fetch(:month, month)
- new_day = options.fetch(:day, day)
- new_hour = options.fetch(:hour, hour)
- new_min = options.fetch(:min, options[:hour] ? 0 : min)
- new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
+ new_year = options.fetch(:year, year)
+ new_month = options.fetch(:month, month)
+ new_day = options.fetch(:day, day)
+ new_hour = options.fetch(:hour, hour)
+ new_min = options.fetch(:min, options[:hour] ? 0 : min)
+ new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
+ new_offset = options.fetch(:offset, nil)
if new_nsec = options[:nsec]
raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec]
@@ -130,13 +131,18 @@ class Time
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
end
- if utc?
- ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
+ raise ArgumentError, "argument out of range" if new_usec >= 1000000
+
+ new_sec += Rational(new_usec, 1000000)
+
+ if new_offset
+ ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, new_offset)
+ elsif utc?
+ ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec)
elsif zone
- ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
+ ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec)
else
- raise ArgumentError, "argument out of range" if new_usec >= 1000000
- ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
+ ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, utc_offset)
end
end
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index 72c74e966a..35cddcfde6 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -30,7 +30,7 @@ module ActiveSupport
attr_accessor :deprecation_horizon
# It accepts two parameters on initialization. The first is a version of library
- # and the second is a library name
+ # and the second is a library name.
#
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
def initialize(deprecation_horizon = "5.3", gem_name = "Rails")
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index 1d1354c23e..a9a182f212 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -9,18 +9,18 @@ module ActiveSupport
class Deprecation
# Default warning behaviors per Rails.env.
DEFAULT_BEHAVIORS = {
- raise: ->(message, callstack) {
+ raise: ->(message, callstack, deprecation_horizon, gem_name) {
e = DeprecationException.new(message)
e.set_backtrace(callstack.map(&:to_s))
raise e
},
- stderr: ->(message, callstack) {
+ stderr: ->(message, callstack, deprecation_horizon, gem_name) {
$stderr.puts(message)
$stderr.puts callstack.join("\n ") if debug
},
- log: ->(message, callstack) {
+ log: ->(message, callstack, deprecation_horizon, gem_name) {
logger =
if defined?(Rails.logger) && Rails.logger
Rails.logger
@@ -32,12 +32,16 @@ module ActiveSupport
logger.debug callstack.join("\n ") if debug
},
- notify: ->(message, callstack) {
- ActiveSupport::Notifications.instrument("deprecation.rails",
- message: message, callstack: callstack)
+ notify: ->(message, callstack, deprecation_horizon, gem_name) {
+ notification_name = "deprecation.#{gem_name.underscore.tr('/', '_')}"
+ ActiveSupport::Notifications.instrument(notification_name,
+ message: message,
+ callstack: callstack,
+ gem_name: gem_name,
+ deprecation_horizon: deprecation_horizon)
},
- silence: ->(message, callstack) {},
+ silence: ->(message, callstack, deprecation_horizon, gem_name) {},
}
# Behavior module allows to determine how to display deprecation messages.
@@ -83,8 +87,17 @@ module ActiveSupport
# # custom stuff
# }
def behavior=(behavior)
- @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
+ @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
end
+
+ private
+ def arity_coerce(behavior)
+ if behavior.arity == 4 || behavior.arity == -1
+ behavior
+ else
+ -> message, callstack, _, _ { behavior.call(message, callstack) }
+ end
+ end
end
end
end
diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb
index 58c5c50e30..851d8eeda1 100644
--- a/activesupport/lib/active_support/deprecation/reporting.rb
+++ b/activesupport/lib/active_support/deprecation/reporting.rb
@@ -18,7 +18,7 @@ module ActiveSupport
callstack ||= caller_locations(2)
deprecation_message(callstack, message).tap do |m|
- behavior.each { |b| b.call(m, callstack) }
+ behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
end
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 1e1d6d3aac..3b185dd86b 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -195,6 +195,19 @@ module ActiveSupport
indices.collect { |key| self[convert_key(key)] }
end
+ # Returns an array of the values at the specified indices, but also
+ # raises an exception when one of the keys can't be found.
+ #
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
+ # hash[:a] = 'x'
+ # hash[:b] = 'y'
+ # hash.fetch_values('a', 'b') # => ["x", "y"]
+ # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
+ # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
+ def fetch_values(*indices, &block)
+ indices.collect { |key| fetch(key, &block) }
+ end if Hash.method_defined?(:fetch_values)
+
# Returns a shallow copy of the hash.
#
# hash = ActiveSupport::HashWithIndifferentAccess.new({ a: { b: 'b' } })
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 2df819e554..37dfdc0422 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -64,6 +64,8 @@ module ActiveSupport
# If an exception happens during that particular instrumentation the payload will
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
# the name of the exception class, and the exception message.
+ # The <tt>:exception_object</tt> key of the payload will have the exception
+ # itself as the value.
#
# As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
# is able to take the arguments as they come and provide an object-oriented
diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb
index 07c9be0604..3d9ff99729 100644
--- a/activesupport/lib/active_support/testing/time_helpers.rb
+++ b/activesupport/lib/active_support/testing/time_helpers.rb
@@ -1,4 +1,5 @@
require "active_support/core_ext/string/strip" # for strip_heredoc
+require "active_support/core_ext/time/calculations"
require "concurrent/map"
module ActiveSupport
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index b0dd6b7e8c..ecb9fb6785 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -330,6 +330,42 @@ module ActiveSupport
since(-other)
end
+ # Returns a new +ActiveSupport::TimeWithZone+ where one or more of the elements have
+ # been changed according to the +options+ parameter. The time options (<tt>:hour</tt>,
+ # <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly,
+ # so if only the hour is passed, then minute, sec, usec and nsec is set to 0. If the
+ # hour and minute is passed, then sec, usec and nsec is set to 0. The +options+
+ # parameter takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>,
+ # <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt>,
+ # <tt>:nsec</tt>, <tt>:offset</tt>, <tt>:zone</tt>. Pass either <tt>:usec</tt>
+ # or <tt>:nsec</tt>, not both. Similarly, pass either <tt>:zone</tt> or
+ # <tt>:offset</tt>, not both.
+ #
+ # t = Time.zone.now # => Fri, 14 Apr 2017 11:45:15 EST -05:00
+ # t.change(year: 2020) # => Tue, 14 Apr 2020 11:45:15 EST -05:00
+ # t.change(hour: 12) # => Fri, 14 Apr 2017 12:00:00 EST -05:00
+ # t.change(min: 30) # => Fri, 14 Apr 2017 11:30:00 EST -05:00
+ # t.change(offset: "-10:00") # => Fri, 14 Apr 2017 11:45:15 HST -10:00
+ # t.change(zone: "Hawaii") # => Fri, 14 Apr 2017 11:45:15 HST -10:00
+ def change(options)
+ if options[:zone] && options[:offset]
+ raise ArgumentError, "Can't change both :offset and :zone at the same time: #{options.inspect}"
+ end
+
+ new_time = time.change(options)
+
+ if options[:zone]
+ new_zone = ::Time.find_zone(options[:zone])
+ elsif options[:offset]
+ new_zone = ::Time.find_zone(new_time.utc_offset)
+ end
+
+ new_zone ||= time_zone
+ periods = new_zone.periods_for_local(new_time)
+
+ self.class.new(nil, new_zone, new_time, periods.include?(period) ? period : nil)
+ end
+
# Uses Date to provide precise Time calculations for years, months, and days
# according to the proleptic Gregorian calendar. The result is returned as a
# new TimeWithZone object.
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index ce5207546d..96a541a4ef 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -6,7 +6,7 @@ module ActiveSupport
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
# It allows us to do the following:
#
- # * Limit the set of zones provided by TZInfo to a meaningful subset of 146
+ # * Limit the set of zones provided by TZInfo to a meaningful subset of 134
# zones.
# * Retrieve and display zones with a friendlier name
# (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
@@ -59,6 +59,7 @@ module ActiveSupport
"Buenos Aires" => "America/Argentina/Buenos_Aires",
"Montevideo" => "America/Montevideo",
"Georgetown" => "America/Guyana",
+ "Puerto Rico" => "America/Puerto_Rico",
"Greenland" => "America/Godthab",
"Mid-Atlantic" => "Atlantic/South_Georgia",
"Azores" => "Atlantic/Azores",
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index cde2967132..d849cdfa6b 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -14,11 +14,9 @@ module ActiveSupport
data = StringIO.new(data || "")
end
- char = data.getc
- if char.nil?
+ if data.eof?
{}
else
- data.ungetc(char)
LibXML::XML::Parser.io(data).parse.to_hash
end
end
diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
index 8a43b05b17..f3d485da2f 100644
--- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb
+++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
@@ -65,12 +65,9 @@ module ActiveSupport
data = StringIO.new(data || "")
end
- char = data.getc
- if char.nil?
+ if data.eof?
{}
else
- data.ungetc(char)
-
LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER)
parser = LibXML::XML::SaxParser.io(data)
document = document_class.new
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index 4c2be3f3ec..63466c08b2 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -19,11 +19,9 @@ module ActiveSupport
data = StringIO.new(data || "")
end
- char = data.getc
- if char.nil?
+ if data.eof?
{}
else
- data.ungetc(char)
doc = Nokogiri::XML(data)
raise doc.errors.first if doc.errors.length > 0
doc.to_hash
diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
index 7388bea5d8..54e15e6a5f 100644
--- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
@@ -71,11 +71,9 @@ module ActiveSupport
data = StringIO.new(data || "")
end
- char = data.getc
- if char.nil?
+ if data.eof?
{}
else
- data.ungetc(char)
document = document_class.new
parser = Nokogiri::XML::SAX::Parser.new(document)
parser.parse(data)
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 36f0ee22b8..be7c14e9b4 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -166,6 +166,9 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal DateTime.civil(2005, 2, 22, 16, 45), DateTime.civil(2005, 2, 22, 15, 15, 10).change(hour: 16, min: 45)
assert_equal DateTime.civil(2005, 2, 22, 15, 45), DateTime.civil(2005, 2, 22, 15, 15, 10).change(min: 45)
+ # datetime with non-zero offset
+ assert_equal DateTime.civil(2005, 2, 22, 15, 15, 10, Rational(-5, 24)), DateTime.civil(2005, 2, 22, 15, 15, 10, 0).change(offset: Rational(-5, 24))
+
# datetime with fractions of a second
assert_equal DateTime.civil(2005, 2, 1, 15, 15, 10.7), DateTime.civil(2005, 2, 22, 15, 15, 10.7).change(day: 1)
assert_equal DateTime.civil(2005, 1, 2, 11, 22, Rational(33000008, 1000000)), DateTime.civil(2005, 1, 2, 11, 22, 33).change(usec: 8)
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index a17438bf4d..085fd6592d 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -70,7 +70,23 @@ Product = Struct.new(:name) do
end
end
+module ExtraMissing
+ def method_missing(sym, *args)
+ if sym == :extra_missing
+ 42
+ else
+ super
+ end
+ end
+
+ def respond_to_missing?(sym, priv = false)
+ sym == :extra_missing || super
+ end
+end
+
DecoratedTester = Struct.new(:client) do
+ include ExtraMissing
+
delegate_missing_to :client
end
@@ -356,6 +372,22 @@ class ModuleTest < ActiveSupport::TestCase
assert_match(/undefined method `my_fake_method' for/, e.message)
end
+ def test_delegate_to_missing_affects_respond_to
+ assert DecoratedTester.new(@david).respond_to?(:name)
+ assert_not DecoratedTester.new(@david).respond_to?(:private_name)
+ assert_not DecoratedTester.new(@david).respond_to?(:my_fake_method)
+
+ assert DecoratedTester.new(@david).respond_to?(:name, true)
+ assert_not DecoratedTester.new(@david).respond_to?(:private_name, true)
+ assert_not DecoratedTester.new(@david).respond_to?(:my_fake_method, true)
+ end
+
+ def test_delegate_to_missing_respects_superclass_missing
+ assert_equal 42, DecoratedTester.new(@david).extra_missing
+
+ assert_respond_to DecoratedTester.new(@david), :extra_missing
+ end
+
def test_delegate_with_case
event = Event.new(Tester.new)
assert_equal 1, event.foo
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index bd644c8457..625a5bffb8 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -433,6 +433,13 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "-08:00").change(nsec: 1000000000) }
end
+ def test_change_offset
+ assert_equal Time.new(2006, 2, 22, 15, 15, 10, "-08:00"), Time.new(2006, 2, 22, 15, 15, 10, "+01:00").change(offset: "-08:00")
+ assert_equal Time.new(2006, 2, 22, 15, 15, 10, -28800), Time.new(2006, 2, 22, 15, 15, 10, 3600).change(offset: -28800)
+ assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "+01:00").change(usec: 1000000, offset: "-08:00") }
+ assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "+01:00").change(nsec: 1000000000, offset: -28800) }
+ end
+
def test_advance
assert_equal Time.local(2006, 2, 28, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(years: 1)
assert_equal Time.local(2005, 6, 28, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(months: 4)
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index c3afe68378..70ae793cda 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -625,6 +625,12 @@ class TimeWithZoneTest < ActiveSupport::TestCase
assert_equal "Fri, 31 Dec 1999 06:00:00 EST -05:00", @twz.change(hour: 6).inspect
assert_equal "Fri, 31 Dec 1999 19:15:00 EST -05:00", @twz.change(min: 15).inspect
assert_equal "Fri, 31 Dec 1999 19:00:30 EST -05:00", @twz.change(sec: 30).inspect
+ assert_equal "Fri, 31 Dec 1999 19:00:00 HST -10:00", @twz.change(offset: "-10:00").inspect
+ assert_equal "Fri, 31 Dec 1999 19:00:00 HST -10:00", @twz.change(offset: -36000).inspect
+ assert_equal "Fri, 31 Dec 1999 19:00:00 HST -10:00", @twz.change(zone: "Hawaii").inspect
+ assert_equal "Fri, 31 Dec 1999 19:00:00 HST -10:00", @twz.change(zone: -10).inspect
+ assert_equal "Fri, 31 Dec 1999 19:00:00 HST -10:00", @twz.change(zone: -36000).inspect
+ assert_equal "Fri, 31 Dec 1999 19:00:00 HST -10:00", @twz.change(zone: "Pacific/Honolulu").inspect
end
def test_change_at_dst_boundary
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index 36d1ef0849..257cb50fb2 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -100,16 +100,18 @@ class DeprecationTest < ActiveSupport::TestCase
end
def test_several_behaviors
- @a, @b = nil, nil
+ @a, @b, @c = nil, nil, nil
ActiveSupport::Deprecation.behavior = [
- Proc.new { |msg, callstack| @a = msg },
- Proc.new { |msg, callstack| @b = msg }
+ lambda { |msg, callstack, horizon, gem| @a = msg },
+ lambda { |msg, callstack| @b = msg },
+ lambda { |*args| @c = args },
]
@dtc.partially
assert_match(/foo=nil/, @a)
assert_match(/foo=nil/, @b)
+ assert_equal 4, @c.size
end
def test_raise_behaviour
@@ -119,7 +121,7 @@ class DeprecationTest < ActiveSupport::TestCase
callstack = caller_locations
e = assert_raise ActiveSupport::DeprecationException do
- ActiveSupport::Deprecation.behavior.first.call(message, callstack)
+ ActiveSupport::Deprecation.behavior.first.call(message, callstack, "horizon", "gem")
end
assert_equal message, e.message
assert_equal callstack.map(&:to_s), e.backtrace.map(&:to_s)
@@ -130,7 +132,7 @@ class DeprecationTest < ActiveSupport::TestCase
behavior = ActiveSupport::Deprecation.behavior.first
content = capture(:stderr) {
- assert_nil behavior.call("Some error!", ["call stack!"])
+ assert_nil behavior.call("Some error!", ["call stack!"], "horizon", "gem")
}
assert_match(/Some error!/, content)
assert_match(/call stack!/, content)
@@ -152,11 +154,32 @@ class DeprecationTest < ActiveSupport::TestCase
behavior = ActiveSupport::Deprecation.behavior.first
stderr_output = capture(:stderr) {
- assert_nil behavior.call("Some error!", ["call stack!"])
+ assert_nil behavior.call("Some error!", ["call stack!"], "horizon", "gem")
}
assert stderr_output.empty?
end
+ def test_default_notify_behavior
+ ActiveSupport::Deprecation.behavior = :notify
+ behavior = ActiveSupport::Deprecation.behavior.first
+
+ begin
+ events = []
+ ActiveSupport::Notifications.subscribe("deprecation.my_gem_custom") { |_, **args|
+ events << args
+ }
+
+ assert_nil behavior.call("Some error!", ["call stack!"], "horizon", "MyGem::Custom")
+ assert_equal 1, events.size
+ assert_equal "Some error!", events.first[:message]
+ assert_equal ["call stack!"], events.first[:callstack]
+ assert_equal "horizon", events.first[:deprecation_horizon]
+ assert_equal "MyGem::Custom", events.first[:gem_name]
+ ensure
+ ActiveSupport::Notifications.unsubscribe("deprecation.my_gem_custom")
+ end
+ end
+
def test_deprecated_instance_variable_proxy
assert_not_deprecated { @dtc.request.size }
diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
index ee862320e8..d68add46cd 100644
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ b/activesupport/test/hash_with_indifferent_access_test.rb
@@ -166,6 +166,18 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_equal [1, 2], @mixed.values_at(:a, :b)
end
+ def test_indifferent_fetch_values
+ skip unless Hash.method_defined?(:fetch_values)
+
+ @mixed = @mixed.with_indifferent_access
+
+ assert_equal [1, 2], @mixed.fetch_values("a", "b")
+ assert_equal [1, 2], @mixed.fetch_values(:a, :b)
+ assert_equal [1, 2], @mixed.fetch_values(:a, "b")
+ assert_equal [1, "c"], @mixed.fetch_values(:a, :c) { |key| key }
+ assert_raise(KeyError) { @mixed.fetch_values(:a, :c) }
+ end
+
def test_indifferent_reading
hash = HashWithIndifferentAccess.new
hash["a"] = 1
@@ -675,7 +687,6 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_includes yaml_output, "hash"
end
end
- HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
def test_should_use_default_proc_for_unknown_key
hash_wia = HashWithIndifferentAccess.new { 1 + 2 }
diff --git a/activesupport/test/xml_mini/xml_mini_engine_test.rb b/activesupport/test/xml_mini/xml_mini_engine_test.rb
index 5be9084c9d..244e0b0d3a 100644
--- a/activesupport/test/xml_mini/xml_mini_engine_test.rb
+++ b/activesupport/test/xml_mini/xml_mini_engine_test.rb
@@ -75,6 +75,11 @@ class XMLMiniEngineTest < ActiveSupport::TestCase
assert_equal({}, ActiveSupport::XmlMini.parse(""))
end
+ def test_parse_from_frozen_string
+ xml_string = "<root/>".freeze
+ assert_equal({ "root" => {} }, ActiveSupport::XmlMini.parse(xml_string))
+ end
+
def test_array_type_makes_an_array
assert_equal_rexml(<<-eoxml)
<blog>