aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support')
-rw-r--r--activesupport/lib/active_support/cache.rb10
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb13
-rw-r--r--activesupport/lib/active_support/configurable.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb25
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/range/compare_range.rb33
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb47
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb3
-rw-r--r--activesupport/lib/active_support/dependencies/zeitwerk_integration.rb37
-rw-r--r--activesupport/lib/active_support/descendants_tracker.rb54
-rw-r--r--activesupport/lib/active_support/evented_file_update_checker.rb4
-rw-r--r--activesupport/lib/active_support/gem_version.rb2
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb29
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb2
-rw-r--r--activesupport/lib/active_support/message_verifier.rb2
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb4
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb9
-rw-r--r--activesupport/lib/active_support/security_utils.rb2
22 files changed, 253 insertions, 110 deletions
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 30a69c550b..e055135bb4 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -492,7 +492,7 @@ module ActiveSupport
#
# Options are passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def delete_matched(matcher, options = nil)
raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
end
@@ -501,7 +501,7 @@ module ActiveSupport
#
# Options are passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def increment(name, amount = 1, options = nil)
raise NotImplementedError.new("#{self.class.name} does not support increment")
end
@@ -510,7 +510,7 @@ module ActiveSupport
#
# Options are passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def decrement(name, amount = 1, options = nil)
raise NotImplementedError.new("#{self.class.name} does not support decrement")
end
@@ -519,7 +519,7 @@ module ActiveSupport
#
# Options are passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def cleanup(options = nil)
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
end
@@ -529,7 +529,7 @@ module ActiveSupport
#
# The options hash is passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def clear(options = nil)
raise NotImplementedError.new("#{self.class.name} does not support clear")
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index de1fb1886c..f43894a1ea 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -18,7 +18,6 @@ module ActiveSupport
DIR_FORMATTER = "%03X"
FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
- EXCLUDED_DIRS = [".", ".."].freeze
GITKEEP_FILES = [".gitkeep", ".keep"].freeze
def initialize(cache_path, options = nil)
@@ -35,7 +34,7 @@ module ActiveSupport
# file store directory except for .keep or .gitkeep. Be careful which directory is specified in your
# config file when using +FileStore+ because everything in that directory will be deleted.
def clear(options = nil)
- root_dirs = exclude_from(cache_path, EXCLUDED_DIRS + GITKEEP_FILES)
+ root_dirs = (Dir.children(cache_path) - GITKEEP_FILES)
FileUtils.rm_r(root_dirs.collect { |f| File.join(cache_path, f) })
rescue Errno::ENOENT
end
@@ -154,7 +153,7 @@ module ActiveSupport
# Delete empty directories in the cache.
def delete_empty_directories(dir)
return if File.realpath(dir) == File.realpath(cache_path)
- if exclude_from(dir, EXCLUDED_DIRS).empty?
+ if Dir.children(dir).empty?
Dir.delete(dir) rescue nil
delete_empty_directories(File.dirname(dir))
end
@@ -167,8 +166,7 @@ module ActiveSupport
def search_dir(dir, &callback)
return if !File.exist?(dir)
- Dir.foreach(dir) do |d|
- next if EXCLUDED_DIRS.include?(d)
+ Dir.each_child(dir) do |d|
name = File.join(dir, d)
if File.directory?(name)
search_dir(name, &callback)
@@ -193,11 +191,6 @@ module ActiveSupport
end
end
end
-
- # Exclude entries from source directory
- def exclude_from(source, excludes)
- Dir.entries(source).reject { |f| excludes.include?(f) }
- end
end
end
end
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 9acf674c40..71c23dae9b 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -67,8 +67,8 @@ module ActiveSupport
# end
# # => NameError: invalid config attribute name
#
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
#
# class User
# include ActiveSupport::Configurable
@@ -81,7 +81,7 @@ module ActiveSupport
# User.new.allowed_access = true # => NoMethodError
# User.new.allowed_access # => NoMethodError
#
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
#
# class User
# include ActiveSupport::Configurable
diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb
index b7ff7a3907..10e4c6b09d 100644
--- a/activesupport/lib/active_support/core_ext/array/access.rb
+++ b/activesupport/lib/active_support/core_ext/array/access.rb
@@ -29,16 +29,28 @@ class Array
end
end
- # Returns a copy of the Array without the specified elements.
+ # Returns a new array that includes the passed elements.
#
- # people = ["David", "Rafael", "Aaron", "Todd"]
- # people.without "Aaron", "Todd"
- # # => ["David", "Rafael"]
+ # [ 1, 2, 3 ].including(4, 5) => [ 1, 2, 3, 4, 5 ]
+ # [ [ 0, 1 ] ].including([ [ 1, 0 ] ]) => [ [ 0, 1 ], [ 1, 0 ] ]
+ def including(*elements)
+ self + elements.flatten(1)
+ end
+
+ # Returns a copy of the Array excluding the specified elements.
+ #
+ # ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") => ["David", "Rafael"]
+ # [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) => [ [ 0, 1 ] ]
#
- # Note: This is an optimization of <tt>Enumerable#without</tt> that uses <tt>Array#-</tt>
+ # Note: This is an optimization of <tt>Enumerable#excluding</tt> that uses <tt>Array#-</tt>
# instead of <tt>Array#reject</tt> for performance reasons.
+ def excluding(*elements)
+ self - elements.flatten(1)
+ end
+
+ # Alias for #excluding.
def without(*elements)
- self - elements
+ excluding(*elements)
end
# Equal to <tt>self[1]</tt>.
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index fa33ff945f..255cbee55c 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -84,16 +84,17 @@ class Class
# To set a default value for the attribute, pass <tt>default:</tt>, like so:
#
# class_attribute :settings, default: {}
- def class_attribute(*attrs)
- options = attrs.extract_options!
- instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
- instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
- instance_predicate = options.fetch(:instance_predicate, true)
- default_value = options.fetch(:default, nil)
-
+ def class_attribute(
+ *attrs,
+ instance_accessor: true,
+ instance_reader: instance_accessor,
+ instance_writer: instance_accessor,
+ instance_predicate: true,
+ default: nil
+ )
attrs.each do |name|
singleton_class.silence_redefinition_of_method(name)
- define_singleton_method(name) { nil }
+ define_singleton_method(name) { default }
singleton_class.silence_redefinition_of_method("#{name}?")
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
@@ -102,9 +103,7 @@ class Class
singleton_class.silence_redefinition_of_method("#{name}=")
define_singleton_method("#{name}=") do |val|
- singleton_class.class_eval do
- redefine_method(name) { val }
- end
+ redefine_singleton_method(name) { val }
if singleton_class?
class_eval do
@@ -137,10 +136,6 @@ class Class
instance_variable_set ivar, val
end
end
-
- unless default_value.nil?
- self.send("#{name}=", default_value)
- end
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index d87d63f287..4675c41936 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -97,23 +97,43 @@ module Enumerable
end
end
+ # Returns a new array that includes the passed elements.
+ #
+ # [ 1, 2, 3 ].including(4, 5)
+ # # => [ 1, 2, 3, 4, 5 ]
+ #
+ # ["David", "Rafael"].including %w[ Aaron Todd ]
+ # # => ["David", "Rafael", "Aaron", "Todd"]
+ def including(*elements)
+ to_a.including(*elements)
+ end
+
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
# collection does not include the object.
def exclude?(object)
!include?(object)
end
- # Returns a copy of the enumerable without the specified elements.
+ # Returns a copy of the enumerable excluding the specified elements.
+ #
+ # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
+ # # => ["David", "Rafael"]
#
- # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
# # => ["David", "Rafael"]
#
- # {foo: 1, bar: 2, baz: 3}.without :bar
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
# # => {foo: 1, baz: 3}
- def without(*elements)
+ def excluding(*elements)
+ elements.flatten!(1)
reject { |element| elements.include?(element) }
end
+ # Alias for #excluding.
+ def without(*elements)
+ excluding(*elements)
+ end
+
# Convert an enumerable to an array based on the given key.
#
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
index 5850e0193f..cc1926e022 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -24,7 +24,7 @@ class Module
# end
# # => NameError: invalid attribute name: 1_Badname
#
- # If you want to opt out the creation on the instance reader method, pass
+ # To omit the instance reader method, pass
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
#
# module HairColors
@@ -91,7 +91,7 @@ class Module
# Person.new.hair_colors = [:blonde, :red]
# HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
#
- # If you want to opt out the instance writer method, pass
+ # To omit the instance writer method, pass
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
#
# module HairColors
@@ -166,8 +166,8 @@ class Module
# Citizen.new.hair_colors << :blue
# Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
#
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
#
# module HairColors
# mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
@@ -180,7 +180,7 @@ class Module
# Person.new.hair_colors = [:brown] # => NoMethodError
# Person.new.hair_colors # => NoMethodError
#
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
#
# module HairColors
# mattr_accessor :hair_colors, instance_accessor: false
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
index cb996e9e6d..a6e87aeb68 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
@@ -25,7 +25,7 @@ class Module
# end
# # => NameError: invalid attribute name: 1_Badname
#
- # If you want to opt out of the creation of the instance reader method, pass
+ # To omit the instance reader method, pass
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
#
# class Current
@@ -66,7 +66,7 @@ class Module
# Current.user = "DHH"
# Thread.current[:attr_Current_user] # => "DHH"
#
- # If you want to opt out of the creation of the instance writer method, pass
+ # To omit the instance writer method, pass
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
#
# class Current
@@ -118,8 +118,8 @@ class Module
# Customer.user # => "Rafael"
# Account.user # => "DHH"
#
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
#
# class Current
# thread_mattr_accessor :user, instance_writer: false, instance_reader: false
@@ -128,7 +128,7 @@ class Module
# Current.new.user = "DHH" # => NoMethodError
# Current.new.user # => NoMethodError
#
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
#
# class Current
# thread_mattr_accessor :user, instance_accessor: false
diff --git a/activesupport/lib/active_support/core_ext/range/compare_range.rb b/activesupport/lib/active_support/core_ext/range/compare_range.rb
index 6f6d2a27bb..ea1dc29a76 100644
--- a/activesupport/lib/active_support/core_ext/range/compare_range.rb
+++ b/activesupport/lib/active_support/core_ext/range/compare_range.rb
@@ -3,9 +3,10 @@
module ActiveSupport
module CompareWithRange
# Extends the default Range#=== to support range comparisons.
- # (1..5) === (1..5) # => true
- # (1..5) === (2..3) # => true
- # (1..5) === (2..6) # => false
+ # (1..5) === (1..5) # => true
+ # (1..5) === (2..3) # => true
+ # (1..5) === (1...6) # => true
+ # (1..5) === (2..6) # => false
#
# The native Range#=== behavior is untouched.
# ('a'..'f') === ('c') # => true
@@ -13,17 +14,20 @@ module ActiveSupport
def ===(value)
if value.is_a?(::Range)
# 1...10 includes 1..9 but it does not include 1..10.
+ # 1..10 includes 1...11 but it does not include 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
- super(value.first) && value.last.send(operator, last)
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
+ super(value.first) && value_max.send(operator, last)
else
super
end
end
# Extends the default Range#include? to support range comparisons.
- # (1..5).include?(1..5) # => true
- # (1..5).include?(2..3) # => true
- # (1..5).include?(2..6) # => false
+ # (1..5).include?(1..5) # => true
+ # (1..5).include?(2..3) # => true
+ # (1..5).include?(1...6) # => true
+ # (1..5).include?(2..6) # => false
#
# The native Range#include? behavior is untouched.
# ('a'..'f').include?('c') # => true
@@ -31,17 +35,20 @@ module ActiveSupport
def include?(value)
if value.is_a?(::Range)
# 1...10 includes 1..9 but it does not include 1..10.
+ # 1..10 includes 1...11 but it does not include 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
- super(value.first) && value.last.send(operator, last)
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
+ super(value.first) && value_max.send(operator, last)
else
super
end
end
# Extends the default Range#cover? to support range comparisons.
- # (1..5).cover?(1..5) # => true
- # (1..5).cover?(2..3) # => true
- # (1..5).cover?(2..6) # => false
+ # (1..5).cover?(1..5) # => true
+ # (1..5).cover?(2..3) # => true
+ # (1..5).cover?(1...6) # => true
+ # (1..5).cover?(2..6) # => false
#
# The native Range#cover? behavior is untouched.
# ('a'..'f').cover?('c') # => true
@@ -49,8 +56,10 @@ module ActiveSupport
def cover?(value)
if value.is_a?(::Range)
# 1...10 covers 1..9 but it does not cover 1..10.
+ # 1..10 covers 1...11 but it does not cover 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
- super(value.first) && value.last.send(operator, last)
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
+ super(value.first) && value_max.send(operator, last)
else
super
end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 8af301734a..5eb8d9f99b 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -162,6 +162,11 @@ class String
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
#
+ # If the optional parameter +locale+ is specified,
+ # the word will be parameterized as a word of that language.
+ # By default, this parameter is set to <tt>nil</tt> and it will use
+ # the configured <tt>I18n.locale</tt>.
+ #
# class Person
# def to_param
# "#{id}-#{name.parameterize}"
@@ -187,8 +192,8 @@ class String
#
# <%= link_to(@person.name, person_path) %>
# # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
- def parameterize(separator: "-", preserve_case: false)
- ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
+ def parameterize(separator: "-", preserve_case: false, locale: nil)
+ ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
end
# Creates the name of a table like Rails does for models to table names. This method
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index 3a80de4617..638152626b 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -135,10 +135,12 @@ module ActiveSupport #:nodoc:
class SafeBuffer < String
UNSAFE_STRING_METHODS = %w(
capitalize chomp chop delete delete_prefix delete_suffix
- downcase gsub lstrip next reverse rstrip slice squeeze strip
- sub succ swapcase tr tr_s unicode_normalize upcase
+ downcase lstrip next reverse rstrip slice squeeze strip
+ succ swapcase tr tr_s unicode_normalize upcase
)
+ UNSAFE_STRING_METHODS_WITH_BACKREF = %w(gsub sub)
+
alias_method :original_concat, :concat
private :original_concat
@@ -199,8 +201,12 @@ module ActiveSupport #:nodoc:
super(html_escape_interpolated_argument(value))
end
- def []=(index, value)
- super(index, html_escape_interpolated_argument(value))
+ def []=(*args)
+ if args.count == 3
+ super(args[0], args[1], html_escape_interpolated_argument(args[2]))
+ else
+ super(args[0], html_escape_interpolated_argument(args[1]))
+ end
end
def +(other)
@@ -249,11 +255,44 @@ module ActiveSupport #:nodoc:
end
end
+ UNSAFE_STRING_METHODS_WITH_BACKREF.each do |unsafe_method|
+ if unsafe_method.respond_to?(unsafe_method)
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
+ def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
+ if block # if block
+ to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params|
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
+ block.call(*params) # block.call(*params)
+ } # }
+ else # else
+ to_str.#{unsafe_method}(*args) # to_str.gsub(*args)
+ end # end
+ end # end
+
+ def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
+ @html_safe = false # @html_safe = false
+ if block # if block
+ super(*args) { |*params| # super(*args) { |*params|
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
+ block.call(*params) # block.call(*params)
+ } # }
+ else # else
+ super # super
+ end # end
+ end # end
+ EOT
+ end
+ end
+
private
def html_escape_interpolated_argument(arg)
(!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
end
+
+ def set_block_back_references(block, match_data)
+ block.binding.eval("proc { |m| $~ = m }").call(match_data)
+ end
end
end
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 120768dec5..f09a6271ad 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -170,8 +170,7 @@ class Time
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
end
- d = to_date.advance(options)
- d = d.gregorian if d.julian?
+ d = to_date.gregorian.advance(options)
time_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
seconds_to_advance = \
options.fetch(:seconds, 0) +
diff --git a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb
index 75624bae09..c6fdade006 100644
--- a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb
+++ b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb
@@ -1,33 +1,52 @@
# frozen_string_literal: true
+require "active_support/core_ext/string/inflections"
+
module ActiveSupport
module Dependencies
module ZeitwerkIntegration # :nodoc: all
module Decorations
def clear
Dependencies.unload_interlock do
- Rails.autoloader.reload
+ Rails.autoloaders.main.reload
end
end
def constantize(cpath)
- Inflector.constantize(cpath)
+ ActiveSupport::Inflector.constantize(cpath)
end
def safe_constantize(cpath)
- Inflector.safe_constantize(cpath)
+ ActiveSupport::Inflector.safe_constantize(cpath)
end
def autoloaded_constants
- Rails.autoloaders.flat_map do |autoloader|
- autoloader.loaded.to_a
+ cpaths = []
+ Rails.autoloaders.each do |autoloader|
+ cpaths.concat(autoloader.loaded_cpaths.to_a)
end
+ cpaths
end
def autoloaded?(object)
cpath = object.is_a?(Module) ? object.name : object.to_s
Rails.autoloaders.any? { |autoloader| autoloader.loaded?(cpath) }
end
+
+ def verbose=(verbose)
+ l = verbose ? logger || Rails.logger : nil
+ Rails.autoloaders.each { |autoloader| autoloader.logger = l }
+ end
+
+ def unhook!
+ :no_op
+ end
+ end
+
+ module Inflector
+ def self.camelize(basename, _abspath)
+ basename.camelize
+ end
end
class << self
@@ -46,9 +65,9 @@ module ActiveSupport
next unless File.directory?(autoload_path)
if autoload_once?(autoload_path)
- Rails.once_autoloader.push_dir(autoload_path)
+ Rails.autoloaders.once.push_dir(autoload_path)
else
- Rails.autoloader.push_dir(autoload_path)
+ Rails.autoloaders.main.push_dir(autoload_path)
end
end
@@ -56,8 +75,7 @@ module ActiveSupport
end
def autoload_once?(autoload_path)
- Dependencies.autoload_once_paths.include?(autoload_path) ||
- Gem.path.any? { |gem_path| autoload_path.to_s.start_with?(gem_path) }
+ Dependencies.autoload_once_paths.include?(autoload_path)
end
def freeze_autoload_paths
@@ -66,6 +84,7 @@ module ActiveSupport
end
def decorate_dependencies
+ Dependencies.unhook!
Dependencies.singleton_class.prepend(Decorations)
Object.class_eval { alias_method :require_dependency, :require }
end
diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb
index 05236d3162..2dca990712 100644
--- a/activesupport/lib/active_support/descendants_tracker.rb
+++ b/activesupport/lib/active_support/descendants_tracker.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "weakref"
+
module ActiveSupport
# This module provides an internal implementation to track descendants
# which is faster than iterating through ObjectSpace.
@@ -8,7 +10,8 @@ module ActiveSupport
class << self
def direct_descendants(klass)
- @@direct_descendants[klass] || []
+ descendants = @@direct_descendants[klass]
+ descendants ? descendants.to_a : []
end
def descendants(klass)
@@ -34,15 +37,17 @@ module ActiveSupport
# This is the only method that is not thread safe, but is only ever called
# during the eager loading phase.
def store_inherited(klass, descendant)
- (@@direct_descendants[klass] ||= []) << descendant
+ (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
end
private
def accumulate_descendants(klass, acc)
if direct_descendants = @@direct_descendants[klass]
- acc.concat(direct_descendants)
- direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
+ direct_descendants.each do |direct_descendant|
+ acc << direct_descendant
+ accumulate_descendants(direct_descendant, acc)
+ end
end
end
end
@@ -59,5 +64,46 @@ module ActiveSupport
def descendants
DescendantsTracker.descendants(self)
end
+
+ # DescendantsArray is an array that contains weak references to classes.
+ class DescendantsArray # :nodoc:
+ include Enumerable
+
+ def initialize
+ @refs = []
+ end
+
+ def initialize_copy(orig)
+ @refs = @refs.dup
+ end
+
+ def <<(klass)
+ cleanup!
+ @refs << WeakRef.new(klass)
+ end
+
+ def each
+ @refs.each do |ref|
+ yield ref.__getobj__
+ rescue WeakRef::RefError
+ end
+ end
+
+ def refs_size
+ @refs.size
+ end
+
+ def cleanup!
+ @refs.delete_if { |ref| !ref.weakref_alive? }
+ end
+
+ def reject!
+ @refs.reject! do |ref|
+ yield ref.__getobj__
+ rescue WeakRef::RefError
+ true
+ end
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/evented_file_update_checker.rb b/activesupport/lib/active_support/evented_file_update_checker.rb
index 54bc5c0ae5..3893b0de0e 100644
--- a/activesupport/lib/active_support/evented_file_update_checker.rb
+++ b/activesupport/lib/active_support/evented_file_update_checker.rb
@@ -131,7 +131,9 @@ module ActiveSupport
ext = @ph.normalize_extension(file.extname)
file.dirname.ascend do |dir|
- if @dirs.fetch(dir, []).include?(ext)
+ matching = @dirs[dir]
+
+ if matching && (matching.empty? || matching.include?(ext))
break true
elsif dir == @lcsp || dir.root?
break false
diff --git a/activesupport/lib/active_support/gem_version.rb b/activesupport/lib/active_support/gem_version.rb
index 8bf016e88c..cf17922d7d 100644
--- a/activesupport/lib/active_support/gem_version.rb
+++ b/activesupport/lib/active_support/gem_version.rb
@@ -10,7 +10,7 @@ module ActiveSupport
MAJOR = 6
MINOR = 0
TINY = 0
- PRE = "beta1"
+ PRE = "beta3"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index 0d2a17970f..ec6e9ccb59 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -51,19 +51,18 @@ module ActiveSupport
#
# Now you can have different transliterations for each locale:
#
- # I18n.locale = :en
- # transliterate('Jürgen')
+ # transliterate('Jürgen', locale: :en)
# # => "Jurgen"
#
- # I18n.locale = :de
- # transliterate('Jürgen')
+ # transliterate('Jürgen', locale: :de)
# # => "Juergen"
- def transliterate(string, replacement = "?")
+ def transliterate(string, replacement = "?", locale: nil)
raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
I18n.transliterate(
ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
- replacement: replacement
+ replacement: replacement,
+ locale: locale
)
end
@@ -75,8 +74,8 @@ module ActiveSupport
#
# To use a custom separator, override the +separator+ argument.
#
- # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
- # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
#
# To preserve the case of the characters in a string, use the +preserve_case+ argument.
#
@@ -85,13 +84,17 @@ module ActiveSupport
#
# It preserves dashes and underscores unless they are used as separators:
#
- # parameterize("^très|Jolie__ ") # => "tres-jolie__"
- # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
- # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
#
- def parameterize(string, separator: "-", preserve_case: false)
+ # If the optional parameter +locale+ is specified,
+ # the word will be parameterized as a word of that language.
+ # By default, this parameter is set to <tt>nil</tt> and it will use
+ # the configured <tt>I18n.locale<tt>.
+ def parameterize(string, separator: "-", preserve_case: false, locale: nil)
# Replace accented chars with their ASCII equivalents.
- parameterized_string = transliterate(string)
+ parameterized_string = transliterate(string, locale: locale)
# Turn unwanted chars into the separator.
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 6f7302e732..7d6f8937f0 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -53,7 +53,7 @@ module ActiveSupport
# crypt.encrypt_and_sign(parcel, expires_in: 1.month)
# crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
#
- # Then the messages can be verified and returned upto the expire time.
+ # Then the messages can be verified and returned up to the expire time.
# Thereafter, verifying returns +nil+.
#
# === Rotating keys
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 64c557bec6..c4a4afe95f 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -71,7 +71,7 @@ module ActiveSupport
# @verifier.generate(parcel, expires_in: 1.month)
# @verifier.generate(doowad, expires_at: Time.now.end_of_year)
#
- # Then the messages can be verified and returned upto the expire time.
+ # Then the messages can be verified and returned up to the expire time.
# Thereafter, the +verified+ method returns +nil+ while +verify+ raises
# <tt>ActiveSupport::MessageVerifier::InvalidSignature</tt>.
#
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index f06504cf2c..c506b35b1e 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -20,8 +20,8 @@ module ActiveSupport
super
end
- def subscribe(pattern = nil, block = Proc.new)
- subscriber = Subscribers.new pattern, block
+ def subscribe(pattern = nil, callable = nil, &block)
+ subscriber = Subscribers.new(pattern, callable || block)
synchronize do
if String === pattern
@string_subscribers[pattern] << subscriber
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index 00a57c38c9..a03e7e483e 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -13,14 +13,15 @@ module ActiveSupport
@notifier = notifier
end
- # Instrument the given block by measuring the time taken to execute it
- # and publish it. Notice that events get sent even if an error occurs
- # in the passed-in block.
+ # Given a block, instrument it by measuring the time taken to execute
+ # and publish it. Without a block, simply send a message via the
+ # notifier. Notice that events get sent even if an error occurs in the
+ # passed-in block.
def instrument(name, payload = {})
# some of the listeners might have state
listeners_state = start name, payload
begin
- yield payload
+ yield payload if block_given?
rescue Exception => e
payload[:exception] = [e.class.name, e.message]
payload[:exception_object] = e
diff --git a/activesupport/lib/active_support/security_utils.rb b/activesupport/lib/active_support/security_utils.rb
index 20b6b9cd3f..5e455fca57 100644
--- a/activesupport/lib/active_support/security_utils.rb
+++ b/activesupport/lib/active_support/security_utils.rb
@@ -24,7 +24,7 @@ module ActiveSupport
# The values are first processed by SHA256, so that we don't leak length info
# via timing attacks.
def secure_compare(a, b)
- fixed_length_secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b)) && a == b
+ fixed_length_secure_compare(::Digest::SHA256.digest(a), ::Digest::SHA256.digest(b)) && a == b
end
module_function :secure_compare
end