aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib')
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb23
-rw-r--r--activesupport/lib/active_support/cache.rb35
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb21
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/null_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/redis_cache_store.rb29
-rw-r--r--activesupport/lib/active_support/callbacks.rb17
-rw-r--r--activesupport/lib/active_support/concern.rb10
-rw-r--r--activesupport/lib/active_support/configurable.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/array.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/extract.rb21
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb31
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb32
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/integer/multiple.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/load_error.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/module/introspection.rb50
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/conversions.rb244
-rw-r--r--activesupport/lib/active_support/core_ext/object/blank.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_query.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/object/with_options.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/range.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/range/compare_range.rb61
-rw-r--r--activesupport/lib/active_support/core_ext/range/conversions.rb60
-rw-r--r--activesupport/lib/active_support/core_ext/range/include_range.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb21
-rw-r--r--activesupport/lib/active_support/core_ext/string/strip.rb2
-rw-r--r--activesupport/lib/active_support/current_attributes.rb2
-rw-r--r--activesupport/lib/active_support/dependencies.rb49
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb6
-rw-r--r--activesupport/lib/active_support/deprecation/method_wrappers.rb32
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb2
-rw-r--r--activesupport/lib/active_support/descendants_tracker.rb11
-rw-r--r--activesupport/lib/active_support/duration.rb1
-rw-r--r--activesupport/lib/active_support/duration/iso8601_parser.rb5
-rw-r--r--activesupport/lib/active_support/duration/iso8601_serializer.rb6
-rw-r--r--activesupport/lib/active_support/encrypted_configuration.rb2
-rw-r--r--activesupport/lib/active_support/execution_wrapper.rb1
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb4
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb15
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb1
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb27
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb12
-rw-r--r--activesupport/lib/active_support/json/decoding.rb46
-rw-r--r--activesupport/lib/active_support/key_generator.rb2
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb6
-rw-r--r--activesupport/lib/active_support/logger.rb15
-rw-r--r--activesupport/lib/active_support/logger_silence.rb40
-rw-r--r--activesupport/lib/active_support/logger_thread_safe_level.rb30
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb6
-rw-r--r--activesupport/lib/active_support/message_verifier.rb4
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb77
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb60
-rw-r--r--activesupport/lib/active_support/notifications.rb22
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb42
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb75
-rw-r--r--activesupport/lib/active_support/number_helper.rb7
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_currency_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_phone_converter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb8
-rw-r--r--activesupport/lib/active_support/parameter_filter.rb124
-rw-r--r--activesupport/lib/active_support/reloader.rb1
-rw-r--r--activesupport/lib/active_support/subscriber.rb15
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb13
-rw-r--r--activesupport/lib/active_support/test_case.rb4
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb16
-rw-r--r--activesupport/lib/active_support/testing/deprecation.rb1
-rw-r--r--activesupport/lib/active_support/testing/file_fixtures.rb2
-rw-r--r--activesupport/lib/active_support/testing/method_call_assertions.rb31
-rw-r--r--activesupport/lib/active_support/testing/parallelization.rb61
-rw-r--r--activesupport/lib/active_support/testing/time_helpers.rb3
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb18
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb18
-rw-r--r--activesupport/lib/active_support/xml_mini.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/libxmlsax.rb8
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogirisax.rb6
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb4
93 files changed, 1222 insertions, 553 deletions
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 16dd733ddb..62973eca58 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -31,6 +31,9 @@ module ActiveSupport
class BacktraceCleaner
def initialize
@filters, @silencers = [], []
+ add_gem_filter
+ add_gem_silencer
+ add_stdlib_silencer
end
# Returns the backtrace after all filters and silencers have been run
@@ -82,6 +85,26 @@ module ActiveSupport
end
private
+
+ FORMATTED_GEMS_PATTERN = /\A[^\/]+ \([\w.]+\) /
+
+ def add_gem_filter
+ gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
+ return if gems_paths.empty?
+
+ gems_regexp = %r{(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
+ gems_result = '\3 (\4) \5'
+ add_filter { |line| line.sub(gems_regexp, gems_result) }
+ end
+
+ def add_gem_silencer
+ add_silencer { |line| FORMATTED_GEMS_PATTERN.match?(line) }
+ end
+
+ def add_stdlib_silencer
+ add_silencer { |line| line.start_with?(RbConfig::CONFIG["rubylibdir"]) }
+ end
+
def filter_backtrace(backtrace)
@filters.each do |f|
backtrace = backtrace.map { |line| f.call(line) }
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index d769e2c8ea..e8518645d9 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -229,6 +229,14 @@ module ActiveSupport
# ask whether you should force a cache write. Otherwise, it's clearer to
# just call <tt>Cache#write</tt>.
#
+ # Setting <tt>skip_nil: true</tt> will not cache nil result:
+ #
+ # cache.fetch('foo') { nil }
+ # cache.fetch('bar', skip_nil: true) { nil }
+ # cache.exist?('foo') # => true
+ # cache.exist?('bar') # => false
+ #
+ #
# Setting <tt>compress: false</tt> disables compression of the cache entry.
#
# Setting <tt>:expires_in</tt> will set an expiration time on the cache.
@@ -403,8 +411,6 @@ module ActiveSupport
# to the cache. If you do not want to write the cache when the cache is
# not found, use #read_multi.
#
- # Options are passed to the underlying cache implementation.
- #
# Returns a hash with the data for each of the names. For example:
#
# cache.write("bim", "bam")
@@ -414,6 +420,17 @@ module ActiveSupport
# # => { "bim" => "bam",
# # "unknown_key" => "Fallback value for key: unknown_key" }
#
+ # Options are passed to the underlying cache implementation. For example:
+ #
+ # cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
+ # "buzz"
+ # end
+ # # => {"fizz"=>"buzz"}
+ # cache.read("fizz")
+ # # => "buzz"
+ # sleep(6)
+ # cache.read("fizz")
+ # # => nil
def fetch_multi(*names)
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
@@ -588,9 +605,13 @@ module ActiveSupport
# Merges the default options with ones specific to a method call.
def merged_options(call_options)
if call_options
- options.merge(call_options)
+ if options.empty?
+ call_options
+ else
+ options.merge(call_options)
+ end
else
- options.dup
+ options
end
end
@@ -635,7 +656,7 @@ module ActiveSupport
if key.size > 1
key = key.collect { |element| expanded_key(element) }
else
- key = key.first
+ key = expanded_key(key.first)
end
when Hash
key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
@@ -686,7 +707,7 @@ module ActiveSupport
end
def get_entry_value(entry, name, options)
- instrument(:fetch_hit, name, options) {}
+ instrument(:fetch_hit, name, options) { }
entry.value
end
@@ -695,7 +716,7 @@ module ActiveSupport
yield(name)
end
- write(name, result, options)
+ write(name, result, options) unless result.nil? && options[:skip_nil]
result
end
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index a0f44aac0f..53a2b07536 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -26,6 +26,11 @@ module ActiveSupport
@cache_path = cache_path.to_s
end
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
# Deletes all items from the cache. In this case it deletes all the entries in the specified
# 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.
@@ -127,15 +132,19 @@ module ActiveSupport
hash = Zlib.adler32(fname)
hash, dir_1 = hash.divmod(0x1000)
dir_2 = hash.modulo(0x1000)
- fname_paths = []
# Make sure file name doesn't exceed file system limits.
- begin
- fname_paths << fname[0, FILENAME_MAX_SIZE]
- fname = fname[FILENAME_MAX_SIZE..-1]
- end until fname.blank?
+ if fname.length < FILENAME_MAX_SIZE
+ fname_paths = fname
+ else
+ fname_paths = []
+ begin
+ fname_paths << fname[0, FILENAME_MAX_SIZE]
+ fname = fname[FILENAME_MAX_SIZE..-1]
+ end until fname.blank?
+ end
- File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
+ File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, fname_paths)
end
# Translate a file path into a key.
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 2840781dde..174c784deb 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -47,6 +47,11 @@ module ActiveSupport
end
end
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
prepend Strategy::LocalCache
prepend LocalCacheWithRaw
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 564ac17241..106b616529 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -30,6 +30,11 @@ module ActiveSupport
@pruning = false
end
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
# Delete all data stored in a given cache store.
def clear(options = nil)
synchronize do
diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb
index 1a5983db43..8452a28fd8 100644
--- a/activesupport/lib/active_support/cache/null_store.rb
+++ b/activesupport/lib/active_support/cache/null_store.rb
@@ -12,6 +12,11 @@ module ActiveSupport
class NullStore < Store
prepend Strategy::LocalCache
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
def clear(options = nil)
end
diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb
index 11c574258f..9a55e49e27 100644
--- a/activesupport/lib/active_support/cache/redis_cache_store.rb
+++ b/activesupport/lib/active_support/cache/redis_cache_store.rb
@@ -66,6 +66,11 @@ module ActiveSupport
SCAN_BATCH_SIZE = 1000
private_constant :SCAN_BATCH_SIZE
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
# Support raw values in the local cache strategy.
module LocalCacheWithRaw # :nodoc:
private
@@ -258,7 +263,14 @@ module ActiveSupport
def increment(name, amount = 1, options = nil)
instrument :increment, name, amount: amount do
failsafe :increment do
- redis.with { |c| c.incrby normalize_key(name, options), amount }
+ options = merged_options(options)
+ key = normalize_key(name, options)
+
+ redis.with do |c|
+ c.incrby(key, amount).tap do
+ write_key_expiry(c, key, options)
+ end
+ end
end
end
end
@@ -274,7 +286,14 @@ module ActiveSupport
def decrement(name, amount = 1, options = nil)
instrument :decrement, name, amount: amount do
failsafe :decrement do
- redis.with { |c| c.decrby normalize_key(name, options), amount }
+ options = merged_options(options)
+ key = normalize_key(name, options)
+
+ redis.with do |c|
+ c.decrby(key, amount).tap do
+ write_key_expiry(c, key, options)
+ end
+ end
end
end
end
@@ -385,6 +404,12 @@ module ActiveSupport
end
end
+ def write_key_expiry(client, key, options)
+ if options[:expires_in] && client.ttl(key).negative?
+ client.expire key, options[:expires_in].to_i
+ end
+ end
+
# Delete an entry from the cache.
def delete_entry(key, options)
failsafe :delete_entry, returning: false do
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index a1b841ec3d..d0644a0f7e 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -23,6 +23,9 @@ module ActiveSupport
# +ClassMethods.set_callback+), and run the installed callbacks at the
# appropriate times (via +run_callbacks+).
#
+ # By default callbacks are halted by throwing +:abort+.
+ # See +ClassMethods.define_callbacks+ for details.
+ #
# Three kinds of callbacks are supported: before callbacks, run before a
# certain event; after callbacks, run after the event; and around callbacks,
# blocks that surround the event, triggering it when they yield. Callback code
@@ -497,9 +500,7 @@ module ActiveSupport
arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
end
- def nested
- @nested
- end
+ attr_reader :nested
def final?
!@call_template
@@ -578,7 +579,7 @@ module ActiveSupport
end
protected
- def chain; @chain; end
+ attr_reader :chain
private
@@ -659,9 +660,17 @@ module ActiveSupport
# * <tt>:if</tt> - A symbol or an array of symbols, each naming an instance
# method or a proc; the callback will be called only when they all return
# a true value.
+ #
+ # If a proc is given, its body is evaluated in the context of the
+ # current object. It can also optionally accept the current object as
+ # an argument.
# * <tt>:unless</tt> - A symbol or an array of symbols, each naming an
# instance method or a proc; the callback will be called only when they
# all return a false value.
+ #
+ # If a proc is given, its body is evaluated in the context of the
+ # current object. It can also optionally accept the current object as
+ # an argument.
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
# existing chain rather than appended.
def set_callback(name, *filter_list, &block)
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index b0a0d845e5..5d356a0ab6 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -125,9 +125,13 @@ module ActiveSupport
def included(base = nil, &block)
if base.nil?
- raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
-
- @_included_block = block
+ if instance_variable_defined?(:@_included_block)
+ if @_included_block.source_location != block.source_location
+ raise MultipleIncludedBlocks
+ end
+ else
+ @_included_block = block
+ end
else
super
end
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 4d6f7819bb..9acf674c40 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -2,8 +2,6 @@
require "active_support/concern"
require "active_support/ordered_options"
-require "active_support/core_ext/array/extract_options"
-require "active_support/core_ext/regexp"
module ActiveSupport
# Configurable provides a <tt>config</tt> method to store and retrieve
@@ -106,9 +104,7 @@ module ActiveSupport
# end
#
# User.hair_colors # => [:brown, :black, :blonde, :red]
- def config_accessor(*names)
- options = names.extract_options!
-
+ def config_accessor(*names, instance_reader: true, instance_writer: true, instance_accessor: true) # :doc:
names.each do |name|
raise NameError.new("invalid config attribute name") unless /\A[_A-Za-z]\w*\z/.match?(name)
@@ -118,9 +114,9 @@ module ActiveSupport
singleton_class.class_eval reader, __FILE__, reader_line
singleton_class.class_eval writer, __FILE__, writer_line
- unless options[:instance_accessor] == false
- class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
- class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
+ if instance_accessor
+ class_eval reader, __FILE__, reader_line if instance_reader
+ class_eval writer, __FILE__, writer_line if instance_writer
end
send("#{name}=", yield) if block_given?
end
diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb
index 6d83b76882..a2569c798b 100644
--- a/activesupport/lib/active_support/core_ext/array.rb
+++ b/activesupport/lib/active_support/core_ext/array.rb
@@ -3,6 +3,7 @@
require "active_support/core_ext/array/wrap"
require "active_support/core_ext/array/access"
require "active_support/core_ext/array/conversions"
+require "active_support/core_ext/array/extract"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/array/grouping"
require "active_support/core_ext/array/prepend_and_append"
diff --git a/activesupport/lib/active_support/core_ext/array/extract.rb b/activesupport/lib/active_support/core_ext/array/extract.rb
new file mode 100644
index 0000000000..cc5a8a3f88
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/array/extract.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class Array
+ # Removes and returns the elements for which the block returns a true value.
+ # If no block is given, an Enumerator is returned instead.
+ #
+ # numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ # odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
+ # numbers # => [0, 2, 4, 6, 8]
+ def extract!
+ return to_enum(:extract!) { size } unless block_given?
+
+ extracted_elements = []
+
+ reject! do |element|
+ extracted_elements << element if yield(element)
+ end
+
+ extracted_elements
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index 75e65337b7..56fb46a88d 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -3,7 +3,7 @@
class Class
begin
# Test if this Ruby supports each_object against singleton_class
- ObjectSpace.each_object(Numeric.singleton_class) {}
+ ObjectSpace.each_object(Numeric.singleton_class) { }
# Returns an array with all classes that are < than its receiver.
#
diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
index de13f00e60..05abd83221 100644
--- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
@@ -5,13 +5,13 @@ require "active_support/core_ext/object/try"
module DateAndTime
module Calculations
DAYS_INTO_WEEK = {
- monday: 0,
- tuesday: 1,
- wednesday: 2,
- thursday: 3,
- friday: 4,
- saturday: 5,
- sunday: 6
+ sunday: 0,
+ monday: 1,
+ tuesday: 2,
+ wednesday: 3,
+ thursday: 4,
+ friday: 5,
+ saturday: 6
}
WEEKEND_DAYS = [ 6, 0 ]
@@ -60,12 +60,12 @@ module DateAndTime
!WEEKEND_DAYS.include?(wday)
end
- # Returns true if the date/time before <tt>date_or_time</tt>.
+ # Returns true if the date/time falls before <tt>date_or_time</tt>.
def before?(date_or_time)
self < date_or_time
end
- # Returns true if the date/time after <tt>date_or_time</tt>.
+ # Returns true if the date/time falls after <tt>date_or_time</tt>.
def after?(date_or_time)
self > date_or_time
end
@@ -263,9 +263,8 @@ module DateAndTime
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
def days_to_week_start(start_day = Date.beginning_of_week)
- start_day_number = DAYS_INTO_WEEK[start_day]
- current_day_number = wday != 0 ? wday - 1 : 6
- (current_day_number - start_day_number) % 7
+ start_day_number = DAYS_INTO_WEEK.fetch(start_day)
+ (wday - start_day_number) % 7
end
# Returns a new date/time representing the start of this week on the given day.
@@ -346,8 +345,7 @@ module DateAndTime
# today.next_occurring(:monday) # => Mon, 18 Dec 2017
# today.next_occurring(:thursday) # => Thu, 21 Dec 2017
def next_occurring(day_of_week)
- current_day_number = wday != 0 ? wday - 1 : 6
- from_now = DAYS_INTO_WEEK.fetch(day_of_week) - current_day_number
+ from_now = DAYS_INTO_WEEK.fetch(day_of_week) - wday
from_now += 7 unless from_now > 0
advance(days: from_now)
end
@@ -358,8 +356,7 @@ module DateAndTime
# today.prev_occurring(:monday) # => Mon, 11 Dec 2017
# today.prev_occurring(:thursday) # => Thu, 07 Dec 2017
def prev_occurring(day_of_week)
- current_day_number = wday != 0 ? wday - 1 : 6
- ago = current_day_number - DAYS_INTO_WEEK.fetch(day_of_week)
+ ago = wday - DAYS_INTO_WEEK.fetch(day_of_week)
ago += 7 unless ago > 0
advance(days: -ago)
end
@@ -374,7 +371,7 @@ module DateAndTime
end
def days_span(day)
- (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
+ (DAYS_INTO_WEEK.fetch(day) - DAYS_INTO_WEEK.fetch(Date.beginning_of_week)) % 7
end
def copy_time_to(other)
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index e61b23f842..bc670c3e76 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -110,7 +110,7 @@ class DateTime
# instance time. Do not use this method in combination with x.months, use
# months_since instead!
def since(seconds)
- self + Rational(seconds.round, 86400)
+ self + Rational(seconds, 86400)
end
alias :in :since
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index edde4f46b9..d87d63f287 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -1,14 +1,21 @@
# frozen_string_literal: true
module Enumerable
+ INDEX_WITH_DEFAULT = Object.new
+ private_constant :INDEX_WITH_DEFAULT
+
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
# when we omit an identity.
+ # :stopdoc:
+
# 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 # :nodoc:
+ alias :_original_sum_with_required_identity :sum
private :_original_sum_with_required_identity
+ # :startdoc:
+
# Calculates a sum from the elements.
#
# payments.sum { |p| p.price * p.tax_rate }
@@ -37,10 +44,11 @@ module Enumerable
end
end
- # Convert an enumerable to a hash.
+ # Convert an enumerable to a hash keying it by the block return value.
#
# people.index_by(&:login)
# # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
+ #
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
# # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
def index_by
@@ -53,6 +61,26 @@ module Enumerable
end
end
+ # Convert an enumerable to a hash keying it with the enumerable items and with the values returned in the block.
+ #
+ # post = Post.new(title: "hey there", body: "what's up?")
+ #
+ # %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
+ # # => { title: "hey there", body: "what's up?" }
+ def index_with(default = INDEX_WITH_DEFAULT)
+ if block_given?
+ result = {}
+ each { |elem| result[elem] = yield(elem) }
+ result
+ elsif default != INDEX_WITH_DEFAULT
+ result = {}
+ each { |elem| result[elem] = default }
+ result
+ else
+ to_enum(:index_with) { size if respond_to?(:size) }
+ end
+ end
+
# Returns +true+ if the enumerable has more than 1 element. Functionally
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb
index 8e288833b6..9deceb1bb4 100644
--- a/activesupport/lib/active_support/core_ext/file/atomic.rb
+++ b/activesupport/lib/active_support/core_ext/file/atomic.rb
@@ -29,7 +29,7 @@ class File
old_stat = if exist?(file_name)
# Get original file permissions
stat(file_name)
- elsif temp_dir != dirname(file_name)
+ else
# If not possible, probe which are the default permissions in the
# destination directory.
probe_stat_in(dirname(file_name))
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index 2bd0a56ea4..e07a5498ff 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -27,8 +27,9 @@ class Hash
# Replaces the hash with only the given keys.
# Returns a hash containing the removed key/value pairs.
#
- # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
- # # => {:c=>3, :d=>4}
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
+ # hash.slice!(:a, :b) # => {:c=>3, :d=>4}
+ # hash # => {:a=>1, :b=>2}
def slice!(*keys)
omit = slice(*self.keys - keys)
hash = slice(*keys)
diff --git a/activesupport/lib/active_support/core_ext/integer/multiple.rb b/activesupport/lib/active_support/core_ext/integer/multiple.rb
index e7606662d3..bd57a909c5 100644
--- a/activesupport/lib/active_support/core_ext/integer/multiple.rb
+++ b/activesupport/lib/active_support/core_ext/integer/multiple.rb
@@ -7,6 +7,6 @@ class Integer
# 6.multiple_of?(5) # => false
# 10.multiple_of?(2) # => true
def multiple_of?(number)
- number != 0 ? self % number == 0 : zero?
+ number == 0 ? self == 0 : self % number == 0
end
end
diff --git a/activesupport/lib/active_support/core_ext/load_error.rb b/activesupport/lib/active_support/core_ext/load_error.rb
index 6b0dcab905..b81ed0605e 100644
--- a/activesupport/lib/active_support/core_ext/load_error.rb
+++ b/activesupport/lib/active_support/core_ext/load_error.rb
@@ -4,6 +4,6 @@ class LoadError
# Returns true if the given path name (except perhaps for the ".rb"
# extension) is the missing file which caused the exception to be raised.
def is_missing?(location)
- location.sub(/\.rb$/, "".freeze) == path.to_s.sub(/\.rb$/, "".freeze)
+ location.sub(/\.rb$/, "") == path.to_s.sub(/\.rb$/, "")
end
end
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 01fee0fb74..5850e0193f 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -1,8 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/array/extract_options"
-require "active_support/core_ext/regexp"
-
# Extends the module object with class/module and instance accessors for
# class/module attributes, just like the native attr* accessors for instance
# attributes.
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 4b9b6ea9bd..cb996e9e6d 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
@@ -1,8 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/array/extract_options"
-require "active_support/core_ext/regexp"
-
# Extends the module object with class/module and instance accessors for
# class/module attributes, just like the native attr* accessors for instance
# attributes, but does so on a per-thread basis.
@@ -36,9 +33,7 @@ class Module
# end
#
# Current.new.user # => NoMethodError
- def thread_mattr_reader(*syms) # :nodoc:
- options = syms.extract_options!
-
+ def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true) # :nodoc:
syms.each do |sym|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
@@ -50,7 +45,7 @@ class Module
end
EOS
- unless options[:instance_reader] == false || options[:instance_accessor] == false
+ if instance_reader && instance_accessor
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{sym}
self.class.#{sym}
@@ -79,8 +74,7 @@ class Module
# end
#
# Current.new.user = "DHH" # => NoMethodError
- def thread_mattr_writer(*syms) # :nodoc:
- options = syms.extract_options!
+ def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true) # :nodoc:
syms.each do |sym|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
@@ -92,7 +86,7 @@ class Module
end
EOS
- unless options[:instance_writer] == false || options[:instance_accessor] == false
+ if instance_writer && instance_accessor
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{sym}=(obj)
self.class.#{sym} = obj
@@ -137,14 +131,14 @@ class Module
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
#
# class Current
- # mattr_accessor :user, instance_accessor: false
+ # thread_mattr_accessor :user, instance_accessor: false
# end
#
# Current.new.user = "DHH" # => NoMethodError
# Current.new.user # => NoMethodError
- def thread_mattr_accessor(*syms)
- thread_mattr_reader(*syms)
- thread_mattr_writer(*syms)
+ def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true)
+ thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor)
+ thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
end
alias :thread_cattr_accessor :thread_mattr_accessor
end
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 7f42f44efb..3128539112 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "set"
-require "active_support/core_ext/regexp"
class Module
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb
index c5bb598bd1..9b6df40596 100644
--- a/activesupport/lib/active_support/core_ext/module/introspection.rb
+++ b/activesupport/lib/active_support/core_ext/module/introspection.rb
@@ -5,8 +5,8 @@ require "active_support/inflector"
class Module
# Returns the name of the module containing this one.
#
- # M::N.parent_name # => "M"
- def parent_name
+ # M::N.module_parent_name # => "M"
+ def module_parent_name
if defined?(@parent_name)
@parent_name
else
@@ -16,6 +16,14 @@ class Module
end
end
+ def parent_name
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `Module#parent_name` has been renamed to `module_parent_name`.
+ `parent_name` is deprecated and will be removed in Rails 6.1.
+ MSG
+ module_parent_name
+ end
+
# Returns the module which contains this one according to its name.
#
# module M
@@ -24,15 +32,23 @@ class Module
# end
# X = M::N
#
- # M::N.parent # => M
- # X.parent # => M
+ # M::N.module_parent # => M
+ # X.module_parent # => M
#
# The parent of top-level and anonymous modules is Object.
#
- # M.parent # => Object
- # Module.new.parent # => Object
+ # M.module_parent # => Object
+ # Module.new.module_parent # => Object
+ def module_parent
+ module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object
+ end
+
def parent
- parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `Module#parent` has been renamed to `module_parent`.
+ `parent` is deprecated and will be removed in Rails 6.1.
+ MSG
+ module_parent
end
# Returns all the parents of this module according to its name, ordered from
@@ -44,13 +60,13 @@ class Module
# end
# X = M::N
#
- # M.parents # => [Object]
- # M::N.parents # => [M, Object]
- # X.parents # => [M, Object]
- def parents
+ # M.module_parents # => [Object]
+ # M::N.module_parents # => [M, Object]
+ # X.module_parents # => [M, Object]
+ def module_parents
parents = []
- if parent_name
- parts = parent_name.split("::")
+ if module_parent_name
+ parts = module_parent_name.split("::")
until parts.empty?
parents << ActiveSupport::Inflector.constantize(parts * "::")
parts.pop
@@ -59,4 +75,12 @@ class Module
parents << Object unless parents.include? Object
parents
end
+
+ def parents
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `Module#parents` has been renamed to `module_parents`.
+ `parents` is deprecated and will be removed in Rails 6.1.
+ MSG
+ module_parents
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
index 7fcd0d0311..8acad6164a 100644
--- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
@@ -4,127 +4,129 @@ require "active_support/core_ext/big_decimal/conversions"
require "active_support/number_helper"
require "active_support/core_ext/module/deprecation"
-module ActiveSupport::NumericWithFormat
- # Provides options for converting numbers into formatted strings.
- # Options are provided for phone numbers, currency, percentage,
- # precision, positional notation, file size and pretty printing.
- #
- # ==== Options
- #
- # For details on which formats use which options, see ActiveSupport::NumberHelper
- #
- # ==== Examples
- #
- # Phone Numbers:
- # 5551234.to_s(:phone) # => "555-1234"
- # 1235551234.to_s(:phone) # => "123-555-1234"
- # 1235551234.to_s(:phone, area_code: true) # => "(123) 555-1234"
- # 1235551234.to_s(:phone, delimiter: ' ') # => "123 555 1234"
- # 1235551234.to_s(:phone, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
- # 1235551234.to_s(:phone, country_code: 1) # => "+1-123-555-1234"
- # 1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
- # # => "+1.123.555.1234 x 1343"
- #
- # Currency:
- # 1234567890.50.to_s(:currency) # => "$1,234,567,890.50"
- # 1234567890.506.to_s(:currency) # => "$1,234,567,890.51"
- # 1234567890.506.to_s(:currency, precision: 3) # => "$1,234,567,890.506"
- # 1234567890.506.to_s(:currency, locale: :fr) # => "1 234 567 890,51 €"
- # -1234567890.50.to_s(:currency, negative_format: '(%u%n)')
- # # => "($1,234,567,890.50)"
- # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '')
- # # => "&pound;1234567890,50"
- # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
- # # => "1234567890,50 &pound;"
- #
- # Percentage:
- # 100.to_s(:percentage) # => "100.000%"
- # 100.to_s(:percentage, precision: 0) # => "100%"
- # 1000.to_s(:percentage, delimiter: '.', separator: ',') # => "1.000,000%"
- # 302.24398923423.to_s(:percentage, precision: 5) # => "302.24399%"
- # 1000.to_s(:percentage, locale: :fr) # => "1 000,000%"
- # 100.to_s(:percentage, format: '%n %') # => "100.000 %"
- #
- # Delimited:
- # 12345678.to_s(:delimited) # => "12,345,678"
- # 12345678.05.to_s(:delimited) # => "12,345,678.05"
- # 12345678.to_s(:delimited, delimiter: '.') # => "12.345.678"
- # 12345678.to_s(:delimited, delimiter: ',') # => "12,345,678"
- # 12345678.05.to_s(:delimited, separator: ' ') # => "12,345,678 05"
- # 12345678.05.to_s(:delimited, locale: :fr) # => "12 345 678,05"
- # 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
- # # => "98 765 432,98"
- #
- # Rounded:
- # 111.2345.to_s(:rounded) # => "111.235"
- # 111.2345.to_s(:rounded, precision: 2) # => "111.23"
- # 13.to_s(:rounded, precision: 5) # => "13.00000"
- # 389.32314.to_s(:rounded, precision: 0) # => "389"
- # 111.2345.to_s(:rounded, significant: true) # => "111"
- # 111.2345.to_s(:rounded, precision: 1, significant: true) # => "100"
- # 13.to_s(:rounded, precision: 5, significant: true) # => "13.000"
- # 111.234.to_s(:rounded, locale: :fr) # => "111,234"
- # 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
- # # => "13"
- # 389.32314.to_s(:rounded, precision: 4, significant: true) # => "389.3"
- # 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
- # # => "1.111,23"
- #
- # Human-friendly size in Bytes:
- # 123.to_s(:human_size) # => "123 Bytes"
- # 1234.to_s(:human_size) # => "1.21 KB"
- # 12345.to_s(:human_size) # => "12.1 KB"
- # 1234567.to_s(:human_size) # => "1.18 MB"
- # 1234567890.to_s(:human_size) # => "1.15 GB"
- # 1234567890123.to_s(:human_size) # => "1.12 TB"
- # 1234567890123456.to_s(:human_size) # => "1.1 PB"
- # 1234567890123456789.to_s(:human_size) # => "1.07 EB"
- # 1234567.to_s(:human_size, precision: 2) # => "1.2 MB"
- # 483989.to_s(:human_size, precision: 2) # => "470 KB"
- # 1234567.to_s(:human_size, precision: 2, separator: ',') # => "1,2 MB"
- # 1234567890123.to_s(:human_size, precision: 5) # => "1.1228 TB"
- # 524288000.to_s(:human_size, precision: 5) # => "500 MB"
- #
- # Human-friendly format:
- # 123.to_s(:human) # => "123"
- # 1234.to_s(:human) # => "1.23 Thousand"
- # 12345.to_s(:human) # => "12.3 Thousand"
- # 1234567.to_s(:human) # => "1.23 Million"
- # 1234567890.to_s(:human) # => "1.23 Billion"
- # 1234567890123.to_s(:human) # => "1.23 Trillion"
- # 1234567890123456.to_s(:human) # => "1.23 Quadrillion"
- # 1234567890123456789.to_s(:human) # => "1230 Quadrillion"
- # 489939.to_s(:human, precision: 2) # => "490 Thousand"
- # 489939.to_s(:human, precision: 4) # => "489.9 Thousand"
- # 1234567.to_s(:human, precision: 4,
- # significant: false) # => "1.2346 Million"
- # 1234567.to_s(:human, precision: 1,
- # separator: ',',
- # significant: false) # => "1,2 Million"
- def to_s(format = nil, options = nil)
- case format
- when nil
- super()
- when Integer, String
- super(format)
- when :phone
- ActiveSupport::NumberHelper.number_to_phone(self, options || {})
- when :currency
- ActiveSupport::NumberHelper.number_to_currency(self, options || {})
- when :percentage
- ActiveSupport::NumberHelper.number_to_percentage(self, options || {})
- when :delimited
- ActiveSupport::NumberHelper.number_to_delimited(self, options || {})
- when :rounded
- ActiveSupport::NumberHelper.number_to_rounded(self, options || {})
- when :human
- ActiveSupport::NumberHelper.number_to_human(self, options || {})
- when :human_size
- ActiveSupport::NumberHelper.number_to_human_size(self, options || {})
- when Symbol
- super()
- else
- super(format)
+module ActiveSupport
+ module NumericWithFormat
+ # Provides options for converting numbers into formatted strings.
+ # Options are provided for phone numbers, currency, percentage,
+ # precision, positional notation, file size and pretty printing.
+ #
+ # ==== Options
+ #
+ # For details on which formats use which options, see ActiveSupport::NumberHelper
+ #
+ # ==== Examples
+ #
+ # Phone Numbers:
+ # 5551234.to_s(:phone) # => "555-1234"
+ # 1235551234.to_s(:phone) # => "123-555-1234"
+ # 1235551234.to_s(:phone, area_code: true) # => "(123) 555-1234"
+ # 1235551234.to_s(:phone, delimiter: ' ') # => "123 555 1234"
+ # 1235551234.to_s(:phone, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
+ # 1235551234.to_s(:phone, country_code: 1) # => "+1-123-555-1234"
+ # 1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
+ # # => "+1.123.555.1234 x 1343"
+ #
+ # Currency:
+ # 1234567890.50.to_s(:currency) # => "$1,234,567,890.50"
+ # 1234567890.506.to_s(:currency) # => "$1,234,567,890.51"
+ # 1234567890.506.to_s(:currency, precision: 3) # => "$1,234,567,890.506"
+ # 1234567890.506.to_s(:currency, locale: :fr) # => "1 234 567 890,51 €"
+ # -1234567890.50.to_s(:currency, negative_format: '(%u%n)')
+ # # => "($1,234,567,890.50)"
+ # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '')
+ # # => "&pound;1234567890,50"
+ # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
+ # # => "1234567890,50 &pound;"
+ #
+ # Percentage:
+ # 100.to_s(:percentage) # => "100.000%"
+ # 100.to_s(:percentage, precision: 0) # => "100%"
+ # 1000.to_s(:percentage, delimiter: '.', separator: ',') # => "1.000,000%"
+ # 302.24398923423.to_s(:percentage, precision: 5) # => "302.24399%"
+ # 1000.to_s(:percentage, locale: :fr) # => "1 000,000%"
+ # 100.to_s(:percentage, format: '%n %') # => "100.000 %"
+ #
+ # Delimited:
+ # 12345678.to_s(:delimited) # => "12,345,678"
+ # 12345678.05.to_s(:delimited) # => "12,345,678.05"
+ # 12345678.to_s(:delimited, delimiter: '.') # => "12.345.678"
+ # 12345678.to_s(:delimited, delimiter: ',') # => "12,345,678"
+ # 12345678.05.to_s(:delimited, separator: ' ') # => "12,345,678 05"
+ # 12345678.05.to_s(:delimited, locale: :fr) # => "12 345 678,05"
+ # 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
+ # # => "98 765 432,98"
+ #
+ # Rounded:
+ # 111.2345.to_s(:rounded) # => "111.235"
+ # 111.2345.to_s(:rounded, precision: 2) # => "111.23"
+ # 13.to_s(:rounded, precision: 5) # => "13.00000"
+ # 389.32314.to_s(:rounded, precision: 0) # => "389"
+ # 111.2345.to_s(:rounded, significant: true) # => "111"
+ # 111.2345.to_s(:rounded, precision: 1, significant: true) # => "100"
+ # 13.to_s(:rounded, precision: 5, significant: true) # => "13.000"
+ # 111.234.to_s(:rounded, locale: :fr) # => "111,234"
+ # 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
+ # # => "13"
+ # 389.32314.to_s(:rounded, precision: 4, significant: true) # => "389.3"
+ # 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
+ # # => "1.111,23"
+ #
+ # Human-friendly size in Bytes:
+ # 123.to_s(:human_size) # => "123 Bytes"
+ # 1234.to_s(:human_size) # => "1.21 KB"
+ # 12345.to_s(:human_size) # => "12.1 KB"
+ # 1234567.to_s(:human_size) # => "1.18 MB"
+ # 1234567890.to_s(:human_size) # => "1.15 GB"
+ # 1234567890123.to_s(:human_size) # => "1.12 TB"
+ # 1234567890123456.to_s(:human_size) # => "1.1 PB"
+ # 1234567890123456789.to_s(:human_size) # => "1.07 EB"
+ # 1234567.to_s(:human_size, precision: 2) # => "1.2 MB"
+ # 483989.to_s(:human_size, precision: 2) # => "470 KB"
+ # 1234567.to_s(:human_size, precision: 2, separator: ',') # => "1,2 MB"
+ # 1234567890123.to_s(:human_size, precision: 5) # => "1.1228 TB"
+ # 524288000.to_s(:human_size, precision: 5) # => "500 MB"
+ #
+ # Human-friendly format:
+ # 123.to_s(:human) # => "123"
+ # 1234.to_s(:human) # => "1.23 Thousand"
+ # 12345.to_s(:human) # => "12.3 Thousand"
+ # 1234567.to_s(:human) # => "1.23 Million"
+ # 1234567890.to_s(:human) # => "1.23 Billion"
+ # 1234567890123.to_s(:human) # => "1.23 Trillion"
+ # 1234567890123456.to_s(:human) # => "1.23 Quadrillion"
+ # 1234567890123456789.to_s(:human) # => "1230 Quadrillion"
+ # 489939.to_s(:human, precision: 2) # => "490 Thousand"
+ # 489939.to_s(:human, precision: 4) # => "489.9 Thousand"
+ # 1234567.to_s(:human, precision: 4,
+ # significant: false) # => "1.2346 Million"
+ # 1234567.to_s(:human, precision: 1,
+ # separator: ',',
+ # significant: false) # => "1,2 Million"
+ def to_s(format = nil, options = nil)
+ case format
+ when nil
+ super()
+ when Integer, String
+ super(format)
+ when :phone
+ ActiveSupport::NumberHelper.number_to_phone(self, options || {})
+ when :currency
+ ActiveSupport::NumberHelper.number_to_currency(self, options || {})
+ when :percentage
+ ActiveSupport::NumberHelper.number_to_percentage(self, options || {})
+ when :delimited
+ ActiveSupport::NumberHelper.number_to_delimited(self, options || {})
+ when :rounded
+ ActiveSupport::NumberHelper.number_to_rounded(self, options || {})
+ when :human
+ ActiveSupport::NumberHelper.number_to_human(self, options || {})
+ when :human_size
+ ActiveSupport::NumberHelper.number_to_human_size(self, options || {})
+ when Symbol
+ super()
+ else
+ super(format)
+ end
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb
index 2ca431ab10..f36fef6cc9 100644
--- a/activesupport/lib/active_support/core_ext/object/blank.rb
+++ b/activesupport/lib/active_support/core_ext/object/blank.rb
@@ -1,11 +1,10 @@
# frozen_string_literal: true
-require "active_support/core_ext/regexp"
require "concurrent/map"
class Object
# An object is blank if it's false, empty, or a whitespace string.
- # For example, +false+, '', ' ', +nil+, [], and {} are all blank.
+ # For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
#
# This simplifies
#
diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb
index abb461966a..bac6ff9c97 100644
--- a/activesupport/lib/active_support/core_ext/object/to_query.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_query.rb
@@ -75,11 +75,14 @@ class Hash
#
# This method is also aliased as +to_param+.
def to_query(namespace = nil)
- collect do |key, value|
+ query = collect do |key, value|
unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
end
- end.compact.sort! * "&"
+ end.compact
+
+ query.sort! unless namespace.to_s.include?("[]")
+ query.join("&")
end
alias_method :to_param, :to_query
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index c874691629..ef8a1f476d 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -4,19 +4,27 @@ require "delegate"
module ActiveSupport
module Tryable #:nodoc:
- def try(*a, &b)
- try!(*a, &b) if a.empty? || respond_to?(a.first)
+ def try(method_name = nil, *args, &b)
+ if method_name.nil? && block_given?
+ if b.arity == 0
+ instance_eval(&b)
+ else
+ yield self
+ end
+ elsif respond_to?(method_name)
+ public_send(method_name, *args, &b)
+ end
end
- def try!(*a, &b)
- if a.empty? && block_given?
+ def try!(method_name = nil, *args, &b)
+ if method_name.nil? && block_given?
if b.arity == 0
instance_eval(&b)
else
yield self
end
else
- public_send(*a, &b)
+ public_send(method_name, *args, &b)
end
end
end
@@ -135,14 +143,14 @@ class NilClass
#
# With +try+
# @person.try(:children).try(:first).try(:name)
- def try(*args)
+ def try(method_name = nil, *args)
nil
end
# Calling +try!+ on +nil+ always returns +nil+.
#
# nil.try!(:name) # => nil
- def try!(*args)
+ def try!(method_name = nil, *args)
nil
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb
index 2838fd76be..1d46add6e0 100644
--- a/activesupport/lib/active_support/core_ext/object/with_options.rb
+++ b/activesupport/lib/active_support/core_ext/object/with_options.rb
@@ -68,7 +68,7 @@ class Object
# You can access these methods using the class name instead:
#
# class Phone < ActiveRecord::Base
- # enum phone_number_type: [home: 0, office: 1, mobile: 2]
+ # enum phone_number_type: { home: 0, office: 1, mobile: 2 }
#
# with_options presence: true do
# validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb
index 4074e91d17..78814fd189 100644
--- a/activesupport/lib/active_support/core_ext/range.rb
+++ b/activesupport/lib/active_support/core_ext/range.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "active_support/core_ext/range/conversions"
-require "active_support/core_ext/range/include_range"
+require "active_support/core_ext/range/compare_range"
require "active_support/core_ext/range/include_time_with_zone"
require "active_support/core_ext/range/overlaps"
require "active_support/core_ext/range/each"
diff --git a/activesupport/lib/active_support/core_ext/range/compare_range.rb b/activesupport/lib/active_support/core_ext/range/compare_range.rb
new file mode 100644
index 0000000000..6f6d2a27bb
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/range/compare_range.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+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
+ #
+ # The native Range#=== behavior is untouched.
+ # ('a'..'f') === ('c') # => true
+ # (5..9) === (11) # => false
+ def ===(value)
+ if value.is_a?(::Range)
+ # 1...10 includes 1..9 but it does not include 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.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
+ #
+ # The native Range#include? behavior is untouched.
+ # ('a'..'f').include?('c') # => true
+ # (5..9).include?(11) # => false
+ def include?(value)
+ if value.is_a?(::Range)
+ # 1...10 includes 1..9 but it does not include 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.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
+ #
+ # The native Range#cover? behavior is untouched.
+ # ('a'..'f').cover?('c') # => true
+ # (5..9).cover?(11) # => false
+ def cover?(value)
+ if value.is_a?(::Range)
+ # 1...10 covers 1..9 but it does not cover 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.send(operator, last)
+ else
+ super
+ end
+ end
+ end
+end
+
+Range.prepend(ActiveSupport::CompareWithRange)
diff --git a/activesupport/lib/active_support/core_ext/range/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb
index 8832fbcb3c..024e32db40 100644
--- a/activesupport/lib/active_support/core_ext/range/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/range/conversions.rb
@@ -1,39 +1,41 @@
# frozen_string_literal: true
-module ActiveSupport::RangeWithFormat
- RANGE_FORMATS = {
- db: -> (start, stop) do
- case start
- when String then "BETWEEN '#{start}' AND '#{stop}'"
+module ActiveSupport
+ module RangeWithFormat
+ RANGE_FORMATS = {
+ db: -> (start, stop) do
+ case start
+ when String then "BETWEEN '#{start}' AND '#{stop}'"
+ else
+ "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'"
+ end
+ end
+ }
+
+ # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
+ #
+ # range = (1..100) # => 1..100
+ #
+ # range.to_s # => "1..100"
+ # range.to_s(:db) # => "BETWEEN '1' AND '100'"
+ #
+ # == Adding your own range formats to to_s
+ # You can add your own formats to the Range::RANGE_FORMATS hash.
+ # Use the format name as the hash key and a Proc instance.
+ #
+ # # config/initializers/range_formats.rb
+ # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
+ def to_s(format = :default)
+ if formatter = RANGE_FORMATS[format]
+ formatter.call(first, last)
else
- "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'"
+ super()
end
end
- }
- # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
- #
- # range = (1..100) # => 1..100
- #
- # range.to_s # => "1..100"
- # range.to_s(:db) # => "BETWEEN '1' AND '100'"
- #
- # == Adding your own range formats to to_s
- # You can add your own formats to the Range::RANGE_FORMATS hash.
- # Use the format name as the hash key and a Proc instance.
- #
- # # config/initializers/range_formats.rb
- # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
- def to_s(format = :default)
- if formatter = RANGE_FORMATS[format]
- formatter.call(first, last)
- else
- super()
- end
+ alias_method :to_default_s, :to_s
+ alias_method :to_formatted_s, :to_s
end
-
- alias_method :to_default_s, :to_s
- alias_method :to_formatted_s, :to_s
end
Range.prepend(ActiveSupport::RangeWithFormat)
diff --git a/activesupport/lib/active_support/core_ext/range/include_range.rb b/activesupport/lib/active_support/core_ext/range/include_range.rb
index 7ba1011921..2da2c587a3 100644
--- a/activesupport/lib/active_support/core_ext/range/include_range.rb
+++ b/activesupport/lib/active_support/core_ext/range/include_range.rb
@@ -1,25 +1,9 @@
# frozen_string_literal: true
-module ActiveSupport
- module IncludeWithRange #:nodoc:
- # 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
- #
- # The native Range#include? behavior is untouched.
- # ('a'..'f').include?('c') # => true
- # (5..9).include?(11) # => false
- def include?(value)
- if value.is_a?(::Range)
- # 1...10 includes 1..9 but it does not include 1..10.
- operator = exclude_end? && !value.exclude_end? ? :< : :<=
- super(value.first) && value.last.send(operator, last)
- else
- super
- end
- end
- end
-end
+require "active_support/deprecation"
-Range.prepend(ActiveSupport::IncludeWithRange)
+ActiveSupport::Deprecation.warn "You have required `active_support/core_ext/range/include_range`. " \
+"This file will be removed in Rails 6.1. You should require `active_support/core_ext/range/compare_range` " \
+ "instead."
+
+require "active_support/core_ext/range/compare_range"
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index 58591bbaaf..4ca24028b0 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -75,6 +75,10 @@ class String
# str.first(0) # => ""
# str.first(6) # => "hello"
def first(limit = 1)
+ ActiveSupport::Deprecation.warn(
+ "Calling String#first with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ ) if limit < 0
if limit == 0
""
elsif limit >= size
@@ -95,6 +99,10 @@ class String
# str.last(0) # => ""
# str.last(6) # => "hello"
def last(limit = 1)
+ ActiveSupport::Deprecation.warn(
+ "Calling String#last with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ ) if limit < 0
if limit == 0
""
elsif limit >= size
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 f3bdc2977e..3a80de4617 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -134,8 +134,9 @@ end
module ActiveSupport #:nodoc:
class SafeBuffer < String
UNSAFE_STRING_METHODS = %w(
- capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
- slice squeeze strip sub succ swapcase tr tr_s upcase
+ 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
)
alias_method :original_concat, :concat
@@ -149,9 +150,7 @@ module ActiveSupport #:nodoc:
end
def [](*args)
- if args.size < 2
- super
- elsif html_safe?
+ if html_safe?
new_safe_buffer = super
if new_safe_buffer
@@ -188,10 +187,22 @@ module ActiveSupport #:nodoc:
end
alias << concat
+ def insert(index, value)
+ super(index, html_escape_interpolated_argument(value))
+ end
+
def prepend(value)
super(html_escape_interpolated_argument(value))
end
+ def replace(value)
+ super(html_escape_interpolated_argument(value))
+ end
+
+ def []=(index, value)
+ super(index, html_escape_interpolated_argument(value))
+ end
+
def +(other)
dup.concat(other)
end
diff --git a/activesupport/lib/active_support/core_ext/string/strip.rb b/activesupport/lib/active_support/core_ext/string/strip.rb
index 6f9834bb16..60e9952ee6 100644
--- a/activesupport/lib/active_support/core_ext/string/strip.rb
+++ b/activesupport/lib/active_support/core_ext/string/strip.rb
@@ -20,7 +20,7 @@ class String
# Technically, it looks for the least indented non-empty line
# in the whole string, and removes that amount of leading whitespace.
def strip_heredoc
- gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze).tap do |stripped|
+ gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "").tap do |stripped|
stripped.freeze if frozen?
end
end
diff --git a/activesupport/lib/active_support/current_attributes.rb b/activesupport/lib/active_support/current_attributes.rb
index 4e6d8e4585..3145ff87a1 100644
--- a/activesupport/lib/active_support/current_attributes.rb
+++ b/activesupport/lib/active_support/current_attributes.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/callbacks"
+
module ActiveSupport
# Abstract super class that provides a thread-isolated attributes singleton, which resets automatically
# before and after each request. This allows you to keep all the per-request attributes easily
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 0f59558bb5..d5d00b5e6e 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -79,6 +79,12 @@ module ActiveSupport #:nodoc:
# to allow arbitrary constants to be marked for unloading.
mattr_accessor :explicitly_unloadable_constants, default: []
+ # The logger used when tracing autoloads.
+ mattr_accessor :logger
+
+ # If true, trace autoloads with +logger.debug+.
+ mattr_accessor :verbose, default: false
+
# The WatchStack keeps a stack of the modules being watched as files are
# loaded. If a file in the process of being loaded (parent.rb) triggers the
# load of another file (child.rb) the stack will ensure that child.rb
@@ -97,6 +103,8 @@ module ActiveSupport #:nodoc:
# parent.rb then requires namespace/child.rb, the stack will look like
# [[Object], [Namespace]].
+ attr_reader :watching
+
def initialize
@watching = []
@stack = Hash.new { |h, k| h[k] = [] }
@@ -138,7 +146,7 @@ module ActiveSupport #:nodoc:
# Normalize the list of new constants, and add them to the list we will return
new_constants.each do |suffix|
- constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
+ constants << ([namespace, suffix] - ["Object"]).join("::")
end
end
constants
@@ -224,6 +232,8 @@ module ActiveSupport #:nodoc:
Dependencies.require_or_load(file_name)
end
+ # :doc:
+
# Interprets a file using <tt>mechanism</tt> and marks its defined
# constants as autoloaded. <tt>file_name</tt> can be either a string or
# respond to <tt>to_path</tt>.
@@ -242,9 +252,13 @@ module ActiveSupport #:nodoc:
Dependencies.depend_on(file_name, message)
end
+ # :nodoc:
+
def load_dependency(file)
if Dependencies.load? && Dependencies.constant_watch_stack.watching?
- Dependencies.new_constants_in(Object) { yield }
+ descs = Dependencies.constant_watch_stack.watching.flatten.uniq
+
+ Dependencies.new_constants_in(*descs) { yield }
else
yield
end
@@ -341,7 +355,7 @@ module ActiveSupport #:nodoc:
end
def require_or_load(file_name, const_path = nil)
- file_name = $` if file_name =~ /\.rb\z/
+ file_name = file_name.chomp(".rb")
expanded = File.expand_path(file_name)
return if loaded.include?(expanded)
@@ -391,7 +405,7 @@ module ActiveSupport #:nodoc:
# constant paths which would cause Dependencies to attempt to load this
# file.
def loadable_constants_for_path(path, bases = autoload_paths)
- path = $` if path =~ /\.rb\z/
+ path = path.chomp(".rb")
expanded_path = File.expand_path(path)
paths = []
@@ -400,7 +414,7 @@ module ActiveSupport #:nodoc:
next unless expanded_path.start_with?(expanded_root)
root_size = expanded_root.size
- next if expanded_path[root_size] != ?/.freeze
+ next if expanded_path[root_size] != ?/
nesting = expanded_path[(root_size + 1)..-1]
paths << nesting.camelize unless nesting.blank?
@@ -412,7 +426,7 @@ module ActiveSupport #:nodoc:
# Search for a file in autoload_paths matching the provided suffix.
def search_for_file(path_suffix)
- path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb".freeze)
+ path_suffix += ".rb" unless path_suffix.ends_with?(".rb")
autoload_paths.each do |root|
path = File.join(root, path_suffix)
@@ -446,6 +460,7 @@ module ActiveSupport #:nodoc:
return nil unless base_path = autoloadable_module?(path_suffix)
mod = Module.new
into.const_set const_name, mod
+ log("constant #{qualified_name} autoloaded (module autovivified from #{File.join(base_path, path_suffix)})")
autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
autoloaded_constants.uniq!
mod
@@ -487,26 +502,31 @@ module ActiveSupport #:nodoc:
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
end
- qualified_name = qualified_name_for from_mod, const_name
+ qualified_name = qualified_name_for(from_mod, const_name)
path_suffix = qualified_name.underscore
file_path = search_for_file(path_suffix)
if file_path
expanded = File.expand_path(file_path)
- expanded.sub!(/\.rb\z/, "".freeze)
+ expanded.sub!(/\.rb\z/, "")
if loading.include?(expanded)
raise "Circular dependency detected while autoloading constant #{qualified_name}"
else
require_or_load(expanded, qualified_name)
- raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
- return from_mod.const_get(const_name)
+
+ if from_mod.const_defined?(const_name, false)
+ log("constant #{qualified_name} autoloaded from #{expanded}.rb")
+ return from_mod.const_get(const_name)
+ else
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it"
+ end
end
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
return mod
- elsif (parent = from_mod.parent) && parent != from_mod &&
- ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
+ elsif (parent = from_mod.module_parent) && parent != from_mod &&
+ ! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) }
# If our parents do not have a constant named +const_name+ then we are free
# to attempt to load upwards. If they do have such a constant, then this
# const_missing must be due to from_mod::const_name, which should not
@@ -550,6 +570,7 @@ module ActiveSupport #:nodoc:
# as the environment will be in an inconsistent state, e.g. other constants
# may have already been unloaded and not accessible.
def remove_unloadable_constants!
+ log("removing unloadable constants")
autoloaded_constants.each { |const| remove_constant const }
autoloaded_constants.clear
Reference.clear!
@@ -739,6 +760,10 @@ module ActiveSupport #:nodoc:
# The constant is no longer reachable, just skip it.
end
end
+
+ def log(message)
+ logger.debug("autoloading: #{message}") if logger && verbose
+ end
end
end
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index 66d6f3225a..725667d139 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -43,7 +43,7 @@ module ActiveSupport
deprecation_horizon: deprecation_horizon)
},
- silence: ->(message, callstack, deprecation_horizon, gem_name) {},
+ silence: ->(message, callstack, deprecation_horizon, gem_name) { },
}
# Behavior module allows to determine how to display deprecation messages.
@@ -94,6 +94,10 @@ module ActiveSupport
private
def arity_coerce(behavior)
+ unless behavior.respond_to?(:call)
+ raise ArgumentError, "#{behavior.inspect} is not a valid deprecation behavior."
+ end
+
if behavior.arity == 4 || behavior.arity == -1
behavior
else
diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb
index 5be893d281..f0c3e37e65 100644
--- a/activesupport/lib/active_support/deprecation/method_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/module/aliasing"
require "active_support/core_ext/array/extract_options"
module ActiveSupport
@@ -53,24 +52,37 @@ module ActiveSupport
options = method_names.extract_options!
deprecator = options.delete(:deprecator) || self
method_names += options.keys
+ mod = Module.new
- mod = Module.new do
- method_names.each do |method_name|
- define_method(method_name) do |*args, &block|
+ method_names.each do |method_name|
+ if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name)
+ aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ""), $1
+ with_method = "#{aliased_method}_with_deprecation#{punctuation}"
+ without_method = "#{aliased_method}_without_deprecation#{punctuation}"
+
+ target_module.send(:define_method, with_method) do |*args, &block|
deprecator.deprecation_warning(method_name, options[method_name])
- super(*args, &block)
+ send(without_method, *args, &block)
end
+ target_module.send(:alias_method, without_method, method_name)
+ target_module.send(:alias_method, method_name, with_method)
+
case
- when target_module.protected_method_defined?(method_name)
- protected method_name
- when target_module.private_method_defined?(method_name)
- private method_name
+ when target_module.protected_method_defined?(without_method)
+ target_module.send(:protected, method_name)
+ when target_module.private_method_defined?(without_method)
+ target_module.send(:private, method_name)
+ end
+ else
+ mod.send(:define_method, method_name) do |*args, &block|
+ deprecator.deprecation_warning(method_name, options[method_name])
+ super(*args, &block)
end
end
end
- target_module.prepend(mod)
+ target_module.prepend(mod) unless mod.instance_methods(false).empty?
end
end
end
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index 896c0d2d8e..56f1e23136 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/regexp"
-
module ActiveSupport
class Deprecation
class DeprecationProxy #:nodoc:
diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb
index a4cee788b6..05236d3162 100644
--- a/activesupport/lib/active_support/descendants_tracker.rb
+++ b/activesupport/lib/active_support/descendants_tracker.rb
@@ -38,12 +38,13 @@ module ActiveSupport
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) }
+
+ 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) }
+ end
end
- end
end
def inherited(base)
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 88897f811e..314c926ac0 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -373,7 +373,6 @@ module ActiveSupport
return "0 seconds" if parts.empty?
parts.
- reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
sort_by { |unit, _ | PARTS.index(unit) }.
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
to_sentence(locale: ::I18n.default_locale)
diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb
index 1847eeaa86..d3233e6111 100644
--- a/activesupport/lib/active_support/duration/iso8601_parser.rb
+++ b/activesupport/lib/active_support/duration/iso8601_parser.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "strscan"
-require "active_support/core_ext/regexp"
module ActiveSupport
class Duration
@@ -14,8 +13,8 @@ module ActiveSupport
class ParsingError < ::ArgumentError; end
PERIOD_OR_COMMA = /\.|,/
- PERIOD = ".".freeze
- COMMA = ",".freeze
+ PERIOD = "."
+ COMMA = ","
SIGN_MARKER = /\A\-|\+|/
DATE_MARKER = /P/
diff --git a/activesupport/lib/active_support/duration/iso8601_serializer.rb b/activesupport/lib/active_support/duration/iso8601_serializer.rb
index 84ae29c1ec..1125454919 100644
--- a/activesupport/lib/active_support/duration/iso8601_serializer.rb
+++ b/activesupport/lib/active_support/duration/iso8601_serializer.rb
@@ -14,14 +14,14 @@ module ActiveSupport
# Builds and returns output string.
def serialize
parts, sign = normalize
- return "PT0S".freeze if parts.empty?
+ return "PT0S" if parts.empty?
- output = "P".dup
+ output = +"P"
output << "#{parts[:years]}Y" if parts.key?(:years)
output << "#{parts[:months]}M" if parts.key?(:months)
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
output << "#{parts[:days]}D" if parts.key?(:days)
- time = "".dup
+ time = +""
time << "#{parts[:hours]}H" if parts.key?(:hours)
time << "#{parts[:minutes]}M" if parts.key?(:minutes)
if parts.key?(:seconds)
diff --git a/activesupport/lib/active_support/encrypted_configuration.rb b/activesupport/lib/active_support/encrypted_configuration.rb
index 3c6da10548..cc1d026737 100644
--- a/activesupport/lib/active_support/encrypted_configuration.rb
+++ b/activesupport/lib/active_support/encrypted_configuration.rb
@@ -39,7 +39,7 @@ module ActiveSupport
end
def deserialize(config)
- config.present? ? YAML.load(config, content_path) : {}
+ YAML.load(config).presence || {}
end
end
end
diff --git a/activesupport/lib/active_support/execution_wrapper.rb b/activesupport/lib/active_support/execution_wrapper.rb
index f48c586cad..ca810db584 100644
--- a/activesupport/lib/active_support/execution_wrapper.rb
+++ b/activesupport/lib/active_support/execution_wrapper.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support/callbacks"
+require "concurrent/hash"
module ActiveSupport
class ExecutionWrapper
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index e4afc8af93..f1af76019a 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -2,6 +2,7 @@
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/hash/reverse_merge"
+require "active_support/core_ext/hash/except"
module ActiveSupport
# Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
@@ -279,6 +280,8 @@ module ActiveSupport
super(convert_key(key))
end
+ alias_method :without, :except
+
def stringify_keys!; self end
def deep_stringify_keys!; self end
def stringify_keys; dup end
@@ -286,6 +289,7 @@ module ActiveSupport
undef :symbolize_keys!
undef :deep_symbolize_keys!
def symbolize_keys; to_hash.symbolize_keys! end
+ alias_method :to_options, :symbolize_keys
def deep_symbolize_keys; to_hash.deep_symbolize_keys! end
def to_options!; self end
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index ce8bfbfd8c..584930e413 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "active_support"
-require "active_support/file_update_checker"
require "active_support/core_ext/array/wrap"
# :enddoc:
@@ -88,9 +87,21 @@ module I18n
when Hash, Array
Array.wrap(fallbacks)
else # TrueClass
- []
+ [I18n.default_locale]
end
+ if args.empty? || args.first.is_a?(Hash)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Using I18n fallbacks with an empty `defaults` sets the defaults to
+ include the `default_locale`. This behavior will change in Rails 6.1.
+ If you desire the default locale to be included in the defaults, please
+ explicitly configure it with `config.i18n.fallbacks.defaults =
+ [I18n.default_locale]` or `config.i18n.fallbacks = [I18n.default_locale,
+ {...}]`
+ MSG
+ args.unshift I18n.default_locale
+ end
+
I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
end
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index 7e5dff1d6d..2b86d233e5 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -2,7 +2,6 @@
require "concurrent/map"
require "active_support/core_ext/array/prepend_and_append"
-require "active_support/core_ext/regexp"
require "active_support/i18n"
require "active_support/deprecation"
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 339b93b8da..1af9833d46 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "active_support/inflections"
-require "active_support/core_ext/regexp"
module ActiveSupport
# The Inflector transforms words from singular to plural, class names to table
@@ -74,7 +73,7 @@ module ActiveSupport
string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
end
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
- string.gsub!("/".freeze, "::".freeze)
+ string.gsub!("/", "::")
string
end
@@ -91,11 +90,11 @@ module ActiveSupport
# camelize(underscore('SSLError')) # => "SslError"
def underscore(camel_cased_word)
return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
- word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
- word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'.freeze }#{$2.downcase}" }
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
- word.tr!("-".freeze, "_".freeze)
+ word = camel_cased_word.to_s.gsub("::", "/")
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
+ word.tr!("-", "_")
word.downcase!
word
end
@@ -131,11 +130,11 @@ module ActiveSupport
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
- result.sub!(/\A_+/, "".freeze)
+ result.sub!(/\A_+/, "")
unless keep_id_suffix
- result.sub!(/_id\z/, "".freeze)
+ result.sub!(/_id\z/, "")
end
- result.tr!("_".freeze, " ".freeze)
+ result.tr!("_", " ")
result.gsub!(/([a-z\d]*)/i) do |match|
"#{inflections.acronyms[match.downcase] || match.downcase}"
@@ -200,14 +199,14 @@ module ActiveSupport
# classify('calculus') # => "Calculus"
def classify(table_name)
# strip out any leading schema name
- camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
end
# Replaces underscores with dashes in the string.
#
# dasherize('puni_puni') # => "puni-puni"
def dasherize(underscored_word)
- underscored_word.tr("_".freeze, "-".freeze)
+ underscored_word.tr("_", "-")
end
# Removes the module part from the expression in the string.
@@ -270,7 +269,7 @@ module ActiveSupport
# NameError is raised when the name is not in CamelCase or the constant is
# unknown.
def constantize(camel_cased_word)
- names = camel_cased_word.split("::".freeze)
+ names = camel_cased_word.split("::")
# Trigger a built-in NameError exception including the ill-formed constant in the message.
Object.const_get(camel_cased_word) if names.empty?
@@ -365,7 +364,7 @@ module ActiveSupport
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
# const_regexp("::") # => "::"
def const_regexp(camel_cased_word)
- parts = camel_cased_word.split("::".freeze)
+ parts = camel_cased_word.split("::")
return Regexp.escape(camel_cased_word) if parts.blank?
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index 6f2ca4999c..0d2a17970f 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -58,13 +58,13 @@ module ActiveSupport
# I18n.locale = :de
# transliterate('Jürgen')
# # => "Juergen"
- def transliterate(string, replacement = "?".freeze)
+ def transliterate(string, replacement = "?")
raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
I18n.transliterate(
- ActiveSupport::Multibyte::Unicode.normalize(
- ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
- replacement: replacement)
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
+ replacement: replacement
+ )
end
# Replaces special characters in a string so that it may be used as part of
@@ -97,7 +97,7 @@ module ActiveSupport
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
unless separator.nil? || separator.empty?
- if separator == "-".freeze
+ if separator == "-"
re_duplicate_separator = /-{2,}/
re_leading_trailing_separator = /^-|-$/i
else
@@ -108,7 +108,7 @@ module ActiveSupport
# No more than one of the separator in a row.
parameterized_string.gsub!(re_duplicate_separator, separator)
# Remove leading/trailing separator.
- parameterized_string.gsub!(re_leading_trailing_separator, "".freeze)
+ parameterized_string.gsub!(re_leading_trailing_separator, "")
end
parameterized_string.downcase! unless preserve_case
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index 8c0e016dc5..402a3fbe60 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -45,32 +45,32 @@ module ActiveSupport
private
- def convert_dates_from(data)
- case data
- when nil
- nil
- when DATE_REGEX
- begin
- Date.parse(data)
- rescue ArgumentError
+ def convert_dates_from(data)
+ case data
+ when nil
+ nil
+ when DATE_REGEX
+ begin
+ Date.parse(data)
+ rescue ArgumentError
+ data
+ end
+ when DATETIME_REGEX
+ begin
+ Time.zone.parse(data)
+ rescue ArgumentError
+ data
+ end
+ when Array
+ data.map! { |d| convert_dates_from(d) }
+ when Hash
+ data.each do |key, value|
+ data[key] = convert_dates_from(value)
+ end
+ else
data
end
- when DATETIME_REGEX
- begin
- Time.zone.parse(data)
- rescue ArgumentError
- data
- end
- when Array
- data.map! { |d| convert_dates_from(d) }
- when Hash
- data.each do |key, value|
- data[key] = convert_dates_from(value)
- end
- else
- data
end
- end
end
end
end
diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb
index 78f7d7ca8d..00edcdd05a 100644
--- a/activesupport/lib/active_support/key_generator.rb
+++ b/activesupport/lib/active_support/key_generator.rb
@@ -59,7 +59,7 @@ module ActiveSupport
if secret.blank?
raise ArgumentError, "A secret is required to generate an integrity hash " \
"for cookie session data. Set a secret_key_base of at least " \
- "#{SECRET_MIN_LENGTH} characters in via `bin/rails credentials:edit`."
+ "#{SECRET_MIN_LENGTH} characters by running `rails credentials:edit`."
end
if secret.length < SECRET_MIN_LENGTH
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index dc8080c469..a6b096a973 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -68,7 +68,11 @@ module ActiveSupport
if options[:yield]
block.call(base)
else
- base.instance_eval(&block)
+ if base.is_a?(Module)
+ base.class_eval(&block)
+ else
+ base.instance_eval(&block)
+ end
end
end
end
diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb
index 8152a182b4..b8555c887b 100644
--- a/activesupport/lib/active_support/logger.rb
+++ b/activesupport/lib/active_support/logger.rb
@@ -6,7 +6,6 @@ require "logger"
module ActiveSupport
class Logger < ::Logger
- include ActiveSupport::LoggerThreadSafeLevel
include LoggerSilence
# Returns true if the logger destination matches one of the sources
@@ -81,20 +80,6 @@ module ActiveSupport
def initialize(*args)
super
@formatter = SimpleFormatter.new
- after_initialize if respond_to? :after_initialize
- end
-
- def add(severity, message = nil, progname = nil, &block)
- return true if @logdev.nil? || (severity || UNKNOWN) < level
- super
- end
-
- Logger::Severity.constants.each do |severity|
- class_eval(<<-EOT, __FILE__, __LINE__ + 1)
- def #{severity.downcase}? # def debug?
- Logger::#{severity} >= level # DEBUG >= level
- end # end
- EOT
end
# Simple formatter which only displays the message.
diff --git a/activesupport/lib/active_support/logger_silence.rb b/activesupport/lib/active_support/logger_silence.rb
index 89f32b6782..b2444c1e34 100644
--- a/activesupport/lib/active_support/logger_silence.rb
+++ b/activesupport/lib/active_support/logger_silence.rb
@@ -2,28 +2,44 @@
require "active_support/concern"
require "active_support/core_ext/module/attribute_accessors"
-require "concurrent"
+require "active_support/logger_thread_safe_level"
module LoggerSilence
extend ActiveSupport::Concern
included do
- cattr_accessor :silencer, default: true
+ ActiveSupport::Deprecation.warn(
+ "Including LoggerSilence is deprecated and will be removed in Rails 6.1. " \
+ "Please use `ActiveSupport::LoggerSilence` instead"
+ )
+
+ include ActiveSupport::LoggerSilence
end
+end
+
+module ActiveSupport
+ module LoggerSilence
+ extend ActiveSupport::Concern
+
+ included do
+ cattr_accessor :silencer, default: true
+ include ActiveSupport::LoggerThreadSafeLevel
+ end
- # Silences the logger for the duration of the block.
- def silence(temporary_level = Logger::ERROR)
- if silencer
- begin
- old_local_level = local_level
- self.local_level = temporary_level
+ # Silences the logger for the duration of the block.
+ def silence(temporary_level = Logger::ERROR)
+ if silencer
+ begin
+ old_local_level = local_level
+ self.local_level = temporary_level
+ yield self
+ ensure
+ self.local_level = old_local_level
+ end
+ else
yield self
- ensure
- self.local_level = old_local_level
end
- else
- yield self
end
end
end
diff --git a/activesupport/lib/active_support/logger_thread_safe_level.rb b/activesupport/lib/active_support/logger_thread_safe_level.rb
index ba32813d3d..f16c90cfc6 100644
--- a/activesupport/lib/active_support/logger_thread_safe_level.rb
+++ b/activesupport/lib/active_support/logger_thread_safe_level.rb
@@ -1,13 +1,30 @@
# frozen_string_literal: true
require "active_support/concern"
+require "active_support/core_ext/module/attribute_accessors"
+require "concurrent"
module ActiveSupport
module LoggerThreadSafeLevel # :nodoc:
extend ActiveSupport::Concern
+ included do
+ cattr_accessor :local_levels, default: Concurrent::Map.new(initial_capacity: 2), instance_accessor: false
+ end
+
+ Logger::Severity.constants.each do |severity|
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
+ def #{severity.downcase}? # def debug?
+ Logger::#{severity} >= level # DEBUG >= level
+ end # end
+ EOT
+ end
+
def after_initialize
- @local_levels = Concurrent::Map.new(initial_capacity: 2)
+ ActiveSupport::Deprecation.warn(
+ "Logger don't need to call #after_initialize directly anymore. It will be deprecated without replacement in " \
+ "Rails 6.1."
+ )
end
def local_log_id
@@ -15,19 +32,24 @@ module ActiveSupport
end
def local_level
- @local_levels[local_log_id]
+ self.class.local_levels[local_log_id]
end
def local_level=(level)
if level
- @local_levels[local_log_id] = level
+ self.class.local_levels[local_log_id] = level
else
- @local_levels.delete(local_log_id)
+ self.class.local_levels.delete(local_log_id)
end
end
def level
local_level || super
end
+
+ def add(severity, message = nil, progname = nil, &block) # :nodoc:
+ return true if @logdev.nil? || (severity || UNKNOWN) < level
+ super
+ end
end
end
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 8b73270894..6f7302e732 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -182,7 +182,7 @@ module ActiveSupport
def _decrypt(encrypted_message, purpose)
cipher = new_cipher
- encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }
+ encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) }
# Currently the OpenSSL bindings do not raise an error if auth_tag is
# truncated, which would allow an attacker to easily forge it. See
@@ -210,9 +210,7 @@ module ActiveSupport
OpenSSL::Cipher.new(@cipher)
end
- def verifier
- @verifier
- end
+ attr_reader :verifier
def aead_mode?
@aead_mode ||= new_cipher.authenticated?
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 83c39c0a86..64c557bec6 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -122,7 +122,7 @@ module ActiveSupport
def valid_message?(signed_message)
return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
- data, digest = signed_message.split("--".freeze)
+ data, digest = signed_message.split("--")
data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
end
@@ -150,7 +150,7 @@ module ActiveSupport
def verified(signed_message, purpose: nil, **)
if valid_message?(signed_message)
begin
- data = signed_message.split("--".freeze)[0]
+ data = signed_message.split("--")[0]
message = Messages::Metadata.verify(decode(data), purpose)
@serializer.load(message) if message
rescue ArgumentError => argument_error
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 8152b8fd22..a1e23aeaca 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -4,7 +4,6 @@ require "active_support/json"
require "active_support/core_ext/string/access"
require "active_support/core_ext/string/behavior"
require "active_support/core_ext/module/delegation"
-require "active_support/core_ext/regexp"
module ActiveSupport #:nodoc:
module Multibyte #:nodoc:
@@ -18,7 +17,7 @@ module ActiveSupport #:nodoc:
# through the +mb_chars+ method. Methods which would normally return a
# String object now return a Chars object so methods can be chained.
#
- # 'The Perfect String '.mb_chars.downcase.strip.normalize
+ # 'The Perfect String '.mb_chars.downcase.strip
# # => #<ActiveSupport::Multibyte::Chars:0x007fdc434ccc10 @wrapped_string="the perfect string">
#
# Chars objects are perfectly interchangeable with String objects as long as
@@ -77,6 +76,11 @@ module ActiveSupport #:nodoc:
# Returns +true+ when the proxy class can handle the string. Returns
# +false+ otherwise.
def self.consumes?(string)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Chars.consumes? is deprecated and will be
+ removed from Rails 6.1. Use string.is_utf8? instead.
+ MSG
+
string.encoding == Encoding::UTF_8
end
@@ -109,7 +113,7 @@ module ActiveSupport #:nodoc:
#
# 'Café'.mb_chars.reverse.to_s # => 'éfaC'
def reverse
- chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack("U*"))
+ chars(@wrapped_string.scan(/\X/).reverse.join)
end
# Limits the byte size of the string to a number of bytes without breaking
@@ -118,35 +122,7 @@ module ActiveSupport #:nodoc:
#
# 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
def limit(limit)
- slice(0...translate_offset(limit))
- end
-
- # Converts characters in the string to uppercase.
- #
- # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
- def upcase
- chars Unicode.upcase(@wrapped_string)
- end
-
- # Converts characters in the string to lowercase.
- #
- # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
- def downcase
- chars Unicode.downcase(@wrapped_string)
- end
-
- # Converts characters in the string to the opposite case.
- #
- # 'El Cañón'.mb_chars.swapcase.to_s # => "eL cAÑÓN"
- def swapcase
- chars Unicode.swapcase(@wrapped_string)
- end
-
- # Converts the first character to uppercase and the remainder to lowercase.
- #
- # 'über'.mb_chars.capitalize.to_s # => "Über"
- def capitalize
- (slice(0) || chars("")).upcase + (slice(1..-1) || chars("")).downcase
+ truncate_bytes(limit, omission: nil)
end
# Capitalizes the first letter of every word, when possible.
@@ -154,7 +130,7 @@ module ActiveSupport #:nodoc:
# "ÉL QUE SE ENTERÓ".mb_chars.titleize.to_s # => "Él Que Se Enteró"
# "日本語".mb_chars.titleize.to_s # => "日本語"
def titleize
- chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1) })
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { $1.upcase })
end
alias_method :titlecase, :titleize
@@ -166,7 +142,24 @@ module ActiveSupport #:nodoc:
# <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
# ActiveSupport::Multibyte::Unicode.default_normalization_form
def normalize(form = nil)
- chars(Unicode.normalize(@wrapped_string, form))
+ form ||= Unicode.default_normalization_form
+
+ # See https://www.unicode.org/reports/tr15, Table 1
+ if alias_form = Unicode::NORMALIZATION_FORM_ALIASES[form]
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
+ removed from Rails 6.1. Use #unicode_normalize(:#{alias_form}) instead.
+ MSG
+
+ send(:unicode_normalize, alias_form)
+ else
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
+ removed from Rails 6.1. Use #unicode_normalize instead.
+ MSG
+
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
+ end
end
# Performs canonical decomposition on all the characters.
@@ -190,7 +183,7 @@ module ActiveSupport #:nodoc:
# 'क्षि'.mb_chars.length # => 4
# 'क्षि'.mb_chars.grapheme_length # => 3
def grapheme_length
- Unicode.unpack_graphemes(@wrapped_string).length
+ @wrapped_string.scan(/\X/).length
end
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
@@ -206,7 +199,7 @@ module ActiveSupport #:nodoc:
to_s.as_json(options)
end
- %w(capitalize downcase reverse tidy_bytes upcase).each do |method|
+ %w(reverse tidy_bytes).each do |method|
define_method("#{method}!") do |*args|
@wrapped_string = send(method, *args).to_s
self
@@ -215,18 +208,6 @@ module ActiveSupport #:nodoc:
private
- def translate_offset(byte_offset)
- return nil if byte_offset.nil?
- return 0 if @wrapped_string == ""
-
- begin
- @wrapped_string.byteslice(0...byte_offset).unpack("U*").length
- rescue ArgumentError
- byte_offset -= 1
- retry
- end
- end
-
def chars(string)
self.class.new(string)
end
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 4f0e1165ef..ce8ecece69 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -6,10 +6,17 @@ module ActiveSupport
extend self
# A list of all available normalization forms.
- # See http://www.unicode.org/reports/tr15/tr15-29.html for more
+ # See https://www.unicode.org/reports/tr15/tr15-29.html for more
# information about normalization.
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
+ NORMALIZATION_FORM_ALIASES = { # :nodoc:
+ c: :nfc,
+ d: :nfd,
+ kc: :nfkc,
+ kd: :nfkd
+ }
+
# The Unicode version that is supported by the implementation
UNICODE_VERSION = RbConfig::CONFIG["UNICODE_VERSION"]
@@ -27,6 +34,11 @@ module ActiveSupport
# Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
# Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
def unpack_graphemes(string)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#unpack_graphemes is deprecated and will be
+ removed from Rails 6.1. Use string.scan(/\X/).map(&:codepoints) instead.
+ MSG
+
string.scan(/\X/).map(&:codepoints)
end
@@ -34,6 +46,11 @@ module ActiveSupport
#
# Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
def pack_graphemes(unpacked)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#pack_graphemes is deprecated and will be
+ removed from Rails 6.1. Use array.flatten.pack("U*") instead.
+ MSG
+
unpacked.flatten.pack("U*")
end
@@ -100,31 +117,34 @@ module ActiveSupport
# Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
def normalize(string, form = nil)
form ||= @default_normalization_form
- # See http://www.unicode.org/reports/tr15, Table 1
- case form
- when :d
- string.unicode_normalize(:nfd)
- when :c
- string.unicode_normalize(:nfc)
- when :kd
- string.unicode_normalize(:nfkd)
- when :kc
- string.unicode_normalize(:nfkc)
+
+ # See https://www.unicode.org/reports/tr15, Table 1
+ if alias_form = NORMALIZATION_FORM_ALIASES[form]
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#normalize is deprecated and will be
+ removed from Rails 6.1. Use String#unicode_normalize(:#{alias_form}) instead.
+ MSG
+
+ string.unicode_normalize(alias_form)
else
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#normalize is deprecated and will be
+ removed from Rails 6.1. Use String#unicode_normalize instead.
+ MSG
+
raise ArgumentError, "#{form} is not a valid normalization variant", caller
end
end
- def downcase(string)
- string.downcase
- end
+ %w(downcase upcase swapcase).each do |method|
+ define_method(method) do |string|
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode##{method} is deprecated and
+ will be removed from Rails 6.1. Use String methods directly.
+ MSG
- def upcase(string)
- string.upcase
- end
-
- def swapcase(string)
- string.swapcase
+ string.send(method)
+ end
end
private
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 6207de8094..01cc363e2b 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -34,7 +34,7 @@ module ActiveSupport
# name # => String, name of the event (such as 'render' from above)
# start # => Time, when the instrumented block started execution
# finish # => Time, when the instrumented block ended execution
- # id # => String, unique ID for this notification
+ # id # => String, unique ID for the instrumenter that fired the event
# payload # => Hash, the payload
# end
#
@@ -59,7 +59,7 @@ module ActiveSupport
# event.payload # => { extra: :information }
#
# The block in the <tt>subscribe</tt> call gets the name of the event, start
- # timestamp, end timestamp, a string with a unique identifier for that event
+ # timestamp, end timestamp, a string with a unique identifier for that event's instrumenter
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
# that order.
#
@@ -171,6 +171,24 @@ module ActiveSupport
end
end
+ # Subscribe to a given event name with the passed +block+.
+ #
+ # You can subscribe to events by passing a String to match exact event
+ # names, or by passing a Regexp to match all events that match a pattern.
+ #
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
+ # ...
+ # end
+ #
+ # The +block+ will receive five parameters with information about the event:
+ #
+ # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
+ # name # => String, name of the event (such as 'render' from above)
+ # start # => Time, when the instrumented block started execution
+ # finish # => Time, when the instrumented block ended execution
+ # id # => String, unique ID for the instrumenter that fired the event
+ # payload # => Hash, the payload
+ # end
def subscribe(*args, &block)
notifier.subscribe(*args, &block)
end
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 25aab175b4..4e4ca70942 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -70,12 +70,29 @@ module ActiveSupport
module Subscribers # :nodoc:
def self.new(pattern, listener)
+ subscriber_class = Timed
+
if listener.respond_to?(:start) && listener.respond_to?(:finish)
- subscriber = Evented.new pattern, listener
+ subscriber_class = Evented
else
- subscriber = Timed.new pattern, listener
+ # Doing all this to detect a block like `proc { |x| }` vs
+ # `proc { |*x| }` or `proc { |**x| }`
+ if listener.respond_to?(:parameters)
+ params = listener.parameters
+ if params.length == 1 && params.first.first == :opt
+ subscriber_class = EventObject
+ end
+ end
end
+ wrap_all pattern, subscriber_class.new(pattern, listener)
+ end
+
+ def self.event_object_subscriber(pattern, block)
+ wrap_all pattern, EventObject.new(pattern, block)
+ end
+
+ def self.wrap_all(pattern, subscriber)
unless pattern
AllMessages.new(subscriber)
else
@@ -130,6 +147,27 @@ module ActiveSupport
end
end
+ class EventObject < Evented
+ def start(name, id, payload)
+ stack = Thread.current[:_event_stack] ||= []
+ event = build_event name, id, payload
+ event.start!
+ stack.push event
+ end
+
+ def finish(name, id, payload)
+ stack = Thread.current[:_event_stack]
+ event = stack.pop
+ event.finish!
+ @delegate.call event
+ end
+
+ private
+ def build_event(name, id, payload)
+ ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
+ end
+ end
+
class AllMessages # :nodoc:
def initialize(delegate)
@delegate = delegate
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index e99f5ee688..125c06f37a 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -52,8 +52,13 @@ module ActiveSupport
end
class Event
- attr_reader :name, :time, :transaction_id, :payload, :children
- attr_accessor :end
+ attr_reader :name, :time, :end, :transaction_id, :payload, :children
+
+ def self.clock_gettime_supported? # :nodoc:
+ defined?(Process::CLOCK_PROCESS_CPUTIME_ID) &&
+ !Gem.win_platform?
+ end
+ private_class_method :clock_gettime_supported?
def initialize(name, start, ending, transaction_id, payload)
@name = name
@@ -63,6 +68,47 @@ module ActiveSupport
@end = ending
@children = []
@duration = nil
+ @cpu_time_start = nil
+ @cpu_time_finish = nil
+ @allocation_count_start = 0
+ @allocation_count_finish = 0
+ end
+
+ # Record information at the time this event starts
+ def start!
+ @time = now
+ @cpu_time_start = now_cpu
+ @allocation_count_start = now_allocations
+ end
+
+ # Record information at the time this event finishes
+ def finish!
+ @cpu_time_finish = now_cpu
+ @end = now
+ @allocation_count_finish = now_allocations
+ end
+
+ def end=(ending)
+ ActiveSupport::Deprecation.deprecation_warning(:end=, :finish!)
+ @end = ending
+ end
+
+ # Returns the CPU time (in milliseconds) passed since the call to
+ # +start!+ and the call to +finish!+
+ def cpu_time
+ (@cpu_time_finish - @cpu_time_start) * 1000
+ end
+
+ # Returns the idle time time (in milliseconds) passed since the call to
+ # +start!+ and the call to +finish!+
+ def idle_time
+ duration - cpu_time
+ end
+
+ # Returns the number of allocations made since the call to +start!+ and
+ # the call to +finish!+
+ def allocations
+ @allocation_count_finish - @allocation_count_start
end
# Returns the difference in milliseconds between when the execution of the
@@ -88,6 +134,31 @@ module ActiveSupport
def parent_of?(event)
@children.include? event
end
+
+ private
+ def now
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end
+
+ if clock_gettime_supported?
+ def now_cpu
+ Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
+ end
+ else
+ def now_cpu
+ 0
+ end
+ end
+
+ if defined?(JRUBY_VERSION)
+ def now_allocations
+ 0
+ end
+ else
+ def now_allocations
+ GC.stat :total_allocated_objects
+ end
+ end
end
end
end
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index 8fd6e932f1..d19a2f64d4 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/dependencies/autoload"
+
module ActiveSupport
module NumberHelper
extend ActiveSupport::Autoload
@@ -85,6 +87,9 @@ module ActiveSupport
# number given by <tt>:format</tt>). Accepts the same fields
# than <tt>:format</tt>, except <tt>%n</tt> is here the
# absolute value of the number.
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +false+).
#
# ==== Examples
#
@@ -100,6 +105,8 @@ module ActiveSupport
# # => "&pound;1234567890,50"
# number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
# # => "1234567890,50 &pound;"
+ # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
+ # # => "$1,234,567,890.5"
def number_to_currency(number, options = {})
NumberToCurrencyConverter.convert(number, options)
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
index a25e22cbd3..0e8ae82dd5 100644
--- a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToCurrencyConverter < NumberConverter # :nodoc:
@@ -15,7 +17,7 @@ module ActiveSupport
end
rounded_number = NumberToRoundedConverter.convert(number, options)
- format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, options[:unit])
+ format.gsub("%n", rounded_number).gsub("%u", options[:unit])
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
index d5b5706705..467a580a2e 100644
--- a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToDelimitedConverter < NumberConverter #:nodoc:
@@ -14,7 +16,7 @@ module ActiveSupport
private
def parts
- left, right = number.to_s.split(".".freeze)
+ left, right = number.to_s.split(".")
left.gsub!(delimiter_pattern) do |digit_to_delimit|
"#{digit_to_delimit}#{options[:delimiter]}"
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_human_converter.rb b/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
index 03eb6671ec..494408fc01 100644
--- a/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToHumanConverter < NumberConverter # :nodoc:
@@ -25,7 +27,7 @@ module ActiveSupport
rounded_number = NumberToRoundedConverter.convert(number, options)
unit = determine_unit(units, exponent)
- format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, unit).strip
+ format.gsub("%n", rounded_number).gsub("%u", unit).strip
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
index 842f2fc8df..91262fa656 100644
--- a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToHumanSizeConverter < NumberConverter #:nodoc:
@@ -22,7 +24,7 @@ module ActiveSupport
human_size = number / (base**exponent)
number_to_format = NumberToRoundedConverter.convert(human_size, options)
end
- conversion_format.gsub("%n".freeze, number_to_format).gsub("%u".freeze, unit)
+ conversion_format.gsub("%n", number_to_format).gsub("%u", unit)
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb b/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
index 4dcdad2e2c..0c2e190f8a 100644
--- a/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToPercentageConverter < NumberConverter # :nodoc:
@@ -7,7 +9,7 @@ module ActiveSupport
def convert
rounded_number = NumberToRoundedConverter.convert(number, options)
- options[:format].gsub("%n".freeze, rounded_number)
+ options[:format].gsub("%n", rounded_number)
end
end
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb b/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb
index 96410f4995..d5e72981b4 100644
--- a/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToPhoneConverter < NumberConverter #:nodoc:
diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
index eb528a0583..6ceb9a572e 100644
--- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToRoundedConverter < NumberConverter # :nodoc:
@@ -20,9 +22,9 @@ module ActiveSupport
formatted_string =
if BigDecimal === rounded_number && rounded_number.finite?
s = rounded_number.to_s("F")
- s << "0".freeze * precision
- a, b = s.split(".".freeze, 2)
- a << ".".freeze
+ s << "0" * precision
+ a, b = s.split(".", 2)
+ a << "."
a << b[0, precision]
else
"%00.#{precision}f" % rounded_number
diff --git a/activesupport/lib/active_support/parameter_filter.rb b/activesupport/lib/active_support/parameter_filter.rb
new file mode 100644
index 0000000000..1389d82523
--- /dev/null
+++ b/activesupport/lib/active_support/parameter_filter.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/duplicable"
+require "active_support/core_ext/array/extract"
+
+module ActiveSupport
+ # +ParameterFilter+ allows you to specify keys for sensitive data from
+ # hash-like object and replace corresponding value. Filtering only certain
+ # sub-keys from a hash is possible by using the dot notation:
+ # 'credit_card.number'. If a proc is given, each key and value of a hash and
+ # all sub-hashes are passed to it, where the value or the key can be replaced
+ # using String#replace or similar methods.
+ #
+ # ActiveSupport::ParameterFilter.new([:password])
+ # => replaces the value to all keys matching /password/i with "[FILTERED]"
+ #
+ # ActiveSupport::ParameterFilter.new([:foo, "bar"])
+ # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
+ #
+ # ActiveSupport::ParameterFilter.new(["credit_card.code"])
+ # => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
+ # change { file: { code: "xxxx"} }
+ #
+ # ActiveSupport::ParameterFilter.new([-> (k, v) do
+ # v.reverse! if k =~ /secret/i
+ # end])
+ # => reverses the value to all keys matching /secret/i
+ class ParameterFilter
+ FILTERED = "[FILTERED]" # :nodoc:
+
+ # Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
+ # Other types of filters are treated as +String+ using +to_s+.
+ # For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
+ #
+ # ==== Options
+ #
+ # * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
+ def initialize(filters = [], mask: FILTERED)
+ @filters = filters
+ @mask = mask
+ end
+
+ # Mask value of +params+ if key matches one of filters.
+ def filter(params)
+ compiled_filter.call(params)
+ end
+
+ # Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
+ def filter_param(key, value)
+ @filters.empty? ? value : compiled_filter.value_for_key(key, value)
+ end
+
+ private
+
+ def compiled_filter
+ @compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
+ end
+
+ class CompiledFilter # :nodoc:
+ def self.compile(filters, mask:)
+ return lambda { |params| params.dup } if filters.empty?
+
+ strings, regexps, blocks = [], [], []
+
+ filters.each do |item|
+ case item
+ when Proc
+ blocks << item
+ when Regexp
+ regexps << item
+ else
+ strings << Regexp.escape(item.to_s)
+ end
+ end
+
+ deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.") }
+ deep_strings = strings.extract! { |s| s.include?("\\.") }
+
+ regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
+ deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
+
+ new regexps, deep_regexps, blocks, mask: mask
+ end
+
+ attr_reader :regexps, :deep_regexps, :blocks
+
+ def initialize(regexps, deep_regexps, blocks, mask:)
+ @regexps = regexps
+ @deep_regexps = deep_regexps.any? ? deep_regexps : nil
+ @blocks = blocks
+ @mask = mask
+ end
+
+ def call(params, parents = [], original_params = params)
+ filtered_params = params.class.new
+
+ params.each do |key, value|
+ filtered_params[key] = value_for_key(key, value, parents, original_params)
+ end
+
+ filtered_params
+ end
+
+ def value_for_key(key, value, parents = [], original_params = nil)
+ parents.push(key) if deep_regexps
+ if regexps.any? { |r| r.match?(key) }
+ value = @mask
+ elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
+ value = @mask
+ elsif value.is_a?(Hash)
+ value = call(value, parents, original_params)
+ elsif value.is_a?(Array)
+ value = value.map { |v| v.is_a?(Hash) ? call(v, parents, original_params) : v }
+ elsif blocks.any?
+ key = key.dup if key.duplicable?
+ value = value.dup if value.duplicable?
+ blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
+ end
+ parents.pop if deep_regexps
+ value
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/reloader.rb b/activesupport/lib/active_support/reloader.rb
index b26d9c3665..fea18e9712 100644
--- a/activesupport/lib/active_support/reloader.rb
+++ b/activesupport/lib/active_support/reloader.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support/execution_wrapper"
+require "active_support/executor"
module ActiveSupport
#--
diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb
index 8ad39f7a05..f3e902f9dd 100644
--- a/activesupport/lib/active_support/subscriber.rb
+++ b/activesupport/lib/active_support/subscriber.rb
@@ -79,25 +79,24 @@ module ActiveSupport
end
def start(name, id, payload)
- e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
+ event = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload)
+ event.start!
parent = event_stack.last
- parent << e if parent
+ parent << event if parent
- event_stack.push e
+ event_stack.push event
end
def finish(name, id, payload)
- finished = Time.now
- event = event_stack.pop
- event.end = finished
+ event = event_stack.pop
+ event.finish!
event.payload.merge!(payload)
- method = name.split(".".freeze).first
+ method = name.split(".").first
send(method, event)
end
private
-
def event_stack
SubscriberQueueRegistry.instance.get_queue(@queue_key)
end
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index b069ac94d4..d8a86d997e 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -46,7 +46,7 @@ module ActiveSupport
def current_tags
# We use our object ID here to avoid conflicting with other instances
- thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}".freeze
+ thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
Thread.current[thread_key] ||= []
end
@@ -61,8 +61,15 @@ module ActiveSupport
end
def self.new(logger)
- # Ensure we set a default formatter so we aren't extending nil!
- logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
+ logger = logger.dup
+
+ if logger.formatter
+ logger.formatter = logger.formatter.dup
+ else
+ # Ensure we set a default formatter so we aren't extending nil!
+ logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
+ end
+
logger.formatter.extend Formatter
logger.extend(self)
end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index f17743b6db..ef12c6b9b0 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -65,8 +65,8 @@ module ActiveSupport
#
# parallelize(workers: 2, with: :threads)
#
- # The threaded parallelization uses Minitest's parallel executor directly.
- # The processes parallelization uses a Ruby Drb server.
+ # The threaded parallelization uses minitest's parallel executor directly.
+ # The processes parallelization uses a Ruby DRb server.
def parallelize(workers: 2, with: :processes)
workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index a891ff616d..b27ac7ce99 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -113,11 +113,23 @@ module ActiveSupport
# post :create, params: { article: invalid_attributes }
# end
#
+ # A lambda can be passed in and evaluated.
+ #
+ # assert_no_difference -> { Article.count } do
+ # post :create, params: { article: invalid_attributes }
+ # end
+ #
# An error message can be specified.
#
# assert_no_difference 'Article.count', 'An Article should not be created' do
# post :create, params: { article: invalid_attributes }
# end
+ #
+ # An array of expressions can also be passed in and evaluated.
+ #
+ # assert_no_difference [ 'Article.count', -> { Post.count } ] do
+ # post :create, params: { article: invalid_attributes }
+ # end
def assert_no_difference(expression, message = nil, &block)
assert_difference expression, 0, message, &block
end
@@ -176,7 +188,9 @@ module ActiveSupport
assert before != after, error
unless to == UNTRACKED
- error = "#{expression.inspect} didn't change to #{to}"
+ error = "#{expression.inspect} didn't change to as expected\n"
+ error = "#{error}Expected: #{to.inspect}\n"
+ error = "#{error} Actual: #{after.inspect}"
error = "#{message}.\n#{error}" if message
assert to === after, error
end
diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb
index f655435729..18d63d2780 100644
--- a/activesupport/lib/active_support/testing/deprecation.rb
+++ b/activesupport/lib/active_support/testing/deprecation.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "active_support/deprecation"
-require "active_support/core_ext/regexp"
module ActiveSupport
module Testing
diff --git a/activesupport/lib/active_support/testing/file_fixtures.rb b/activesupport/lib/active_support/testing/file_fixtures.rb
index ad923d1aab..4eb7a88576 100644
--- a/activesupport/lib/active_support/testing/file_fixtures.rb
+++ b/activesupport/lib/active_support/testing/file_fixtures.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/concern"
+
module ActiveSupport
module Testing
# Adds simple access to sample files called file fixtures.
diff --git a/activesupport/lib/active_support/testing/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb
index c6358002ea..eba41aa907 100644
--- a/activesupport/lib/active_support/testing/method_call_assertions.rb
+++ b/activesupport/lib/active_support/testing/method_call_assertions.rb
@@ -17,7 +17,7 @@ module ActiveSupport
assert_equal times, times_called, error
end
- def assert_called_with(object, method_name, args = [], returns: nil)
+ def assert_called_with(object, method_name, args, returns: nil)
mock = Minitest::Mock.new
if args.all? { |arg| arg.is_a?(Array) }
@@ -35,6 +35,35 @@ module ActiveSupport
assert_called(object, method_name, message, times: 0, &block)
end
+ # TODO: No need to resort to #send once support for Ruby 2.4 is
+ # dropped.
+ def assert_called_on_instance_of(klass, method_name, message = nil, times: 1, returns: nil)
+ times_called = 0
+ klass.send(:define_method, "stubbed_#{method_name}") do |*|
+ times_called += 1
+
+ returns
+ end
+
+ klass.send(:alias_method, "original_#{method_name}", method_name)
+ klass.send(:alias_method, method_name, "stubbed_#{method_name}")
+
+ yield
+
+ error = "Expected #{method_name} to be called #{times} times, but was called #{times_called} times"
+ error = "#{message}.\n#{error}" if message
+
+ assert_equal times, times_called, error
+ ensure
+ klass.send(:alias_method, method_name, "original_#{method_name}")
+ klass.send(:undef_method, "original_#{method_name}")
+ klass.send(:undef_method, "stubbed_#{method_name}")
+ end
+
+ def assert_not_called_on_instance_of(klass, method_name, message = nil, &block)
+ assert_called_on_instance_of(klass, method_name, message, times: 0, &block)
+ end
+
def stub_any_instance(klass, instance: klass.new)
klass.stub(:new, instance) { yield instance }
end
diff --git a/activesupport/lib/active_support/testing/parallelization.rb b/activesupport/lib/active_support/testing/parallelization.rb
index 59c8486f41..c5d3a88131 100644
--- a/activesupport/lib/active_support/testing/parallelization.rb
+++ b/activesupport/lib/active_support/testing/parallelization.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
require "drb"
-require "drb/unix"
+require "drb/unix" unless Gem.win_platform?
+require "active_support/core_ext/module/attribute_accessors"
module ActiveSupport
module Testing
@@ -14,37 +15,36 @@ module ActiveSupport
end
def record(reporter, result)
+ raise DRb::DRbConnError if result.is_a?(DRb::DRbUnknown)
+
reporter.synchronize do
reporter.record(result)
end
end
def <<(o)
+ o[2] = DRbObject.new(o[2]) if o
@queue << o
end
def pop; @queue.pop; end
end
- @after_fork_hooks = []
+ @@after_fork_hooks = []
def self.after_fork_hook(&blk)
- @after_fork_hooks << blk
+ @@after_fork_hooks << blk
end
- def self.after_fork_hooks
- @after_fork_hooks
- end
+ cattr_reader :after_fork_hooks
- @run_cleanup_hooks = []
+ @@run_cleanup_hooks = []
def self.run_cleanup_hook(&blk)
- @run_cleanup_hooks << blk
+ @@run_cleanup_hooks << blk
end
- def self.run_cleanup_hooks
- @run_cleanup_hooks
- end
+ cattr_reader :run_cleanup_hooks
def initialize(queue_size)
@queue_size = queue_size
@@ -69,22 +69,31 @@ module ActiveSupport
def start
@pool = @queue_size.times.map do |worker|
fork do
- DRb.stop_service
-
- after_fork(worker)
-
- queue = DRbObject.new_with_uri(@url)
-
- while job = queue.pop
- klass = job[0]
- method = job[1]
- reporter = job[2]
- result = Minitest.run_one_method(klass, method)
-
- queue.record(reporter, result)
+ begin
+ DRb.stop_service
+
+ after_fork(worker)
+
+ queue = DRbObject.new_with_uri(@url)
+
+ while job = queue.pop
+ klass = job[0]
+ method = job[1]
+ reporter = job[2]
+ result = Minitest.run_one_method(klass, method)
+
+ begin
+ queue.record(reporter, result)
+ rescue DRb::DRbConnError
+ result.failures.each do |failure|
+ failure.exception = DRb::DRbRemoteError.new(failure.exception)
+ end
+ queue.record(reporter, result)
+ end
+ end
+ ensure
+ run_cleanup(worker)
end
-
- run_cleanup(worker)
end
end
end
diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb
index 801ea2909b..f160e66971 100644
--- a/activesupport/lib/active_support/testing/time_helpers.rb
+++ b/activesupport/lib/active_support/testing/time_helpers.rb
@@ -158,7 +158,7 @@ module ActiveSupport
end
# Returns the current time back to its original state, by removing the stubs added by
- # +travel+ and +travel_to+.
+ # +travel+, +travel_to+, and +freeze_time+.
#
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
# travel_to Time.zone.local(2004, 11, 24, 01, 04, 44)
@@ -168,6 +168,7 @@ module ActiveSupport
def travel_back
simple_stubs.unstub_all!
end
+ alias_method :unfreeze_time, :travel_back
# Calls +travel_to+ with +Time.now+.
#
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 7e71318404..3be5f6f7b5 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -43,8 +43,8 @@ module ActiveSupport
"Time"
end
- PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N".freeze }
- PRECISIONS[0] = "%FT%T".freeze
+ PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N" }
+ PRECISIONS[0] = "%FT%T"
include Comparable, DateAndTime::Compatibility
attr_reader :time_zone
@@ -147,7 +147,7 @@ module ActiveSupport
#
# Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
def xmlschema(fraction_digits = 0)
- "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}"
+ "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z')}"
end
alias_method :iso8601, :xmlschema
alias_method :rfc3339, :xmlschema
@@ -286,8 +286,10 @@ module ActiveSupport
alias_method :since, :+
alias_method :in, :+
- # Returns a new TimeWithZone object that represents the difference between
- # the current object's time and the +other+ time.
+ # Subtracts an interval of time and returns a new TimeWithZone object unless
+ # the other value `acts_like?` time. Then it will return a Float of the difference
+ # between the two times that represents the difference between the current
+ # object's time and the +other+ time.
#
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
# now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
@@ -302,6 +304,12 @@ module ActiveSupport
#
# now - 24.hours # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
# now - 1.day # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
+ #
+ # If both the TimeWithZone object and the other value act like Time, a Float
+ # will be returned.
+ #
+ # Time.zone.now - 1.day.ago # => 86399.999967
+ #
def -(other)
if other.acts_like?(:time)
to_time - other.to_time
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 5f709c5fd9..d9e033e23b 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -182,8 +182,9 @@ module ActiveSupport
"Samoa" => "Pacific/Apia"
}
- UTC_OFFSET_WITH_COLON = "%s%02d:%02d"
- UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "")
+ UTC_OFFSET_WITH_COLON = "%s%02d:%02d" # :nodoc:
+ UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "") # :nodoc:
+ private_constant :UTC_OFFSET_WITH_COLON, :UTC_OFFSET_WITHOUT_COLON
@lazy_zones_map = Concurrent::Map.new
@country_zones = Concurrent::Map.new
@@ -265,7 +266,7 @@ module ActiveSupport
private
def load_country_zones(code)
country = TZInfo::Country.get(code)
- country.zone_identifiers.map do |tz_id|
+ country.zone_identifiers.flat_map do |tz_id|
if MAPPING.value?(tz_id)
MAPPING.inject([]) do |memo, (key, value)|
memo << self[key] if value == tz_id
@@ -274,7 +275,7 @@ module ActiveSupport
else
create(tz_id, nil, TZInfo::Timezone.new(tz_id))
end
- end.flatten(1).sort!
+ end.sort!
end
def zones_map
@@ -354,8 +355,13 @@ module ActiveSupport
# Time.zone = 'Hawaii' # => "Hawaii"
# Time.utc(2000).to_f # => 946684800.0
# Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
- def at(secs)
- Time.at(secs).utc.in_time_zone(self)
+ #
+ # A second argument can be supplied to specify sub-second precision.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.at(946684800, 123456.789).nsec # => 123456789
+ def at(*args)
+ Time.at(*args).utc.in_time_zone(self)
end
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index e42eee07a3..be298bf0a1 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -71,7 +71,7 @@ module ActiveSupport
begin
BigDecimal(number)
rescue ArgumentError
- BigDecimal("0")
+ BigDecimal(number.to_f.to_s)
end
else
BigDecimal(number)
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 7f94a64016..32fe6ade28 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -18,7 +18,7 @@ module ActiveSupport
module XmlMini_JDOM #:nodoc:
extend self
- CONTENT_KEY = "__content__".freeze
+ CONTENT_KEY = "__content__"
NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE
DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE
@@ -169,7 +169,7 @@ module ActiveSupport
# element::
# XML element to be checked.
def empty_content?(element)
- text = "".dup
+ text = +""
child_nodes = element.child_nodes
(0...child_nodes.length).each do |i|
item = child_nodes.item(i)
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index 0b000fea60..c2e999ef6c 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -34,7 +34,7 @@ module LibXML #:nodoc:
end
module Node #:nodoc:
- CONTENT_ROOT = "__content__".freeze
+ CONTENT_ROOT = "__content__"
# Convert XML document to hash.
#
@@ -55,7 +55,7 @@ module LibXML #:nodoc:
if c.element?
c.to_hash(node_hash)
elsif c.text? || c.cdata?
- node_hash[CONTENT_ROOT] ||= "".dup
+ node_hash[CONTENT_ROOT] ||= +""
node_hash[CONTENT_ROOT] << c.content
end
end
diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
index dcf16e6084..ac8acdfc3c 100644
--- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb
+++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
@@ -13,8 +13,8 @@ module ActiveSupport
class HashBuilder
include LibXML::XML::SaxParser::Callbacks
- CONTENT_KEY = "__content__".freeze
- HASH_SIZE_KEY = "__hash_size__".freeze
+ CONTENT_KEY = "__content__"
+ HASH_SIZE_KEY = "__hash_size__"
attr_reader :hash
@@ -23,7 +23,7 @@ module ActiveSupport
end
def on_start_document
- @hash = { CONTENT_KEY => "".dup }
+ @hash = { CONTENT_KEY => +"" }
@hash_stack = [@hash]
end
@@ -33,7 +33,7 @@ module ActiveSupport
end
def on_start_element(name, attrs = {})
- new_hash = { CONTENT_KEY => "".dup }.merge!(attrs)
+ new_hash = { CONTENT_KEY => +"" }.merge!(attrs)
new_hash[HASH_SIZE_KEY] = new_hash.size + 1
case current_hash[name]
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index 5ee6fc8159..f76513f48b 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -38,7 +38,7 @@ module ActiveSupport
end
module Node #:nodoc:
- CONTENT_ROOT = "__content__".freeze
+ CONTENT_ROOT = "__content__"
# Convert XML document to hash.
#
@@ -59,7 +59,7 @@ module ActiveSupport
if c.element?
c.to_hash(node_hash)
elsif c.text? || c.cdata?
- node_hash[CONTENT_ROOT] ||= "".dup
+ node_hash[CONTENT_ROOT] ||= +""
node_hash[CONTENT_ROOT] << c.content
end
end
diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
index b01ed00a14..55cd72e093 100644
--- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
@@ -16,8 +16,8 @@ module ActiveSupport
# Class that will build the hash while the XML document
# is being parsed using SAX events.
class HashBuilder < Nokogiri::XML::SAX::Document
- CONTENT_KEY = "__content__".freeze
- HASH_SIZE_KEY = "__hash_size__".freeze
+ CONTENT_KEY = "__content__"
+ HASH_SIZE_KEY = "__hash_size__"
attr_reader :hash
@@ -39,7 +39,7 @@ module ActiveSupport
end
def start_element(name, attrs = [])
- new_hash = { CONTENT_KEY => "".dup }.merge!(Hash[attrs])
+ new_hash = { CONTENT_KEY => +"" }.merge!(Hash[attrs])
new_hash[HASH_SIZE_KEY] = new_hash.size + 1
case current_hash[name]
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index 32458d5b0d..8d6e3af066 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -8,7 +8,7 @@ module ActiveSupport
module XmlMini_REXML #:nodoc:
extend self
- CONTENT_KEY = "__content__".freeze
+ CONTENT_KEY = "__content__"
# Parse an XML Document string or IO into a simple hash.
#
@@ -76,7 +76,7 @@ module ActiveSupport
hash
else
# must use value to prevent double-escaping
- texts = "".dup
+ texts = +""
element.texts.each { |t| texts << t.value }
merge!(hash, CONTENT_KEY, texts)
end