diff options
Diffstat (limited to 'activesupport/lib')
50 files changed, 295 insertions, 102 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 8e516de4c9..a5d0c52b13 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -694,7 +694,7 @@ module ActiveSupport end def get_entry_value(entry, name, options) - instrument(:fetch_hit, name, options) {} + instrument(:fetch_hit, name, options) { } entry.value 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 5737450b4a..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 diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index c266b432c0..487fe79f41 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -657,9 +657,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/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/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/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb index ad6a9c6146..f36fef6cc9 100644 --- a/activesupport/lib/active_support/core_ext/object/blank.rb +++ b/activesupport/lib/active_support/core_ext/object/blank.rb @@ -4,7 +4,7 @@ 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/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index c874691629..aa6896af32 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 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/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 9dc2c46880..063d8d1587 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 @@ -138,7 +144,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 @@ -404,7 +410,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? @@ -416,7 +422,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) @@ -450,6 +456,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 @@ -491,21 +498,26 @@ 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 @@ -554,6 +566,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! @@ -743,6 +756,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 3abd25aa85..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. diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb index 414f727705..d3233e6111 100644 --- a/activesupport/lib/active_support/duration/iso8601_parser.rb +++ b/activesupport/lib/active_support/duration/iso8601_parser.rb @@ -13,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/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index e4afc8af93..2fac579469 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 diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb index 93bde57f6a..584930e413 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -87,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/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 7359de762a..1af9833d46 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -73,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 @@ -90,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 @@ -130,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}" @@ -199,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. @@ -269,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? @@ -364,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..dbc8b8a2fa 100644 --- a/activesupport/lib/active_support/inflector/transliterate.rb +++ b/activesupport/lib/active_support/inflector/transliterate.rb @@ -58,7 +58,7 @@ 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( @@ -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/logger_silence.rb b/activesupport/lib/active_support/logger_silence.rb index 89f32b6782..4344ca0429 100644 --- a/activesupport/lib/active_support/logger_silence.rb +++ b/activesupport/lib/active_support/logger_silence.rb @@ -2,7 +2,6 @@ require "active_support/concern" require "active_support/core_ext/module/attribute_accessors" -require "concurrent" module LoggerSilence extend ActiveSupport::Concern diff --git a/activesupport/lib/active_support/logger_thread_safe_level.rb b/activesupport/lib/active_support/logger_thread_safe_level.rb index ba32813d3d..22ab4cc28c 100644 --- a/activesupport/lib/active_support/logger_thread_safe_level.rb +++ b/activesupport/lib/active_support/logger_thread_safe_level.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "active_support/concern" +require "concurrent" module ActiveSupport module LoggerThreadSafeLevel # :nodoc: diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 404404cad1..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 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/number_helper.rb b/activesupport/lib/active_support/number_helper.rb index 8fd6e932f1..c75ad52b0c 100644 --- a/activesupport/lib/active_support/number_helper.rb +++ b/activesupport/lib/active_support/number_helper.rb @@ -85,6 +85,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 +103,8 @@ module ActiveSupport # # => "£1234567890,50" # number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '', format: '%n %u') # # => "1234567890,50 £" + # 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..aef5b62aed 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 @@ -15,7 +15,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..05427fef53 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 @@ -14,7 +14,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..908f788ee3 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 @@ -25,7 +25,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..0c72096b72 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 @@ -22,7 +22,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..6618ecffd5 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 @@ -7,7 +7,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_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb index eb528a0583..0ee5ef92dd 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 @@ -20,9 +20,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/subscriber.rb b/activesupport/lib/active_support/subscriber.rb index 5a4c3d74af..9562149f8d 100644 --- a/activesupport/lib/active_support/subscriber.rb +++ b/activesupport/lib/active_support/subscriber.rb @@ -92,7 +92,7 @@ module ActiveSupport event.finish! event.payload.merge!(payload) - method = name.split(".".freeze).first + method = name.split(".").first send(method, event) 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/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb index c6358002ea..fdc70e1cd3 100644 --- a/activesupport/lib/active_support/testing/method_call_assertions.rb +++ b/activesupport/lib/active_support/testing/method_call_assertions.rb @@ -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 1caac1feb3..beeb470659 100644 --- a/activesupport/lib/active_support/testing/parallelization.rb +++ b/activesupport/lib/active_support/testing/parallelization.rb @@ -65,22 +65,24 @@ module ActiveSupport def start @pool = @queue_size.times.map do |worker| fork do - DRb.stop_service + begin + DRb.stop_service - after_fork(worker) + after_fork(worker) - queue = DRbObject.new_with_uri(@url) + 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) + while job = queue.pop + klass = job[0] + method = job[1] + reporter = job[2] + result = Minitest.run_one_method(klass, method) - queue.record(reporter, result) + queue.record(reporter, result) + 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/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 |