diff options
Diffstat (limited to 'activesupport/lib')
79 files changed, 832 insertions, 536 deletions
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index a4fb697669..5589c71281 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true #-- -# Copyright (c) 2005-2018 David Heinemeier Hansson +# Copyright (c) 2005-2019 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb index 1796956bd7..62973eca58 100644 --- a/activesupport/lib/active_support/backtrace_cleaner.rb +++ b/activesupport/lib/active_support/backtrace_cleaner.rb @@ -93,7 +93,7 @@ module ActiveSupport return if gems_paths.empty? gems_regexp = %r{(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)} - gems_result = '\3 (\4) \5'.freeze + gems_result = '\3 (\4) \5' add_filter { |line| line.sub(gems_regexp, gems_result) } end diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index a5d0c52b13..e8518645d9 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -411,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") @@ -422,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? @@ -596,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 @@ -643,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}" } diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 53a2b07536..de1fb1886c 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -108,12 +108,10 @@ module ActiveSupport def lock_file(file_name, &block) if File.exist?(file_name) File.open(file_name, "r+") do |f| - begin - f.flock File::LOCK_EX - yield - ensure - f.flock File::LOCK_UN - end + f.flock File::LOCK_EX + yield + ensure + f.flock File::LOCK_UN end else yield diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 487fe79f41..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 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 2610114d8f..9acf674c40 100644 --- a/activesupport/lib/active_support/configurable.rb +++ b/activesupport/lib/active_support/configurable.rb @@ -2,7 +2,6 @@ require "active_support/concern" require "active_support/ordered_options" -require "active_support/core_ext/array/extract_options" module ActiveSupport # Configurable provides a <tt>config</tt> method to store and retrieve @@ -105,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) @@ -117,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 a2569c798b..88b6567712 100644 --- a/activesupport/lib/active_support/core_ext/array.rb +++ b/activesupport/lib/active_support/core_ext/array.rb @@ -6,5 +6,4 @@ 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" require "active_support/core_ext/array/inquiry" diff --git a/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb index 661971d7cd..ba3739f640 100644 --- a/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb +++ b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb @@ -1,9 +1,5 @@ # frozen_string_literal: true -class Array - # The human way of thinking about adding stuff to the end of a list is with append. - alias_method :append, :push unless [].respond_to?(:append) +require "active_support/deprecation" - # The human way of thinking about adding stuff to the beginning of a list is with prepend. - alias_method :prepend, :unshift unless [].respond_to?(:prepend) -end +ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Array#append and Array#prepend natively, so requiring active_support/core_ext/array/prepend_and_append is no longer necessary. Requiring it will raise LoadError in Rails 6.1." 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/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/compact.rb b/activesupport/lib/active_support/core_ext/hash/compact.rb index 28c8d86b9b..5cb858af5c 100644 --- a/activesupport/lib/active_support/core_ext/hash/compact.rb +++ b/activesupport/lib/active_support/core_ext/hash/compact.rb @@ -2,4 +2,4 @@ require "active_support/deprecation" -ActiveSupport::Deprecation.warn "Ruby 2.4+ (required by Rails 6) provides Hash#compact and Hash#compact! natively, so requiring active_support/core_ext/hash/compact is no longer necessary. Requiring it will raise LoadError in Rails 6.1." +ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Hash#compact and Hash#compact! natively, so requiring active_support/core_ext/hash/compact is no longer necessary. Requiring it will raise LoadError in Rails 6.1." diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb index bdf196ec3d..7d3495db2c 100644 --- a/activesupport/lib/active_support/core_ext/hash/keys.rb +++ b/activesupport/lib/active_support/core_ext/hash/keys.rb @@ -1,35 +1,6 @@ # frozen_string_literal: true class Hash - # Returns a new hash with all keys converted using the +block+ operation. - # - # hash = { name: 'Rob', age: '28' } - # - # hash.transform_keys { |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"} - # - # If you do not provide a +block+, it will return an Enumerator - # for chaining with other methods: - # - # hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"} - def transform_keys - return enum_for(:transform_keys) { size } unless block_given? - result = {} - each_key do |key| - result[yield(key)] = self[key] - end - result - end unless method_defined? :transform_keys - - # Destructively converts all keys using the +block+ operations. - # Same as +transform_keys+ but modifies +self+. - def transform_keys! - return enum_for(:transform_keys!) { size } unless block_given? - keys.each do |key| - self[yield(key)] = delete(key) - end - self - end unless method_defined? :transform_keys! - # Returns a new hash with all keys converted to strings. # # hash = { name: 'Rob', age: '28' } diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 2bd0a56ea4..3d0f8a1e62 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -1,34 +1,12 @@ # frozen_string_literal: true class Hash - # Slices a hash to include only the given keys. Returns a hash containing - # the given keys. - # - # { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b) - # # => {:a=>1, :b=>2} - # - # This is useful for limiting an options hash to valid keys before - # passing to a method: - # - # def search(criteria = {}) - # criteria.assert_valid_keys(:mass, :velocity, :time) - # end - # - # search(options.slice(:mass, :velocity, :time)) - # - # If you have an array of keys you want to limit to, you should splat them: - # - # valid_keys = [:mass, :velocity, :time] - # search(options.slice(*valid_keys)) - def slice(*keys) - keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) } - end unless method_defined?(:slice) - # 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/hash/transform_values.rb b/activesupport/lib/active_support/core_ext/hash/transform_values.rb index fc15130c9e..e4aeb0e891 100644 --- a/activesupport/lib/active_support/core_ext/hash/transform_values.rb +++ b/activesupport/lib/active_support/core_ext/hash/transform_values.rb @@ -2,4 +2,4 @@ require "active_support/deprecation" -ActiveSupport::Deprecation.warn "Ruby 2.4+ (required by Rails 6) provides Hash#transform_values natively, so requiring active_support/core_ext/hash/transform_values is no longer necessary. Requiring it will raise LoadError in Rails 6.1." +ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Hash#transform_values natively, so requiring active_support/core_ext/hash/transform_values is no longer necessary. Requiring it will raise LoadError in Rails 6.1." 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 281eaa7d5f..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,7 +1,5 @@ # frozen_string_literal: true -require "active_support/core_ext/array/extract_options" - # 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 b1788a000b..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,7 +1,5 @@ # frozen_string_literal: true -require "active_support/core_ext/array/extract_options" - # 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. @@ -35,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) @@ -49,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} @@ -78,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) @@ -91,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 @@ -141,9 +136,9 @@ class Module # # 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 3128539112..be90390ae4 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -19,7 +19,7 @@ class Module # public methods as your own. # # ==== Options - # * <tt>:to</tt> - Specifies the target object + # * <tt>:to</tt> - Specifies the target object name as a symbol or string # * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix # * <tt>:allow_nil</tt> - If set to true, prevents a +Module::DelegationError+ # from being raised 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: '£', separator: ',', delimiter: '') - # # => "£1234567890,50" - # 1234567890.50.to_s(:currency, unit: '£', separator: ',', delimiter: '', format: '%n %u') - # # => "1234567890,50 £" - # - # 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: '£', separator: ',', delimiter: '') + # # => "£1234567890,50" + # 1234567890.50.to_s(:currency, unit: '£', separator: ',', delimiter: '', format: '%n %u') + # # => "1234567890,50 £" + # + # 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/numeric/inquiry.rb b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb index e05e40825b..6b5240d051 100644 --- a/activesupport/lib/active_support/core_ext/numeric/inquiry.rb +++ b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb @@ -2,4 +2,4 @@ require "active_support/deprecation" -ActiveSupport::Deprecation.warn "Ruby 2.4+ (required by Rails 6) provides Numeric#positive? and Numeric#negative? natively, so requiring active_support/core_ext/numeric/inquiry is no longer necessary. Requiring it will raise LoadError in Rails 6.1." +ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Numeric#positive? and Numeric#negative? natively, so requiring active_support/core_ext/numeric/inquiry is no longer necessary. Requiring it will raise LoadError in Rails 6.1." diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index aa6896af32..ef8a1f476d 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -143,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/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/securerandom.rb b/activesupport/lib/active_support/core_ext/securerandom.rb index b4a491f5fd..ef812f7e1a 100644 --- a/activesupport/lib/active_support/core_ext/securerandom.rb +++ b/activesupport/lib/active_support/core_ext/securerandom.rb @@ -4,17 +4,18 @@ require "securerandom" module SecureRandom BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"] + BASE36_ALPHABET = ("0".."9").to_a + ("a".."z").to_a + # SecureRandom.base58 generates a random base58 string. # - # The argument _n_ specifies the length, of the random string to be generated. + # The argument _n_ specifies the length of the random string to be generated. # # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future. # - # The result may contain alphanumeric characters except 0, O, I and l + # The result may contain alphanumeric characters except 0, O, I and l. # # p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE" # p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7" - # def self.base58(n = 16) SecureRandom.random_bytes(n).unpack("C*").map do |byte| idx = byte % 64 @@ -22,4 +23,23 @@ module SecureRandom BASE58_ALPHABET[idx] end.join end + + # SecureRandom.base36 generates a random base36 string in lowercase. + # + # The argument _n_ specifies the length of the random string to be generated. + # + # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future. + # This method can be used over +base58+ if a deterministic case key is necessary. + # + # The result will contain alphanumeric characters in lowercase. + # + # p SecureRandom.base36 # => "4kugl2pdqmscqtje" + # p SecureRandom.base36(24) # => "77tmhrhjfvfdwodq8w7ev2m7" + def self.base36(n = 16) + SecureRandom.random_bytes(n).unpack("C*").map do |byte| + idx = byte % 64 + idx = SecureRandom.random_number(36) if idx >= 36 + BASE36_ALPHABET[idx] + end.join + 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 d837bb10aa..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 @@ -186,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 238a9f0ee6..d5d00b5e6e 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -103,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] = [] } @@ -144,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 @@ -254,7 +256,9 @@ module ActiveSupport #: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 @@ -410,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? @@ -505,7 +509,7 @@ module ActiveSupport #:nodoc: 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}" @@ -521,8 +525,8 @@ module ActiveSupport #:nodoc: 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 diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index 81482092fe..d99571790f 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -52,27 +52,37 @@ module ActiveSupport options = method_names.extract_options! deprecator = options.delete(:deprecator) || self method_names += options.keys + mod = Module.new method_names.each do |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}" + 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]) - send(without_method, *args, &block) - end + target_module.define_method(with_method) do |*args, &block| + deprecator.deprecation_warning(method_name, options[method_name]) + send(without_method, *args, &block) + end - target_module.send(:alias_method, without_method, method_name) - target_module.send(:alias_method, method_name, with_method) + target_module.alias_method(without_method, method_name) + target_module.alias_method(method_name, with_method) - case - 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) + case + 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.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) unless mod.instance_methods(false).empty? end end end 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 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 0fb0e3f3a5..1125454919 100644 --- a/activesupport/lib/active_support/duration/iso8601_serializer.rb +++ b/activesupport/lib/active_support/duration/iso8601_serializer.rb @@ -14,7 +14,7 @@ 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" output << "#{parts[:years]}Y" if parts.key?(:years) 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/evented_file_update_checker.rb b/activesupport/lib/active_support/evented_file_update_checker.rb index 97e982eb05..54bc5c0ae5 100644 --- a/activesupport/lib/active_support/evented_file_update_checker.rb +++ b/activesupport/lib/active_support/evented_file_update_checker.rb @@ -52,16 +52,17 @@ module ActiveSupport @pid = Process.pid @boot_mutex = Mutex.new - if (@dtw = directories_to_watch).any? + dtw = directories_to_watch + @dtw, @missing = dtw.partition(&:exist?) + + if @dtw.any? # Loading listen triggers warnings. These are originated by a legit # usage of attr_* macros for private attributes, but adds a lot of noise # to our test suite. Thus, we lazy load it and disable warnings locally. silence_warnings do - begin - require "listen" - rescue LoadError => e - raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace - end + require "listen" + rescue LoadError => e + raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace end end boot! @@ -75,6 +76,19 @@ module ActiveSupport @updated.make_true end end + + if @missing.any?(&:exist?) + @boot_mutex.synchronize do + appeared, @missing = @missing.partition(&:exist?) + shutdown! + + @dtw += appeared + boot! + + @updated.make_true + end + end + @updated.true? end @@ -96,6 +110,10 @@ module ActiveSupport Listen.to(*@dtw, &method(:changed)).start end + def shutdown! + Listen.stop + end + def changed(modified, added, removed) unless updated? @updated.make_true if (modified + added + removed).any? { |f| watching?(f) } @@ -123,7 +141,7 @@ module ActiveSupport end def directories_to_watch - dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) } + dtw = @files.map(&:dirname) + @dirs.keys dtw.compact! dtw.uniq! 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/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index 2b86d233e5..fa087c4dd6 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "concurrent/map" -require "active_support/core_ext/array/prepend_and_append" 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 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..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/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index 0f7be06c8e..938cfdb914 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -5,8 +5,8 @@ require "active_support/core_ext/class/attribute" require "active_support/subscriber" module ActiveSupport - # ActiveSupport::LogSubscriber is an object set to consume - # ActiveSupport::Notifications with the sole purpose of logging them. + # <tt>ActiveSupport::LogSubscriber</tt> is an object set to consume + # <tt>ActiveSupport::Notifications</tt> with the sole purpose of logging them. # The log subscriber dispatches notifications to a registered object based # on its given namespace. # @@ -16,7 +16,7 @@ module ActiveSupport # module ActiveRecord # class LogSubscriber < ActiveSupport::LogSubscriber # def sql(event) - # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}" + # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}" # end # end # end @@ -29,13 +29,36 @@ module ActiveSupport # subscriber, the line above should be called after your # <tt>ActiveRecord::LogSubscriber</tt> definition. # - # After configured, whenever a "sql.active_record" notification is published, - # it will properly dispatch the event (ActiveSupport::Notifications::Event) to - # the sql method. + # After configured, whenever a <tt>"sql.active_record"</tt> notification is published, + # it will properly dispatch the event + # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method. + # + # Being an <tt>ActiveSupport::Notifications</tt> consumer, + # <tt>ActiveSupport::LogSubscriber</tt> exposes a simple interface to check if + # instrumented code raises an exception. It is common to log a different + # message in case of an error, and this can be achieved by extending + # the previous example: + # + # module ActiveRecord + # class LogSubscriber < ActiveSupport::LogSubscriber + # def sql(event) + # exception = event.payload[:exception] + # + # if exception + # exception_object = event.payload[:exception_object] + # + # error "[ERROR] #{event.payload[:name]}: #{exception.join(', ')} " \ + # "(#{exception_object.backtrace.first})" + # else + # # standard logger code + # end + # end + # end + # end # # Log subscriber also has some helpers to deal with logging and automatically - # flushes all logs when the request finishes (via action_dispatch.callback - # notification) in a Rails environment. + # flushes all logs when the request finishes + # (via <tt>action_dispatch.callback</tt> notification) in a Rails environment. class LogSubscriber < Subscriber # Embed in a String to clear all previous ANSI sequences. CLEAR = "\e[0m" 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 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/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 499a206f49..a1e23aeaca 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -17,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 @@ -76,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 @@ -108,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 @@ -117,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. @@ -153,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 @@ -165,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. @@ -189,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 @@ -205,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 @@ -214,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..7ccc333463 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. # @@ -67,9 +67,12 @@ module ActiveSupport # have a key <tt>:exception</tt> with an array of two elements as value: a string with # the name of the exception class, and the exception message. # The <tt>:exception_object</tt> key of the payload will have the exception - # itself as the value. + # itself as the value: # - # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt> + # event.payload[:exception] # => ["ArgumentError", "Invalid value"] + # event.payload[:exception_object] # => #<ArgumentError: Invalid value> + # + # As the earlier example depicts, the class <tt>ActiveSupport::Notifications::Event</tt> # is able to take the arguments as they come and provide an object-oriented # interface to that data. # @@ -171,6 +174,31 @@ 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| + # @event = ActiveSupport::Notifications::Event.new(*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 + # + # If the block passed to the method only takes one parameter, + # it will yield an event object to the block: + # + # ActiveSupport::Notifications.subscribe(/render/) do |event| + # @event = event + # end def subscribe(*args, &block) notifier.subscribe(*args, &block) end diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index f8344912bb..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 @@ -83,6 +88,11 @@ module ActiveSupport @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 @@ -130,7 +140,7 @@ module ActiveSupport Process.clock_gettime(Process::CLOCK_MONOTONIC) end - if defined?(Process::CLOCK_PROCESS_CPUTIME_ID) + if clock_gettime_supported? def now_cpu Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID) end diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb index c75ad52b0c..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 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..2f81cd4f80 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 #-- @@ -49,11 +50,9 @@ module ActiveSupport def self.reload! executor.wrap do new.tap do |instance| - begin - instance.run! - ensure - instance.complete! - end + instance.run! + ensure + instance.complete! end end prepare! diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb index 5a4c3d74af..f3e902f9dd 100644 --- a/activesupport/lib/active_support/subscriber.rb +++ b/activesupport/lib/active_support/subscriber.rb @@ -79,12 +79,12 @@ module ActiveSupport end def start(name, id, payload) - e = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload) - e.start! + 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) @@ -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 dd72da500c..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 diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index ef12c6b9b0..7be4108ed7 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -12,6 +12,7 @@ require "active_support/testing/constant_lookup" require "active_support/testing/time_helpers" require "active_support/testing/file_fixtures" require "active_support/testing/parallelization" +require "concurrent/utility/processor_counter" module ActiveSupport class TestCase < ::Minitest::Test @@ -58,16 +59,20 @@ module ActiveSupport # If the number of workers is set to +1+ or fewer, the tests will not be # parallelized. # + # If +workers+ is set to +:number_of_processors+, the number of workers will be + # set to the actual core count on the machine you are on. + # # The default parallelization method is to fork processes. If you'd like to # use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+ # method. Note the threaded parallelization does not create multiple # database and will not work with system tests at this time. # - # parallelize(workers: 2, with: :threads) + # parallelize(workers: :number_of_processors, with: :threads) # # The threaded parallelization uses minitest's parallel executor directly. # The processes parallelization uses a Ruby DRb server. - def parallelize(workers: 2, with: :processes) + def parallelize(workers: :number_of_processors, with: :processes) + workers = Concurrent.physical_processor_count if workers == :number_of_processors workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"] return if workers <= 1 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 fdc70e1cd3..03c38be481 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,18 +35,16 @@ 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 |*| + klass.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}") + klass.alias_method "original_#{method_name}", method_name + klass.alias_method method_name, "stubbed_#{method_name}" yield @@ -55,9 +53,9 @@ module ActiveSupport 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}") + klass.alias_method method_name, "original_#{method_name}" + klass.undef_method "original_#{method_name}" + klass.undef_method "stubbed_#{method_name}" end def assert_not_called_on_instance_of(klass, method_name, message = nil, &block) diff --git a/activesupport/lib/active_support/testing/parallelization.rb b/activesupport/lib/active_support/testing/parallelization.rb index beeb470659..63440069b1 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,12 +15,15 @@ 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 @@ -65,24 +69,29 @@ module ActiveSupport def start @pool = @queue_size.times.map do |worker| fork do - begin - DRb.stop_service + 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) + 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 - ensure - run_cleanup(worker) end + ensure + 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 f160e66971..5a3fa9346c 100644 --- a/activesupport/lib/active_support/testing/time_helpers.rb +++ b/activesupport/lib/active_support/testing/time_helpers.rb @@ -22,7 +22,7 @@ module ActiveSupport @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name) - object.singleton_class.send :alias_method, new_name, method_name + object.singleton_class.alias_method new_name, method_name object.define_singleton_method(method_name, &block) end @@ -43,9 +43,9 @@ module ActiveSupport def unstub_object(stub) singleton_class = stub.object.singleton_class - singleton_class.send :silence_redefinition_of_method, stub.method_name - singleton_class.send :alias_method, stub.method_name, stub.original_method - singleton_class.send :undef_method, stub.original_method + singleton_class.silence_redefinition_of_method stub.method_name + singleton_class.alias_method stub.method_name, stub.original_method + singleton_class.undef_method stub.original_method end end diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index fb6956f64f..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 diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index fd07a3a6a2..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 diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index e42eee07a3..075cd4ed8b 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -3,6 +3,7 @@ require "time" require "base64" require "bigdecimal" +require "bigdecimal/util" require "active_support/core_ext/module/delegation" require "active_support/core_ext/string/inflections" require "active_support/core_ext/date_time/calculations" @@ -68,11 +69,7 @@ module ActiveSupport "float" => Proc.new { |float| float.to_f }, "decimal" => Proc.new do |number| if String === number - begin - BigDecimal(number) - rescue ArgumentError - BigDecimal("0") - end + number.to_d else BigDecimal(number) end diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index 59c65db2d5..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 diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index 2a16932f03..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. # diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb index a22a2c9cb7..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 diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 4762a759d6..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. # diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb index 0bbb4e258a..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 diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index 55a155d4ee..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. # |