aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG.md189
-rw-r--r--activesupport/activesupport.gemspec3
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb23
-rw-r--r--activesupport/lib/active_support/cache.rb35
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb21
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/null_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/redis_cache_store.rb29
-rw-r--r--activesupport/lib/active_support/callbacks.rb17
-rw-r--r--activesupport/lib/active_support/concern.rb10
-rw-r--r--activesupport/lib/active_support/configurable.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/array.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/extract.rb21
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb31
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb32
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/integer/multiple.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/load_error.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/module/introspection.rb50
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/conversions.rb244
-rw-r--r--activesupport/lib/active_support/core_ext/object/blank.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_query.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/object/with_options.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/range.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/range/compare_range.rb61
-rw-r--r--activesupport/lib/active_support/core_ext/range/conversions.rb60
-rw-r--r--activesupport/lib/active_support/core_ext/range/include_range.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb21
-rw-r--r--activesupport/lib/active_support/core_ext/string/strip.rb2
-rw-r--r--activesupport/lib/active_support/current_attributes.rb2
-rw-r--r--activesupport/lib/active_support/dependencies.rb49
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb6
-rw-r--r--activesupport/lib/active_support/deprecation/method_wrappers.rb32
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb2
-rw-r--r--activesupport/lib/active_support/descendants_tracker.rb11
-rw-r--r--activesupport/lib/active_support/duration.rb1
-rw-r--r--activesupport/lib/active_support/duration/iso8601_parser.rb5
-rw-r--r--activesupport/lib/active_support/duration/iso8601_serializer.rb6
-rw-r--r--activesupport/lib/active_support/encrypted_configuration.rb2
-rw-r--r--activesupport/lib/active_support/execution_wrapper.rb1
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb4
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb15
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb1
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb27
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb12
-rw-r--r--activesupport/lib/active_support/json/decoding.rb46
-rw-r--r--activesupport/lib/active_support/key_generator.rb2
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb6
-rw-r--r--activesupport/lib/active_support/logger.rb15
-rw-r--r--activesupport/lib/active_support/logger_silence.rb40
-rw-r--r--activesupport/lib/active_support/logger_thread_safe_level.rb30
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb6
-rw-r--r--activesupport/lib/active_support/message_verifier.rb4
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb77
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb60
-rw-r--r--activesupport/lib/active_support/notifications.rb22
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb42
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb75
-rw-r--r--activesupport/lib/active_support/number_helper.rb7
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_currency_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_phone_converter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb8
-rw-r--r--activesupport/lib/active_support/parameter_filter.rb124
-rw-r--r--activesupport/lib/active_support/reloader.rb1
-rw-r--r--activesupport/lib/active_support/subscriber.rb15
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb13
-rw-r--r--activesupport/lib/active_support/test_case.rb4
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb16
-rw-r--r--activesupport/lib/active_support/testing/deprecation.rb1
-rw-r--r--activesupport/lib/active_support/testing/file_fixtures.rb2
-rw-r--r--activesupport/lib/active_support/testing/method_call_assertions.rb31
-rw-r--r--activesupport/lib/active_support/testing/parallelization.rb61
-rw-r--r--activesupport/lib/active_support/testing/time_helpers.rb3
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb18
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb18
-rw-r--r--activesupport/lib/active_support/xml_mini.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/libxmlsax.rb8
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogirisax.rb6
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb4
-rw-r--r--activesupport/test/abstract_unit.rb27
-rw-r--r--activesupport/test/autoloading_fixtures/module_folder/nested_with_require.rb8
-rw-r--r--activesupport/test/autoloading_fixtures/nested_with_require_parent.rb5
-rw-r--r--activesupport/test/benchmarkable_test.rb4
-rw-r--r--activesupport/test/broadcast_logger_test.rb11
-rw-r--r--activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb2
-rw-r--r--activesupport/test/cache/behaviors/cache_store_behavior.rb64
-rw-r--r--activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb6
-rw-r--r--activesupport/test/cache/cache_entry_test.rb4
-rw-r--r--activesupport/test/cache/cache_key_test.rb4
-rw-r--r--activesupport/test/cache/local_cache_middleware_test.rb2
-rw-r--r--activesupport/test/cache/stores/memory_store_test.rb22
-rw-r--r--activesupport/test/cache/stores/redis_cache_store_test.rb44
-rw-r--r--activesupport/test/callbacks_test.rb4
-rw-r--r--activesupport/test/clean_backtrace_test.rb40
-rw-r--r--activesupport/test/concern_test.rb8
-rw-r--r--activesupport/test/core_ext/array/extract_test.rb44
-rw-r--r--activesupport/test/core_ext/date_and_time_compatibility_test.rb4
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb4
-rw-r--r--activesupport/test/core_ext/duration_test.rb12
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb15
-rw-r--r--activesupport/test/core_ext/file_test.rb14
-rw-r--r--activesupport/test/core_ext/module/concerning_test.rb2
-rw-r--r--activesupport/test/core_ext/module/introspection_test.rb40
-rw-r--r--activesupport/test/core_ext/object/duplicable_test.rb2
-rw-r--r--activesupport/test/core_ext/object/instance_variables_test.rb6
-rw-r--r--activesupport/test/core_ext/object/to_query_test.rb14
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb86
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb38
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb2
-rw-r--r--activesupport/test/dependencies/module_folder/lib_class.rb8
-rw-r--r--activesupport/test/dependencies_test.rb95
-rw-r--r--activesupport/test/deprecation/method_wrappers_test.rb42
-rw-r--r--activesupport/test/deprecation_test.rb15
-rw-r--r--activesupport/test/encrypted_configuration_test.rb6
-rw-r--r--activesupport/test/evented_file_update_checker_test.rb2
-rw-r--r--activesupport/test/executor_test.rb18
-rw-r--r--activesupport/test/fixtures/concern/some_concern.rb11
-rw-r--r--activesupport/test/gzip_test.rb2
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb20
-rw-r--r--activesupport/test/json/encoding_test.rb11
-rw-r--r--activesupport/test/key_generator_test.rb3
-rw-r--r--activesupport/test/lazy_load_hooks_test.rb49
-rw-r--r--activesupport/test/log_subscriber_test.rb16
-rw-r--r--activesupport/test/logger_test.rb5
-rw-r--r--activesupport/test/metadata/shared_metadata_tests.rb5
-rw-r--r--activesupport/test/multibyte_chars_test.rb145
-rw-r--r--activesupport/test/multibyte_conformance_test.rb103
-rw-r--r--activesupport/test/multibyte_grapheme_break_conformance_test.rb14
-rw-r--r--activesupport/test/multibyte_normalization_conformance_test.rb102
-rw-r--r--activesupport/test/multibyte_test_helpers.rb10
-rw-r--r--activesupport/test/notifications_test.rb38
-rw-r--r--activesupport/test/parameter_filter_test.rb105
-rw-r--r--activesupport/test/reloader_test.rb8
-rw-r--r--activesupport/test/safe_buffer_test.rb59
-rw-r--r--activesupport/test/share_lock_test.rb54
-rw-r--r--activesupport/test/silence_logger_test.rb35
-rw-r--r--activesupport/test/tagged_logging_test.rb29
-rw-r--r--activesupport/test/test_case_test.rb18
-rw-r--r--activesupport/test/testing/method_call_assertions_test.rb106
-rw-r--r--activesupport/test/time_travel_test.rb4
-rw-r--r--activesupport/test/time_zone_test.rb10
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb2
-rw-r--r--activesupport/test/xml_mini/xml_mini_engine_test.rb2
160 files changed, 2751 insertions, 914 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 62c0f612a8..ba8aaf47f9 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,190 @@
+* If the same block is `included` multiple times for a Concern, an exception is no longer raised.
+
+ *Mark J. Titorenko*, *Vlad Bokov*
+
+* Fix bug where `#to_options` for `ActiveSupport::HashWithIndifferentAccess`
+ would not act as alias for `#symbolize_keys`.
+
+ *Nick Weiland*
+
+* Improve the logic that detects non-autoloaded constants.
+
+ *Jan Habermann*, *Xavier Noria*
+
+* Deprecate `ActiveSupport::Multibyte::Unicode#pack_graphemes(array)` and `ActiveSuppport::Multibyte::Unicode#unpack_graphemes(string)`
+ in favor of `array.flatten.pack("U*")` and `string.scan(/\X/).map(&:codepoints)`, respectively.
+
+ *Francesco Rodríguez*
+
+* Deprecate `ActiveSupport::Multibyte::Chars.consumes?` in favor of `String#is_utf8?`.
+
+ *Francesco Rodríguez*
+
+* Fix duration being rounded to a full second.
+ ```
+ time = DateTime.parse("2018-1-1")
+ time += 0.51.seconds
+ ```
+ Will now correctly add 0.51 second and not 1 full second.
+
+ *Edouard Chin*
+
+* Deprecate `ActiveSupport::Multibyte::Unicode#normalize` and `ActiveSuppport::Multibyte::Chars#normalize`
+ in favor of `String#unicode_normalize`
+
+ *Francesco Rodríguez*
+
+* Deprecate `ActiveSupport::Multibyte::Unicode#downcase/upcase/swapcase` in favor of
+ `String#downcase/upcase/swapcase`.
+
+ *Francesco Rodríguez*
+
+* Add `ActiveSupport::ParameterFilter`.
+
+ *Yoshiyuki Kinjo*
+
+* Rename `Module#parent`, `Module#parents`, and `Module#parent_name` to
+ `module_parent`, `module_parents`, and `module_parent_name`.
+
+ *Gannon McGibbon*
+
+* Deprecate the use of `LoggerSilence` in favor of `ActiveSupport::LoggerSilence`
+
+ *Edouard Chin*
+
+* Deprecate using negative limits in `String#first` and `String#last`.
+
+ *Gannon McGibbon*, *Eric Turner*
+
+* Fix bug where `#without` for `ActiveSupport::HashWithIndifferentAccess` would fail
+ with symbol arguments
+
+ *Abraham Chan*
+
+* Treat `#delete_prefix`, `#delete_suffix` and `#unicode_normalize` results as non-`html_safe`.
+ Ensure safety of arguments for `#insert`, `#[]=` and `#replace` calls on `html_safe` Strings.
+
+ *Janosch Müller*
+
+* Changed `ActiveSupport::TaggedLogging.new` to return a new logger instance instead
+ of mutating the one received as parameter.
+
+ *Thierry Joyal*
+
+* Define `unfreeze_time` as an alias of `travel_back` in `ActiveSupport::Testing::TimeHelpers`.
+
+ The alias is provided for symmetry with `freeze_time`.
+
+ *Ryan Davidson*
+
+* Add support for tracing constant autoloads. Just throw
+
+ ActiveSupport::Dependencies.logger = Rails.logger
+ ActiveSupport::Dependencies.verbose = true
+
+ in an initializer.
+
+ *Xavier Noria*
+
+* Maintain `html_safe?` on html_safe strings when sliced.
+
+ string = "<div>test</div>".html_safe
+ string[-1..1].html_safe? # => true
+
+ *Elom Gomez*, *Yumin Wong*
+
+* Add `Array#extract!`.
+
+ The method removes and returns the elements for which the block returns a true value.
+ If no block is given, an Enumerator is returned instead.
+
+ numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
+ numbers # => [0, 2, 4, 6, 8]
+
+ *bogdanvlviv*
+
+* Support not to cache `nil` for `ActiveSupport::Cache#fetch`.
+
+ cache.fetch('bar', skip_nil: true) { nil }
+ cache.exist?('bar') # => false
+
+ *Martin Hong*
+
+* Add "event object" support to the notification system.
+ Before this change, end users were forced to create hand made artisanal
+ event objects on their own, like this:
+
+ ActiveSupport::Notifications.subscribe('wait') do |*args|
+ @event = ActiveSupport::Notifications::Event.new(*args)
+ end
+
+ ActiveSupport::Notifications.instrument('wait') do
+ sleep 1
+ end
+
+ @event.duration # => 1000.138
+
+ After this change, if the block passed to `subscribe` only takes one
+ parameter, the framework will yield an event object to the block. Now
+ end users are no longer required to make their own:
+
+ ActiveSupport::Notifications.subscribe('wait') do |event|
+ @event = event
+ end
+
+ ActiveSupport::Notifications.instrument('wait') do
+ sleep 1
+ end
+
+ p @event.allocations # => 7
+ p @event.cpu_time # => 0.256
+ p @event.idle_time # => 1003.2399
+
+ Now you can enjoy event objects without making them yourself. Neat!
+
+ *Aaron "t.lo" Patterson*
+
+* Add cpu_time, idle_time, and allocations to Event.
+
+ *Eileen M. Uchitelle*, *Aaron Patterson*
+
+* RedisCacheStore: support key expiry in increment/decrement.
+
+ Pass `:expires_in` to `#increment` and `#decrement` to set a Redis EXPIRE on the key.
+
+ If the key is already set to expire, RedisCacheStore won't extend its expiry.
+
+ Rails.cache.increment("some_key", 1, expires_in: 2.minutes)
+
+ *Jason Lee*
+
+* Allow `Range#===` and `Range#cover?` on Range.
+
+ `Range#cover?` can now accept a range argument like `Range#include?` and
+ `Range#===`. `Range#===` works correctly on Ruby 2.6. `Range#include?` is moved
+ into a new file, with these two methods.
+
+ *Requiring active_support/core_ext/range/include_range is now deprecated.*
+ *Use `require "active_support/core_ext/range/compare_range"` instead.*
+
+ *utilum*
+
+* Add `index_with` to Enumerable.
+
+ Allows creating a hash from an enumerable with the value from a passed block
+ or a default argument.
+
+ %i( title body ).index_with { |attr| post.public_send(attr) }
+ # => { title: "hey", body: "what's up?" }
+
+ %i( title body ).index_with(nil)
+ # => { title: nil, body: nil }
+
+ Closely linked with `index_by`, which creates a hash where the keys are extracted from a block.
+
+ *Kasper Timm Hansen*
+
* Fix bug where `ActiveSupport::Timezone.all` would fail when tzinfo data for
any timezone defined in `ActiveSupport::TimeZone::MAPPING` is missing.
@@ -15,7 +202,7 @@
*Godfrey Chan*
-* Fix bug where `URI.unscape` would fail with mixed Unicode/escaped character input:
+* Fix bug where `URI.unescape` would fail with mixed Unicode/escaped character input:
URI.unescape("\xe3\x83\x90") # => "バ"
URI.unescape("%E3%83%90") # => "バ"
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index aa695c98b2..448a2eeebb 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -27,6 +27,9 @@ Gem::Specification.new do |s|
"changelog_uri" => "https://github.com/rails/rails/blob/v#{version}/activesupport/CHANGELOG.md"
}
+ # NOTE: Please read our dependency guidelines before updating versions:
+ # https://edgeguides.rubyonrails.org/security.html#dependency-management-and-cves
+
s.add_dependency "i18n", ">= 0.7", "< 2"
s.add_dependency "tzinfo", "~> 1.1"
s.add_dependency "minitest", "~> 5.1"
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 16dd733ddb..62973eca58 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -31,6 +31,9 @@ module ActiveSupport
class BacktraceCleaner
def initialize
@filters, @silencers = [], []
+ add_gem_filter
+ add_gem_silencer
+ add_stdlib_silencer
end
# Returns the backtrace after all filters and silencers have been run
@@ -82,6 +85,26 @@ module ActiveSupport
end
private
+
+ FORMATTED_GEMS_PATTERN = /\A[^\/]+ \([\w.]+\) /
+
+ def add_gem_filter
+ gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
+ return if gems_paths.empty?
+
+ gems_regexp = %r{(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
+ gems_result = '\3 (\4) \5'
+ add_filter { |line| line.sub(gems_regexp, gems_result) }
+ end
+
+ def add_gem_silencer
+ add_silencer { |line| FORMATTED_GEMS_PATTERN.match?(line) }
+ end
+
+ def add_stdlib_silencer
+ add_silencer { |line| line.start_with?(RbConfig::CONFIG["rubylibdir"]) }
+ end
+
def filter_backtrace(backtrace)
@filters.each do |f|
backtrace = backtrace.map { |line| f.call(line) }
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index d769e2c8ea..e8518645d9 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -229,6 +229,14 @@ module ActiveSupport
# ask whether you should force a cache write. Otherwise, it's clearer to
# just call <tt>Cache#write</tt>.
#
+ # Setting <tt>skip_nil: true</tt> will not cache nil result:
+ #
+ # cache.fetch('foo') { nil }
+ # cache.fetch('bar', skip_nil: true) { nil }
+ # cache.exist?('foo') # => true
+ # cache.exist?('bar') # => false
+ #
+ #
# Setting <tt>compress: false</tt> disables compression of the cache entry.
#
# Setting <tt>:expires_in</tt> will set an expiration time on the cache.
@@ -403,8 +411,6 @@ module ActiveSupport
# to the cache. If you do not want to write the cache when the cache is
# not found, use #read_multi.
#
- # Options are passed to the underlying cache implementation.
- #
# Returns a hash with the data for each of the names. For example:
#
# cache.write("bim", "bam")
@@ -414,6 +420,17 @@ module ActiveSupport
# # => { "bim" => "bam",
# # "unknown_key" => "Fallback value for key: unknown_key" }
#
+ # Options are passed to the underlying cache implementation. For example:
+ #
+ # cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
+ # "buzz"
+ # end
+ # # => {"fizz"=>"buzz"}
+ # cache.read("fizz")
+ # # => "buzz"
+ # sleep(6)
+ # cache.read("fizz")
+ # # => nil
def fetch_multi(*names)
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
@@ -588,9 +605,13 @@ module ActiveSupport
# Merges the default options with ones specific to a method call.
def merged_options(call_options)
if call_options
- options.merge(call_options)
+ if options.empty?
+ call_options
+ else
+ options.merge(call_options)
+ end
else
- options.dup
+ options
end
end
@@ -635,7 +656,7 @@ module ActiveSupport
if key.size > 1
key = key.collect { |element| expanded_key(element) }
else
- key = key.first
+ key = expanded_key(key.first)
end
when Hash
key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
@@ -686,7 +707,7 @@ module ActiveSupport
end
def get_entry_value(entry, name, options)
- instrument(:fetch_hit, name, options) {}
+ instrument(:fetch_hit, name, options) { }
entry.value
end
@@ -695,7 +716,7 @@ module ActiveSupport
yield(name)
end
- write(name, result, options)
+ write(name, result, options) unless result.nil? && options[:skip_nil]
result
end
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index a0f44aac0f..53a2b07536 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -26,6 +26,11 @@ module ActiveSupport
@cache_path = cache_path.to_s
end
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
# Deletes all items from the cache. In this case it deletes all the entries in the specified
# file store directory except for .keep or .gitkeep. Be careful which directory is specified in your
# config file when using +FileStore+ because everything in that directory will be deleted.
@@ -127,15 +132,19 @@ module ActiveSupport
hash = Zlib.adler32(fname)
hash, dir_1 = hash.divmod(0x1000)
dir_2 = hash.modulo(0x1000)
- fname_paths = []
# Make sure file name doesn't exceed file system limits.
- begin
- fname_paths << fname[0, FILENAME_MAX_SIZE]
- fname = fname[FILENAME_MAX_SIZE..-1]
- end until fname.blank?
+ if fname.length < FILENAME_MAX_SIZE
+ fname_paths = fname
+ else
+ fname_paths = []
+ begin
+ fname_paths << fname[0, FILENAME_MAX_SIZE]
+ fname = fname[FILENAME_MAX_SIZE..-1]
+ end until fname.blank?
+ end
- File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
+ File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, fname_paths)
end
# Translate a file path into a key.
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 2840781dde..174c784deb 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -47,6 +47,11 @@ module ActiveSupport
end
end
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
prepend Strategy::LocalCache
prepend LocalCacheWithRaw
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 564ac17241..106b616529 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -30,6 +30,11 @@ module ActiveSupport
@pruning = false
end
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
# Delete all data stored in a given cache store.
def clear(options = nil)
synchronize do
diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb
index 1a5983db43..8452a28fd8 100644
--- a/activesupport/lib/active_support/cache/null_store.rb
+++ b/activesupport/lib/active_support/cache/null_store.rb
@@ -12,6 +12,11 @@ module ActiveSupport
class NullStore < Store
prepend Strategy::LocalCache
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
def clear(options = nil)
end
diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb
index 11c574258f..9a55e49e27 100644
--- a/activesupport/lib/active_support/cache/redis_cache_store.rb
+++ b/activesupport/lib/active_support/cache/redis_cache_store.rb
@@ -66,6 +66,11 @@ module ActiveSupport
SCAN_BATCH_SIZE = 1000
private_constant :SCAN_BATCH_SIZE
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
# Support raw values in the local cache strategy.
module LocalCacheWithRaw # :nodoc:
private
@@ -258,7 +263,14 @@ module ActiveSupport
def increment(name, amount = 1, options = nil)
instrument :increment, name, amount: amount do
failsafe :increment do
- redis.with { |c| c.incrby normalize_key(name, options), amount }
+ options = merged_options(options)
+ key = normalize_key(name, options)
+
+ redis.with do |c|
+ c.incrby(key, amount).tap do
+ write_key_expiry(c, key, options)
+ end
+ end
end
end
end
@@ -274,7 +286,14 @@ module ActiveSupport
def decrement(name, amount = 1, options = nil)
instrument :decrement, name, amount: amount do
failsafe :decrement do
- redis.with { |c| c.decrby normalize_key(name, options), amount }
+ options = merged_options(options)
+ key = normalize_key(name, options)
+
+ redis.with do |c|
+ c.decrby(key, amount).tap do
+ write_key_expiry(c, key, options)
+ end
+ end
end
end
end
@@ -385,6 +404,12 @@ module ActiveSupport
end
end
+ def write_key_expiry(client, key, options)
+ if options[:expires_in] && client.ttl(key).negative?
+ client.expire key, options[:expires_in].to_i
+ end
+ end
+
# Delete an entry from the cache.
def delete_entry(key, options)
failsafe :delete_entry, returning: false do
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index a1b841ec3d..d0644a0f7e 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -23,6 +23,9 @@ module ActiveSupport
# +ClassMethods.set_callback+), and run the installed callbacks at the
# appropriate times (via +run_callbacks+).
#
+ # By default callbacks are halted by throwing +:abort+.
+ # See +ClassMethods.define_callbacks+ for details.
+ #
# Three kinds of callbacks are supported: before callbacks, run before a
# certain event; after callbacks, run after the event; and around callbacks,
# blocks that surround the event, triggering it when they yield. Callback code
@@ -497,9 +500,7 @@ module ActiveSupport
arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
end
- def nested
- @nested
- end
+ attr_reader :nested
def final?
!@call_template
@@ -578,7 +579,7 @@ module ActiveSupport
end
protected
- def chain; @chain; end
+ attr_reader :chain
private
@@ -659,9 +660,17 @@ module ActiveSupport
# * <tt>:if</tt> - A symbol or an array of symbols, each naming an instance
# method or a proc; the callback will be called only when they all return
# a true value.
+ #
+ # If a proc is given, its body is evaluated in the context of the
+ # current object. It can also optionally accept the current object as
+ # an argument.
# * <tt>:unless</tt> - A symbol or an array of symbols, each naming an
# instance method or a proc; the callback will be called only when they
# all return a false value.
+ #
+ # If a proc is given, its body is evaluated in the context of the
+ # current object. It can also optionally accept the current object as
+ # an argument.
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
# existing chain rather than appended.
def set_callback(name, *filter_list, &block)
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index b0a0d845e5..5d356a0ab6 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -125,9 +125,13 @@ module ActiveSupport
def included(base = nil, &block)
if base.nil?
- raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
-
- @_included_block = block
+ if instance_variable_defined?(:@_included_block)
+ if @_included_block.source_location != block.source_location
+ raise MultipleIncludedBlocks
+ end
+ else
+ @_included_block = block
+ end
else
super
end
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 4d6f7819bb..9acf674c40 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -2,8 +2,6 @@
require "active_support/concern"
require "active_support/ordered_options"
-require "active_support/core_ext/array/extract_options"
-require "active_support/core_ext/regexp"
module ActiveSupport
# Configurable provides a <tt>config</tt> method to store and retrieve
@@ -106,9 +104,7 @@ module ActiveSupport
# end
#
# User.hair_colors # => [:brown, :black, :blonde, :red]
- def config_accessor(*names)
- options = names.extract_options!
-
+ def config_accessor(*names, instance_reader: true, instance_writer: true, instance_accessor: true) # :doc:
names.each do |name|
raise NameError.new("invalid config attribute name") unless /\A[_A-Za-z]\w*\z/.match?(name)
@@ -118,9 +114,9 @@ module ActiveSupport
singleton_class.class_eval reader, __FILE__, reader_line
singleton_class.class_eval writer, __FILE__, writer_line
- unless options[:instance_accessor] == false
- class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
- class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
+ if instance_accessor
+ class_eval reader, __FILE__, reader_line if instance_reader
+ class_eval writer, __FILE__, writer_line if instance_writer
end
send("#{name}=", yield) if block_given?
end
diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb
index 6d83b76882..a2569c798b 100644
--- a/activesupport/lib/active_support/core_ext/array.rb
+++ b/activesupport/lib/active_support/core_ext/array.rb
@@ -3,6 +3,7 @@
require "active_support/core_ext/array/wrap"
require "active_support/core_ext/array/access"
require "active_support/core_ext/array/conversions"
+require "active_support/core_ext/array/extract"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/array/grouping"
require "active_support/core_ext/array/prepend_and_append"
diff --git a/activesupport/lib/active_support/core_ext/array/extract.rb b/activesupport/lib/active_support/core_ext/array/extract.rb
new file mode 100644
index 0000000000..cc5a8a3f88
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/array/extract.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class Array
+ # Removes and returns the elements for which the block returns a true value.
+ # If no block is given, an Enumerator is returned instead.
+ #
+ # numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ # odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
+ # numbers # => [0, 2, 4, 6, 8]
+ def extract!
+ return to_enum(:extract!) { size } unless block_given?
+
+ extracted_elements = []
+
+ reject! do |element|
+ extracted_elements << element if yield(element)
+ end
+
+ extracted_elements
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index 75e65337b7..56fb46a88d 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -3,7 +3,7 @@
class Class
begin
# Test if this Ruby supports each_object against singleton_class
- ObjectSpace.each_object(Numeric.singleton_class) {}
+ ObjectSpace.each_object(Numeric.singleton_class) { }
# Returns an array with all classes that are < than its receiver.
#
diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
index de13f00e60..05abd83221 100644
--- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
@@ -5,13 +5,13 @@ require "active_support/core_ext/object/try"
module DateAndTime
module Calculations
DAYS_INTO_WEEK = {
- monday: 0,
- tuesday: 1,
- wednesday: 2,
- thursday: 3,
- friday: 4,
- saturday: 5,
- sunday: 6
+ sunday: 0,
+ monday: 1,
+ tuesday: 2,
+ wednesday: 3,
+ thursday: 4,
+ friday: 5,
+ saturday: 6
}
WEEKEND_DAYS = [ 6, 0 ]
@@ -60,12 +60,12 @@ module DateAndTime
!WEEKEND_DAYS.include?(wday)
end
- # Returns true if the date/time before <tt>date_or_time</tt>.
+ # Returns true if the date/time falls before <tt>date_or_time</tt>.
def before?(date_or_time)
self < date_or_time
end
- # Returns true if the date/time after <tt>date_or_time</tt>.
+ # Returns true if the date/time falls after <tt>date_or_time</tt>.
def after?(date_or_time)
self > date_or_time
end
@@ -263,9 +263,8 @@ module DateAndTime
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
def days_to_week_start(start_day = Date.beginning_of_week)
- start_day_number = DAYS_INTO_WEEK[start_day]
- current_day_number = wday != 0 ? wday - 1 : 6
- (current_day_number - start_day_number) % 7
+ start_day_number = DAYS_INTO_WEEK.fetch(start_day)
+ (wday - start_day_number) % 7
end
# Returns a new date/time representing the start of this week on the given day.
@@ -346,8 +345,7 @@ module DateAndTime
# today.next_occurring(:monday) # => Mon, 18 Dec 2017
# today.next_occurring(:thursday) # => Thu, 21 Dec 2017
def next_occurring(day_of_week)
- current_day_number = wday != 0 ? wday - 1 : 6
- from_now = DAYS_INTO_WEEK.fetch(day_of_week) - current_day_number
+ from_now = DAYS_INTO_WEEK.fetch(day_of_week) - wday
from_now += 7 unless from_now > 0
advance(days: from_now)
end
@@ -358,8 +356,7 @@ module DateAndTime
# today.prev_occurring(:monday) # => Mon, 11 Dec 2017
# today.prev_occurring(:thursday) # => Thu, 07 Dec 2017
def prev_occurring(day_of_week)
- current_day_number = wday != 0 ? wday - 1 : 6
- ago = current_day_number - DAYS_INTO_WEEK.fetch(day_of_week)
+ ago = wday - DAYS_INTO_WEEK.fetch(day_of_week)
ago += 7 unless ago > 0
advance(days: -ago)
end
@@ -374,7 +371,7 @@ module DateAndTime
end
def days_span(day)
- (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
+ (DAYS_INTO_WEEK.fetch(day) - DAYS_INTO_WEEK.fetch(Date.beginning_of_week)) % 7
end
def copy_time_to(other)
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index e61b23f842..bc670c3e76 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -110,7 +110,7 @@ class DateTime
# instance time. Do not use this method in combination with x.months, use
# months_since instead!
def since(seconds)
- self + Rational(seconds.round, 86400)
+ self + Rational(seconds, 86400)
end
alias :in :since
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index edde4f46b9..d87d63f287 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -1,14 +1,21 @@
# frozen_string_literal: true
module Enumerable
+ INDEX_WITH_DEFAULT = Object.new
+ private_constant :INDEX_WITH_DEFAULT
+
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
# when we omit an identity.
+ # :stopdoc:
+
# We can't use Refinements here because Refinements with Module which will be prepended
# doesn't work well https://bugs.ruby-lang.org/issues/13446
- alias :_original_sum_with_required_identity :sum # :nodoc:
+ alias :_original_sum_with_required_identity :sum
private :_original_sum_with_required_identity
+ # :startdoc:
+
# Calculates a sum from the elements.
#
# payments.sum { |p| p.price * p.tax_rate }
@@ -37,10 +44,11 @@ module Enumerable
end
end
- # Convert an enumerable to a hash.
+ # Convert an enumerable to a hash keying it by the block return value.
#
# people.index_by(&:login)
# # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
+ #
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
# # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
def index_by
@@ -53,6 +61,26 @@ module Enumerable
end
end
+ # Convert an enumerable to a hash keying it with the enumerable items and with the values returned in the block.
+ #
+ # post = Post.new(title: "hey there", body: "what's up?")
+ #
+ # %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
+ # # => { title: "hey there", body: "what's up?" }
+ def index_with(default = INDEX_WITH_DEFAULT)
+ if block_given?
+ result = {}
+ each { |elem| result[elem] = yield(elem) }
+ result
+ elsif default != INDEX_WITH_DEFAULT
+ result = {}
+ each { |elem| result[elem] = default }
+ result
+ else
+ to_enum(:index_with) { size if respond_to?(:size) }
+ end
+ end
+
# Returns +true+ if the enumerable has more than 1 element. Functionally
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb
index 8e288833b6..9deceb1bb4 100644
--- a/activesupport/lib/active_support/core_ext/file/atomic.rb
+++ b/activesupport/lib/active_support/core_ext/file/atomic.rb
@@ -29,7 +29,7 @@ class File
old_stat = if exist?(file_name)
# Get original file permissions
stat(file_name)
- elsif temp_dir != dirname(file_name)
+ else
# If not possible, probe which are the default permissions in the
# destination directory.
probe_stat_in(dirname(file_name))
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index 2bd0a56ea4..e07a5498ff 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -27,8 +27,9 @@ class Hash
# Replaces the hash with only the given keys.
# Returns a hash containing the removed key/value pairs.
#
- # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
- # # => {:c=>3, :d=>4}
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
+ # hash.slice!(:a, :b) # => {:c=>3, :d=>4}
+ # hash # => {:a=>1, :b=>2}
def slice!(*keys)
omit = slice(*self.keys - keys)
hash = slice(*keys)
diff --git a/activesupport/lib/active_support/core_ext/integer/multiple.rb b/activesupport/lib/active_support/core_ext/integer/multiple.rb
index e7606662d3..bd57a909c5 100644
--- a/activesupport/lib/active_support/core_ext/integer/multiple.rb
+++ b/activesupport/lib/active_support/core_ext/integer/multiple.rb
@@ -7,6 +7,6 @@ class Integer
# 6.multiple_of?(5) # => false
# 10.multiple_of?(2) # => true
def multiple_of?(number)
- number != 0 ? self % number == 0 : zero?
+ number == 0 ? self == 0 : self % number == 0
end
end
diff --git a/activesupport/lib/active_support/core_ext/load_error.rb b/activesupport/lib/active_support/core_ext/load_error.rb
index 6b0dcab905..b81ed0605e 100644
--- a/activesupport/lib/active_support/core_ext/load_error.rb
+++ b/activesupport/lib/active_support/core_ext/load_error.rb
@@ -4,6 +4,6 @@ class LoadError
# Returns true if the given path name (except perhaps for the ".rb"
# extension) is the missing file which caused the exception to be raised.
def is_missing?(location)
- location.sub(/\.rb$/, "".freeze) == path.to_s.sub(/\.rb$/, "".freeze)
+ location.sub(/\.rb$/, "") == path.to_s.sub(/\.rb$/, "")
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
index 01fee0fb74..5850e0193f 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -1,8 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/array/extract_options"
-require "active_support/core_ext/regexp"
-
# Extends the module object with class/module and instance accessors for
# class/module attributes, just like the native attr* accessors for instance
# attributes.
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
index 4b9b6ea9bd..cb996e9e6d 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
@@ -1,8 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/array/extract_options"
-require "active_support/core_ext/regexp"
-
# Extends the module object with class/module and instance accessors for
# class/module attributes, just like the native attr* accessors for instance
# attributes, but does so on a per-thread basis.
@@ -36,9 +33,7 @@ class Module
# end
#
# Current.new.user # => NoMethodError
- def thread_mattr_reader(*syms) # :nodoc:
- options = syms.extract_options!
-
+ def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true) # :nodoc:
syms.each do |sym|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
@@ -50,7 +45,7 @@ class Module
end
EOS
- unless options[:instance_reader] == false || options[:instance_accessor] == false
+ if instance_reader && instance_accessor
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{sym}
self.class.#{sym}
@@ -79,8 +74,7 @@ class Module
# end
#
# Current.new.user = "DHH" # => NoMethodError
- def thread_mattr_writer(*syms) # :nodoc:
- options = syms.extract_options!
+ def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true) # :nodoc:
syms.each do |sym|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
@@ -92,7 +86,7 @@ class Module
end
EOS
- unless options[:instance_writer] == false || options[:instance_accessor] == false
+ if instance_writer && instance_accessor
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{sym}=(obj)
self.class.#{sym} = obj
@@ -137,14 +131,14 @@ class Module
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
#
# class Current
- # mattr_accessor :user, instance_accessor: false
+ # thread_mattr_accessor :user, instance_accessor: false
# end
#
# Current.new.user = "DHH" # => NoMethodError
# Current.new.user # => NoMethodError
- def thread_mattr_accessor(*syms)
- thread_mattr_reader(*syms)
- thread_mattr_writer(*syms)
+ def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true)
+ thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor)
+ thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
end
alias :thread_cattr_accessor :thread_mattr_accessor
end
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 7f42f44efb..3128539112 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "set"
-require "active_support/core_ext/regexp"
class Module
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb
index c5bb598bd1..9b6df40596 100644
--- a/activesupport/lib/active_support/core_ext/module/introspection.rb
+++ b/activesupport/lib/active_support/core_ext/module/introspection.rb
@@ -5,8 +5,8 @@ require "active_support/inflector"
class Module
# Returns the name of the module containing this one.
#
- # M::N.parent_name # => "M"
- def parent_name
+ # M::N.module_parent_name # => "M"
+ def module_parent_name
if defined?(@parent_name)
@parent_name
else
@@ -16,6 +16,14 @@ class Module
end
end
+ def parent_name
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `Module#parent_name` has been renamed to `module_parent_name`.
+ `parent_name` is deprecated and will be removed in Rails 6.1.
+ MSG
+ module_parent_name
+ end
+
# Returns the module which contains this one according to its name.
#
# module M
@@ -24,15 +32,23 @@ class Module
# end
# X = M::N
#
- # M::N.parent # => M
- # X.parent # => M
+ # M::N.module_parent # => M
+ # X.module_parent # => M
#
# The parent of top-level and anonymous modules is Object.
#
- # M.parent # => Object
- # Module.new.parent # => Object
+ # M.module_parent # => Object
+ # Module.new.module_parent # => Object
+ def module_parent
+ module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object
+ end
+
def parent
- parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `Module#parent` has been renamed to `module_parent`.
+ `parent` is deprecated and will be removed in Rails 6.1.
+ MSG
+ module_parent
end
# Returns all the parents of this module according to its name, ordered from
@@ -44,13 +60,13 @@ class Module
# end
# X = M::N
#
- # M.parents # => [Object]
- # M::N.parents # => [M, Object]
- # X.parents # => [M, Object]
- def parents
+ # M.module_parents # => [Object]
+ # M::N.module_parents # => [M, Object]
+ # X.module_parents # => [M, Object]
+ def module_parents
parents = []
- if parent_name
- parts = parent_name.split("::")
+ if module_parent_name
+ parts = module_parent_name.split("::")
until parts.empty?
parents << ActiveSupport::Inflector.constantize(parts * "::")
parts.pop
@@ -59,4 +75,12 @@ class Module
parents << Object unless parents.include? Object
parents
end
+
+ def parents
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `Module#parents` has been renamed to `module_parents`.
+ `parents` is deprecated and will be removed in Rails 6.1.
+ MSG
+ module_parents
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
index 7fcd0d0311..8acad6164a 100644
--- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
@@ -4,127 +4,129 @@ require "active_support/core_ext/big_decimal/conversions"
require "active_support/number_helper"
require "active_support/core_ext/module/deprecation"
-module ActiveSupport::NumericWithFormat
- # Provides options for converting numbers into formatted strings.
- # Options are provided for phone numbers, currency, percentage,
- # precision, positional notation, file size and pretty printing.
- #
- # ==== Options
- #
- # For details on which formats use which options, see ActiveSupport::NumberHelper
- #
- # ==== Examples
- #
- # Phone Numbers:
- # 5551234.to_s(:phone) # => "555-1234"
- # 1235551234.to_s(:phone) # => "123-555-1234"
- # 1235551234.to_s(:phone, area_code: true) # => "(123) 555-1234"
- # 1235551234.to_s(:phone, delimiter: ' ') # => "123 555 1234"
- # 1235551234.to_s(:phone, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
- # 1235551234.to_s(:phone, country_code: 1) # => "+1-123-555-1234"
- # 1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
- # # => "+1.123.555.1234 x 1343"
- #
- # Currency:
- # 1234567890.50.to_s(:currency) # => "$1,234,567,890.50"
- # 1234567890.506.to_s(:currency) # => "$1,234,567,890.51"
- # 1234567890.506.to_s(:currency, precision: 3) # => "$1,234,567,890.506"
- # 1234567890.506.to_s(:currency, locale: :fr) # => "1 234 567 890,51 €"
- # -1234567890.50.to_s(:currency, negative_format: '(%u%n)')
- # # => "($1,234,567,890.50)"
- # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '')
- # # => "&pound;1234567890,50"
- # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
- # # => "1234567890,50 &pound;"
- #
- # Percentage:
- # 100.to_s(:percentage) # => "100.000%"
- # 100.to_s(:percentage, precision: 0) # => "100%"
- # 1000.to_s(:percentage, delimiter: '.', separator: ',') # => "1.000,000%"
- # 302.24398923423.to_s(:percentage, precision: 5) # => "302.24399%"
- # 1000.to_s(:percentage, locale: :fr) # => "1 000,000%"
- # 100.to_s(:percentage, format: '%n %') # => "100.000 %"
- #
- # Delimited:
- # 12345678.to_s(:delimited) # => "12,345,678"
- # 12345678.05.to_s(:delimited) # => "12,345,678.05"
- # 12345678.to_s(:delimited, delimiter: '.') # => "12.345.678"
- # 12345678.to_s(:delimited, delimiter: ',') # => "12,345,678"
- # 12345678.05.to_s(:delimited, separator: ' ') # => "12,345,678 05"
- # 12345678.05.to_s(:delimited, locale: :fr) # => "12 345 678,05"
- # 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
- # # => "98 765 432,98"
- #
- # Rounded:
- # 111.2345.to_s(:rounded) # => "111.235"
- # 111.2345.to_s(:rounded, precision: 2) # => "111.23"
- # 13.to_s(:rounded, precision: 5) # => "13.00000"
- # 389.32314.to_s(:rounded, precision: 0) # => "389"
- # 111.2345.to_s(:rounded, significant: true) # => "111"
- # 111.2345.to_s(:rounded, precision: 1, significant: true) # => "100"
- # 13.to_s(:rounded, precision: 5, significant: true) # => "13.000"
- # 111.234.to_s(:rounded, locale: :fr) # => "111,234"
- # 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
- # # => "13"
- # 389.32314.to_s(:rounded, precision: 4, significant: true) # => "389.3"
- # 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
- # # => "1.111,23"
- #
- # Human-friendly size in Bytes:
- # 123.to_s(:human_size) # => "123 Bytes"
- # 1234.to_s(:human_size) # => "1.21 KB"
- # 12345.to_s(:human_size) # => "12.1 KB"
- # 1234567.to_s(:human_size) # => "1.18 MB"
- # 1234567890.to_s(:human_size) # => "1.15 GB"
- # 1234567890123.to_s(:human_size) # => "1.12 TB"
- # 1234567890123456.to_s(:human_size) # => "1.1 PB"
- # 1234567890123456789.to_s(:human_size) # => "1.07 EB"
- # 1234567.to_s(:human_size, precision: 2) # => "1.2 MB"
- # 483989.to_s(:human_size, precision: 2) # => "470 KB"
- # 1234567.to_s(:human_size, precision: 2, separator: ',') # => "1,2 MB"
- # 1234567890123.to_s(:human_size, precision: 5) # => "1.1228 TB"
- # 524288000.to_s(:human_size, precision: 5) # => "500 MB"
- #
- # Human-friendly format:
- # 123.to_s(:human) # => "123"
- # 1234.to_s(:human) # => "1.23 Thousand"
- # 12345.to_s(:human) # => "12.3 Thousand"
- # 1234567.to_s(:human) # => "1.23 Million"
- # 1234567890.to_s(:human) # => "1.23 Billion"
- # 1234567890123.to_s(:human) # => "1.23 Trillion"
- # 1234567890123456.to_s(:human) # => "1.23 Quadrillion"
- # 1234567890123456789.to_s(:human) # => "1230 Quadrillion"
- # 489939.to_s(:human, precision: 2) # => "490 Thousand"
- # 489939.to_s(:human, precision: 4) # => "489.9 Thousand"
- # 1234567.to_s(:human, precision: 4,
- # significant: false) # => "1.2346 Million"
- # 1234567.to_s(:human, precision: 1,
- # separator: ',',
- # significant: false) # => "1,2 Million"
- def to_s(format = nil, options = nil)
- case format
- when nil
- super()
- when Integer, String
- super(format)
- when :phone
- ActiveSupport::NumberHelper.number_to_phone(self, options || {})
- when :currency
- ActiveSupport::NumberHelper.number_to_currency(self, options || {})
- when :percentage
- ActiveSupport::NumberHelper.number_to_percentage(self, options || {})
- when :delimited
- ActiveSupport::NumberHelper.number_to_delimited(self, options || {})
- when :rounded
- ActiveSupport::NumberHelper.number_to_rounded(self, options || {})
- when :human
- ActiveSupport::NumberHelper.number_to_human(self, options || {})
- when :human_size
- ActiveSupport::NumberHelper.number_to_human_size(self, options || {})
- when Symbol
- super()
- else
- super(format)
+module ActiveSupport
+ module NumericWithFormat
+ # Provides options for converting numbers into formatted strings.
+ # Options are provided for phone numbers, currency, percentage,
+ # precision, positional notation, file size and pretty printing.
+ #
+ # ==== Options
+ #
+ # For details on which formats use which options, see ActiveSupport::NumberHelper
+ #
+ # ==== Examples
+ #
+ # Phone Numbers:
+ # 5551234.to_s(:phone) # => "555-1234"
+ # 1235551234.to_s(:phone) # => "123-555-1234"
+ # 1235551234.to_s(:phone, area_code: true) # => "(123) 555-1234"
+ # 1235551234.to_s(:phone, delimiter: ' ') # => "123 555 1234"
+ # 1235551234.to_s(:phone, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
+ # 1235551234.to_s(:phone, country_code: 1) # => "+1-123-555-1234"
+ # 1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
+ # # => "+1.123.555.1234 x 1343"
+ #
+ # Currency:
+ # 1234567890.50.to_s(:currency) # => "$1,234,567,890.50"
+ # 1234567890.506.to_s(:currency) # => "$1,234,567,890.51"
+ # 1234567890.506.to_s(:currency, precision: 3) # => "$1,234,567,890.506"
+ # 1234567890.506.to_s(:currency, locale: :fr) # => "1 234 567 890,51 €"
+ # -1234567890.50.to_s(:currency, negative_format: '(%u%n)')
+ # # => "($1,234,567,890.50)"
+ # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '')
+ # # => "&pound;1234567890,50"
+ # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
+ # # => "1234567890,50 &pound;"
+ #
+ # Percentage:
+ # 100.to_s(:percentage) # => "100.000%"
+ # 100.to_s(:percentage, precision: 0) # => "100%"
+ # 1000.to_s(:percentage, delimiter: '.', separator: ',') # => "1.000,000%"
+ # 302.24398923423.to_s(:percentage, precision: 5) # => "302.24399%"
+ # 1000.to_s(:percentage, locale: :fr) # => "1 000,000%"
+ # 100.to_s(:percentage, format: '%n %') # => "100.000 %"
+ #
+ # Delimited:
+ # 12345678.to_s(:delimited) # => "12,345,678"
+ # 12345678.05.to_s(:delimited) # => "12,345,678.05"
+ # 12345678.to_s(:delimited, delimiter: '.') # => "12.345.678"
+ # 12345678.to_s(:delimited, delimiter: ',') # => "12,345,678"
+ # 12345678.05.to_s(:delimited, separator: ' ') # => "12,345,678 05"
+ # 12345678.05.to_s(:delimited, locale: :fr) # => "12 345 678,05"
+ # 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
+ # # => "98 765 432,98"
+ #
+ # Rounded:
+ # 111.2345.to_s(:rounded) # => "111.235"
+ # 111.2345.to_s(:rounded, precision: 2) # => "111.23"
+ # 13.to_s(:rounded, precision: 5) # => "13.00000"
+ # 389.32314.to_s(:rounded, precision: 0) # => "389"
+ # 111.2345.to_s(:rounded, significant: true) # => "111"
+ # 111.2345.to_s(:rounded, precision: 1, significant: true) # => "100"
+ # 13.to_s(:rounded, precision: 5, significant: true) # => "13.000"
+ # 111.234.to_s(:rounded, locale: :fr) # => "111,234"
+ # 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
+ # # => "13"
+ # 389.32314.to_s(:rounded, precision: 4, significant: true) # => "389.3"
+ # 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
+ # # => "1.111,23"
+ #
+ # Human-friendly size in Bytes:
+ # 123.to_s(:human_size) # => "123 Bytes"
+ # 1234.to_s(:human_size) # => "1.21 KB"
+ # 12345.to_s(:human_size) # => "12.1 KB"
+ # 1234567.to_s(:human_size) # => "1.18 MB"
+ # 1234567890.to_s(:human_size) # => "1.15 GB"
+ # 1234567890123.to_s(:human_size) # => "1.12 TB"
+ # 1234567890123456.to_s(:human_size) # => "1.1 PB"
+ # 1234567890123456789.to_s(:human_size) # => "1.07 EB"
+ # 1234567.to_s(:human_size, precision: 2) # => "1.2 MB"
+ # 483989.to_s(:human_size, precision: 2) # => "470 KB"
+ # 1234567.to_s(:human_size, precision: 2, separator: ',') # => "1,2 MB"
+ # 1234567890123.to_s(:human_size, precision: 5) # => "1.1228 TB"
+ # 524288000.to_s(:human_size, precision: 5) # => "500 MB"
+ #
+ # Human-friendly format:
+ # 123.to_s(:human) # => "123"
+ # 1234.to_s(:human) # => "1.23 Thousand"
+ # 12345.to_s(:human) # => "12.3 Thousand"
+ # 1234567.to_s(:human) # => "1.23 Million"
+ # 1234567890.to_s(:human) # => "1.23 Billion"
+ # 1234567890123.to_s(:human) # => "1.23 Trillion"
+ # 1234567890123456.to_s(:human) # => "1.23 Quadrillion"
+ # 1234567890123456789.to_s(:human) # => "1230 Quadrillion"
+ # 489939.to_s(:human, precision: 2) # => "490 Thousand"
+ # 489939.to_s(:human, precision: 4) # => "489.9 Thousand"
+ # 1234567.to_s(:human, precision: 4,
+ # significant: false) # => "1.2346 Million"
+ # 1234567.to_s(:human, precision: 1,
+ # separator: ',',
+ # significant: false) # => "1,2 Million"
+ def to_s(format = nil, options = nil)
+ case format
+ when nil
+ super()
+ when Integer, String
+ super(format)
+ when :phone
+ ActiveSupport::NumberHelper.number_to_phone(self, options || {})
+ when :currency
+ ActiveSupport::NumberHelper.number_to_currency(self, options || {})
+ when :percentage
+ ActiveSupport::NumberHelper.number_to_percentage(self, options || {})
+ when :delimited
+ ActiveSupport::NumberHelper.number_to_delimited(self, options || {})
+ when :rounded
+ ActiveSupport::NumberHelper.number_to_rounded(self, options || {})
+ when :human
+ ActiveSupport::NumberHelper.number_to_human(self, options || {})
+ when :human_size
+ ActiveSupport::NumberHelper.number_to_human_size(self, options || {})
+ when Symbol
+ super()
+ else
+ super(format)
+ end
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb
index 2ca431ab10..f36fef6cc9 100644
--- a/activesupport/lib/active_support/core_ext/object/blank.rb
+++ b/activesupport/lib/active_support/core_ext/object/blank.rb
@@ -1,11 +1,10 @@
# frozen_string_literal: true
-require "active_support/core_ext/regexp"
require "concurrent/map"
class Object
# An object is blank if it's false, empty, or a whitespace string.
- # For example, +false+, '', ' ', +nil+, [], and {} are all blank.
+ # For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
#
# This simplifies
#
diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb
index abb461966a..bac6ff9c97 100644
--- a/activesupport/lib/active_support/core_ext/object/to_query.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_query.rb
@@ -75,11 +75,14 @@ class Hash
#
# This method is also aliased as +to_param+.
def to_query(namespace = nil)
- collect do |key, value|
+ query = collect do |key, value|
unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
end
- end.compact.sort! * "&"
+ end.compact
+
+ query.sort! unless namespace.to_s.include?("[]")
+ query.join("&")
end
alias_method :to_param, :to_query
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index c874691629..ef8a1f476d 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -4,19 +4,27 @@ require "delegate"
module ActiveSupport
module Tryable #:nodoc:
- def try(*a, &b)
- try!(*a, &b) if a.empty? || respond_to?(a.first)
+ def try(method_name = nil, *args, &b)
+ if method_name.nil? && block_given?
+ if b.arity == 0
+ instance_eval(&b)
+ else
+ yield self
+ end
+ elsif respond_to?(method_name)
+ public_send(method_name, *args, &b)
+ end
end
- def try!(*a, &b)
- if a.empty? && block_given?
+ def try!(method_name = nil, *args, &b)
+ if method_name.nil? && block_given?
if b.arity == 0
instance_eval(&b)
else
yield self
end
else
- public_send(*a, &b)
+ public_send(method_name, *args, &b)
end
end
end
@@ -135,14 +143,14 @@ class NilClass
#
# With +try+
# @person.try(:children).try(:first).try(:name)
- def try(*args)
+ def try(method_name = nil, *args)
nil
end
# Calling +try!+ on +nil+ always returns +nil+.
#
# nil.try!(:name) # => nil
- def try!(*args)
+ def try!(method_name = nil, *args)
nil
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb
index 2838fd76be..1d46add6e0 100644
--- a/activesupport/lib/active_support/core_ext/object/with_options.rb
+++ b/activesupport/lib/active_support/core_ext/object/with_options.rb
@@ -68,7 +68,7 @@ class Object
# You can access these methods using the class name instead:
#
# class Phone < ActiveRecord::Base
- # enum phone_number_type: [home: 0, office: 1, mobile: 2]
+ # enum phone_number_type: { home: 0, office: 1, mobile: 2 }
#
# with_options presence: true do
# validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
diff --git a/activesupport/lib/active_support/core_ext/range.rb b/activesupport/lib/active_support/core_ext/range.rb
index 4074e91d17..78814fd189 100644
--- a/activesupport/lib/active_support/core_ext/range.rb
+++ b/activesupport/lib/active_support/core_ext/range.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "active_support/core_ext/range/conversions"
-require "active_support/core_ext/range/include_range"
+require "active_support/core_ext/range/compare_range"
require "active_support/core_ext/range/include_time_with_zone"
require "active_support/core_ext/range/overlaps"
require "active_support/core_ext/range/each"
diff --git a/activesupport/lib/active_support/core_ext/range/compare_range.rb b/activesupport/lib/active_support/core_ext/range/compare_range.rb
new file mode 100644
index 0000000000..6f6d2a27bb
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/range/compare_range.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ module CompareWithRange
+ # Extends the default Range#=== to support range comparisons.
+ # (1..5) === (1..5) # => true
+ # (1..5) === (2..3) # => true
+ # (1..5) === (2..6) # => false
+ #
+ # The native Range#=== behavior is untouched.
+ # ('a'..'f') === ('c') # => true
+ # (5..9) === (11) # => false
+ def ===(value)
+ if value.is_a?(::Range)
+ # 1...10 includes 1..9 but it does not include 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.send(operator, last)
+ else
+ super
+ end
+ end
+
+ # Extends the default Range#include? to support range comparisons.
+ # (1..5).include?(1..5) # => true
+ # (1..5).include?(2..3) # => true
+ # (1..5).include?(2..6) # => false
+ #
+ # The native Range#include? behavior is untouched.
+ # ('a'..'f').include?('c') # => true
+ # (5..9).include?(11) # => false
+ def include?(value)
+ if value.is_a?(::Range)
+ # 1...10 includes 1..9 but it does not include 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.send(operator, last)
+ else
+ super
+ end
+ end
+
+ # Extends the default Range#cover? to support range comparisons.
+ # (1..5).cover?(1..5) # => true
+ # (1..5).cover?(2..3) # => true
+ # (1..5).cover?(2..6) # => false
+ #
+ # The native Range#cover? behavior is untouched.
+ # ('a'..'f').cover?('c') # => true
+ # (5..9).cover?(11) # => false
+ def cover?(value)
+ if value.is_a?(::Range)
+ # 1...10 covers 1..9 but it does not cover 1..10.
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
+ super(value.first) && value.last.send(operator, last)
+ else
+ super
+ end
+ end
+ end
+end
+
+Range.prepend(ActiveSupport::CompareWithRange)
diff --git a/activesupport/lib/active_support/core_ext/range/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb
index 8832fbcb3c..024e32db40 100644
--- a/activesupport/lib/active_support/core_ext/range/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/range/conversions.rb
@@ -1,39 +1,41 @@
# frozen_string_literal: true
-module ActiveSupport::RangeWithFormat
- RANGE_FORMATS = {
- db: -> (start, stop) do
- case start
- when String then "BETWEEN '#{start}' AND '#{stop}'"
+module ActiveSupport
+ module RangeWithFormat
+ RANGE_FORMATS = {
+ db: -> (start, stop) do
+ case start
+ when String then "BETWEEN '#{start}' AND '#{stop}'"
+ else
+ "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'"
+ end
+ end
+ }
+
+ # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
+ #
+ # range = (1..100) # => 1..100
+ #
+ # range.to_s # => "1..100"
+ # range.to_s(:db) # => "BETWEEN '1' AND '100'"
+ #
+ # == Adding your own range formats to to_s
+ # You can add your own formats to the Range::RANGE_FORMATS hash.
+ # Use the format name as the hash key and a Proc instance.
+ #
+ # # config/initializers/range_formats.rb
+ # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
+ def to_s(format = :default)
+ if formatter = RANGE_FORMATS[format]
+ formatter.call(first, last)
else
- "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'"
+ super()
end
end
- }
- # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
- #
- # range = (1..100) # => 1..100
- #
- # range.to_s # => "1..100"
- # range.to_s(:db) # => "BETWEEN '1' AND '100'"
- #
- # == Adding your own range formats to to_s
- # You can add your own formats to the Range::RANGE_FORMATS hash.
- # Use the format name as the hash key and a Proc instance.
- #
- # # config/initializers/range_formats.rb
- # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
- def to_s(format = :default)
- if formatter = RANGE_FORMATS[format]
- formatter.call(first, last)
- else
- super()
- end
+ alias_method :to_default_s, :to_s
+ alias_method :to_formatted_s, :to_s
end
-
- alias_method :to_default_s, :to_s
- alias_method :to_formatted_s, :to_s
end
Range.prepend(ActiveSupport::RangeWithFormat)
diff --git a/activesupport/lib/active_support/core_ext/range/include_range.rb b/activesupport/lib/active_support/core_ext/range/include_range.rb
index 7ba1011921..2da2c587a3 100644
--- a/activesupport/lib/active_support/core_ext/range/include_range.rb
+++ b/activesupport/lib/active_support/core_ext/range/include_range.rb
@@ -1,25 +1,9 @@
# frozen_string_literal: true
-module ActiveSupport
- module IncludeWithRange #:nodoc:
- # Extends the default Range#include? to support range comparisons.
- # (1..5).include?(1..5) # => true
- # (1..5).include?(2..3) # => true
- # (1..5).include?(2..6) # => false
- #
- # The native Range#include? behavior is untouched.
- # ('a'..'f').include?('c') # => true
- # (5..9).include?(11) # => false
- def include?(value)
- if value.is_a?(::Range)
- # 1...10 includes 1..9 but it does not include 1..10.
- operator = exclude_end? && !value.exclude_end? ? :< : :<=
- super(value.first) && value.last.send(operator, last)
- else
- super
- end
- end
- end
-end
+require "active_support/deprecation"
-Range.prepend(ActiveSupport::IncludeWithRange)
+ActiveSupport::Deprecation.warn "You have required `active_support/core_ext/range/include_range`. " \
+"This file will be removed in Rails 6.1. You should require `active_support/core_ext/range/compare_range` " \
+ "instead."
+
+require "active_support/core_ext/range/compare_range"
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index 58591bbaaf..4ca24028b0 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -75,6 +75,10 @@ class String
# str.first(0) # => ""
# str.first(6) # => "hello"
def first(limit = 1)
+ ActiveSupport::Deprecation.warn(
+ "Calling String#first with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ ) if limit < 0
if limit == 0
""
elsif limit >= size
@@ -95,6 +99,10 @@ class String
# str.last(0) # => ""
# str.last(6) # => "hello"
def last(limit = 1)
+ ActiveSupport::Deprecation.warn(
+ "Calling String#last with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ ) if limit < 0
if limit == 0
""
elsif limit >= size
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index f3bdc2977e..3a80de4617 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -134,8 +134,9 @@ end
module ActiveSupport #:nodoc:
class SafeBuffer < String
UNSAFE_STRING_METHODS = %w(
- capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
- slice squeeze strip sub succ swapcase tr tr_s upcase
+ capitalize chomp chop delete delete_prefix delete_suffix
+ downcase gsub lstrip next reverse rstrip slice squeeze strip
+ sub succ swapcase tr tr_s unicode_normalize upcase
)
alias_method :original_concat, :concat
@@ -149,9 +150,7 @@ module ActiveSupport #:nodoc:
end
def [](*args)
- if args.size < 2
- super
- elsif html_safe?
+ if html_safe?
new_safe_buffer = super
if new_safe_buffer
@@ -188,10 +187,22 @@ module ActiveSupport #:nodoc:
end
alias << concat
+ def insert(index, value)
+ super(index, html_escape_interpolated_argument(value))
+ end
+
def prepend(value)
super(html_escape_interpolated_argument(value))
end
+ def replace(value)
+ super(html_escape_interpolated_argument(value))
+ end
+
+ def []=(index, value)
+ super(index, html_escape_interpolated_argument(value))
+ end
+
def +(other)
dup.concat(other)
end
diff --git a/activesupport/lib/active_support/core_ext/string/strip.rb b/activesupport/lib/active_support/core_ext/string/strip.rb
index 6f9834bb16..60e9952ee6 100644
--- a/activesupport/lib/active_support/core_ext/string/strip.rb
+++ b/activesupport/lib/active_support/core_ext/string/strip.rb
@@ -20,7 +20,7 @@ class String
# Technically, it looks for the least indented non-empty line
# in the whole string, and removes that amount of leading whitespace.
def strip_heredoc
- gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze).tap do |stripped|
+ gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "").tap do |stripped|
stripped.freeze if frozen?
end
end
diff --git a/activesupport/lib/active_support/current_attributes.rb b/activesupport/lib/active_support/current_attributes.rb
index 4e6d8e4585..3145ff87a1 100644
--- a/activesupport/lib/active_support/current_attributes.rb
+++ b/activesupport/lib/active_support/current_attributes.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/callbacks"
+
module ActiveSupport
# Abstract super class that provides a thread-isolated attributes singleton, which resets automatically
# before and after each request. This allows you to keep all the per-request attributes easily
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 0f59558bb5..d5d00b5e6e 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -79,6 +79,12 @@ module ActiveSupport #:nodoc:
# to allow arbitrary constants to be marked for unloading.
mattr_accessor :explicitly_unloadable_constants, default: []
+ # The logger used when tracing autoloads.
+ mattr_accessor :logger
+
+ # If true, trace autoloads with +logger.debug+.
+ mattr_accessor :verbose, default: false
+
# The WatchStack keeps a stack of the modules being watched as files are
# loaded. If a file in the process of being loaded (parent.rb) triggers the
# load of another file (child.rb) the stack will ensure that child.rb
@@ -97,6 +103,8 @@ module ActiveSupport #:nodoc:
# parent.rb then requires namespace/child.rb, the stack will look like
# [[Object], [Namespace]].
+ attr_reader :watching
+
def initialize
@watching = []
@stack = Hash.new { |h, k| h[k] = [] }
@@ -138,7 +146,7 @@ module ActiveSupport #:nodoc:
# Normalize the list of new constants, and add them to the list we will return
new_constants.each do |suffix|
- constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
+ constants << ([namespace, suffix] - ["Object"]).join("::")
end
end
constants
@@ -224,6 +232,8 @@ module ActiveSupport #:nodoc:
Dependencies.require_or_load(file_name)
end
+ # :doc:
+
# Interprets a file using <tt>mechanism</tt> and marks its defined
# constants as autoloaded. <tt>file_name</tt> can be either a string or
# respond to <tt>to_path</tt>.
@@ -242,9 +252,13 @@ module ActiveSupport #:nodoc:
Dependencies.depend_on(file_name, message)
end
+ # :nodoc:
+
def load_dependency(file)
if Dependencies.load? && Dependencies.constant_watch_stack.watching?
- Dependencies.new_constants_in(Object) { yield }
+ descs = Dependencies.constant_watch_stack.watching.flatten.uniq
+
+ Dependencies.new_constants_in(*descs) { yield }
else
yield
end
@@ -341,7 +355,7 @@ module ActiveSupport #:nodoc:
end
def require_or_load(file_name, const_path = nil)
- file_name = $` if file_name =~ /\.rb\z/
+ file_name = file_name.chomp(".rb")
expanded = File.expand_path(file_name)
return if loaded.include?(expanded)
@@ -391,7 +405,7 @@ module ActiveSupport #:nodoc:
# constant paths which would cause Dependencies to attempt to load this
# file.
def loadable_constants_for_path(path, bases = autoload_paths)
- path = $` if path =~ /\.rb\z/
+ path = path.chomp(".rb")
expanded_path = File.expand_path(path)
paths = []
@@ -400,7 +414,7 @@ module ActiveSupport #:nodoc:
next unless expanded_path.start_with?(expanded_root)
root_size = expanded_root.size
- next if expanded_path[root_size] != ?/.freeze
+ next if expanded_path[root_size] != ?/
nesting = expanded_path[(root_size + 1)..-1]
paths << nesting.camelize unless nesting.blank?
@@ -412,7 +426,7 @@ module ActiveSupport #:nodoc:
# Search for a file in autoload_paths matching the provided suffix.
def search_for_file(path_suffix)
- path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb".freeze)
+ path_suffix += ".rb" unless path_suffix.ends_with?(".rb")
autoload_paths.each do |root|
path = File.join(root, path_suffix)
@@ -446,6 +460,7 @@ module ActiveSupport #:nodoc:
return nil unless base_path = autoloadable_module?(path_suffix)
mod = Module.new
into.const_set const_name, mod
+ log("constant #{qualified_name} autoloaded (module autovivified from #{File.join(base_path, path_suffix)})")
autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
autoloaded_constants.uniq!
mod
@@ -487,26 +502,31 @@ module ActiveSupport #:nodoc:
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
end
- qualified_name = qualified_name_for from_mod, const_name
+ qualified_name = qualified_name_for(from_mod, const_name)
path_suffix = qualified_name.underscore
file_path = search_for_file(path_suffix)
if file_path
expanded = File.expand_path(file_path)
- expanded.sub!(/\.rb\z/, "".freeze)
+ expanded.sub!(/\.rb\z/, "")
if loading.include?(expanded)
raise "Circular dependency detected while autoloading constant #{qualified_name}"
else
require_or_load(expanded, qualified_name)
- raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
- return from_mod.const_get(const_name)
+
+ if from_mod.const_defined?(const_name, false)
+ log("constant #{qualified_name} autoloaded from #{expanded}.rb")
+ return from_mod.const_get(const_name)
+ else
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it"
+ end
end
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
return mod
- elsif (parent = from_mod.parent) && parent != from_mod &&
- ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
+ elsif (parent = from_mod.module_parent) && parent != from_mod &&
+ ! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) }
# If our parents do not have a constant named +const_name+ then we are free
# to attempt to load upwards. If they do have such a constant, then this
# const_missing must be due to from_mod::const_name, which should not
@@ -550,6 +570,7 @@ module ActiveSupport #:nodoc:
# as the environment will be in an inconsistent state, e.g. other constants
# may have already been unloaded and not accessible.
def remove_unloadable_constants!
+ log("removing unloadable constants")
autoloaded_constants.each { |const| remove_constant const }
autoloaded_constants.clear
Reference.clear!
@@ -739,6 +760,10 @@ module ActiveSupport #:nodoc:
# The constant is no longer reachable, just skip it.
end
end
+
+ def log(message)
+ logger.debug("autoloading: #{message}") if logger && verbose
+ end
end
end
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index 66d6f3225a..725667d139 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -43,7 +43,7 @@ module ActiveSupport
deprecation_horizon: deprecation_horizon)
},
- silence: ->(message, callstack, deprecation_horizon, gem_name) {},
+ silence: ->(message, callstack, deprecation_horizon, gem_name) { },
}
# Behavior module allows to determine how to display deprecation messages.
@@ -94,6 +94,10 @@ module ActiveSupport
private
def arity_coerce(behavior)
+ unless behavior.respond_to?(:call)
+ raise ArgumentError, "#{behavior.inspect} is not a valid deprecation behavior."
+ end
+
if behavior.arity == 4 || behavior.arity == -1
behavior
else
diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb
index 5be893d281..f0c3e37e65 100644
--- a/activesupport/lib/active_support/deprecation/method_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/module/aliasing"
require "active_support/core_ext/array/extract_options"
module ActiveSupport
@@ -53,24 +52,37 @@ module ActiveSupport
options = method_names.extract_options!
deprecator = options.delete(:deprecator) || self
method_names += options.keys
+ mod = Module.new
- mod = Module.new do
- method_names.each do |method_name|
- define_method(method_name) do |*args, &block|
+ method_names.each do |method_name|
+ if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name)
+ aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ""), $1
+ with_method = "#{aliased_method}_with_deprecation#{punctuation}"
+ without_method = "#{aliased_method}_without_deprecation#{punctuation}"
+
+ target_module.send(:define_method, with_method) do |*args, &block|
deprecator.deprecation_warning(method_name, options[method_name])
- super(*args, &block)
+ send(without_method, *args, &block)
end
+ target_module.send(:alias_method, without_method, method_name)
+ target_module.send(:alias_method, method_name, with_method)
+
case
- when target_module.protected_method_defined?(method_name)
- protected method_name
- when target_module.private_method_defined?(method_name)
- private method_name
+ when target_module.protected_method_defined?(without_method)
+ target_module.send(:protected, method_name)
+ when target_module.private_method_defined?(without_method)
+ target_module.send(:private, method_name)
+ end
+ else
+ mod.send(:define_method, method_name) do |*args, &block|
+ deprecator.deprecation_warning(method_name, options[method_name])
+ super(*args, &block)
end
end
end
- target_module.prepend(mod)
+ target_module.prepend(mod) unless mod.instance_methods(false).empty?
end
end
end
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index 896c0d2d8e..56f1e23136 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/regexp"
-
module ActiveSupport
class Deprecation
class DeprecationProxy #:nodoc:
diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb
index a4cee788b6..05236d3162 100644
--- a/activesupport/lib/active_support/descendants_tracker.rb
+++ b/activesupport/lib/active_support/descendants_tracker.rb
@@ -38,12 +38,13 @@ module ActiveSupport
end
private
- def accumulate_descendants(klass, acc)
- if direct_descendants = @@direct_descendants[klass]
- acc.concat(direct_descendants)
- direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
+
+ def accumulate_descendants(klass, acc)
+ if direct_descendants = @@direct_descendants[klass]
+ acc.concat(direct_descendants)
+ direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
+ end
end
- end
end
def inherited(base)
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 88897f811e..314c926ac0 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -373,7 +373,6 @@ module ActiveSupport
return "0 seconds" if parts.empty?
parts.
- reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
sort_by { |unit, _ | PARTS.index(unit) }.
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
to_sentence(locale: ::I18n.default_locale)
diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb
index 1847eeaa86..d3233e6111 100644
--- a/activesupport/lib/active_support/duration/iso8601_parser.rb
+++ b/activesupport/lib/active_support/duration/iso8601_parser.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "strscan"
-require "active_support/core_ext/regexp"
module ActiveSupport
class Duration
@@ -14,8 +13,8 @@ module ActiveSupport
class ParsingError < ::ArgumentError; end
PERIOD_OR_COMMA = /\.|,/
- PERIOD = ".".freeze
- COMMA = ",".freeze
+ PERIOD = "."
+ COMMA = ","
SIGN_MARKER = /\A\-|\+|/
DATE_MARKER = /P/
diff --git a/activesupport/lib/active_support/duration/iso8601_serializer.rb b/activesupport/lib/active_support/duration/iso8601_serializer.rb
index 84ae29c1ec..1125454919 100644
--- a/activesupport/lib/active_support/duration/iso8601_serializer.rb
+++ b/activesupport/lib/active_support/duration/iso8601_serializer.rb
@@ -14,14 +14,14 @@ module ActiveSupport
# Builds and returns output string.
def serialize
parts, sign = normalize
- return "PT0S".freeze if parts.empty?
+ return "PT0S" if parts.empty?
- output = "P".dup
+ output = +"P"
output << "#{parts[:years]}Y" if parts.key?(:years)
output << "#{parts[:months]}M" if parts.key?(:months)
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
output << "#{parts[:days]}D" if parts.key?(:days)
- time = "".dup
+ time = +""
time << "#{parts[:hours]}H" if parts.key?(:hours)
time << "#{parts[:minutes]}M" if parts.key?(:minutes)
if parts.key?(:seconds)
diff --git a/activesupport/lib/active_support/encrypted_configuration.rb b/activesupport/lib/active_support/encrypted_configuration.rb
index 3c6da10548..cc1d026737 100644
--- a/activesupport/lib/active_support/encrypted_configuration.rb
+++ b/activesupport/lib/active_support/encrypted_configuration.rb
@@ -39,7 +39,7 @@ module ActiveSupport
end
def deserialize(config)
- config.present? ? YAML.load(config, content_path) : {}
+ YAML.load(config).presence || {}
end
end
end
diff --git a/activesupport/lib/active_support/execution_wrapper.rb b/activesupport/lib/active_support/execution_wrapper.rb
index f48c586cad..ca810db584 100644
--- a/activesupport/lib/active_support/execution_wrapper.rb
+++ b/activesupport/lib/active_support/execution_wrapper.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support/callbacks"
+require "concurrent/hash"
module ActiveSupport
class ExecutionWrapper
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index e4afc8af93..f1af76019a 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -2,6 +2,7 @@
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/hash/reverse_merge"
+require "active_support/core_ext/hash/except"
module ActiveSupport
# Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
@@ -279,6 +280,8 @@ module ActiveSupport
super(convert_key(key))
end
+ alias_method :without, :except
+
def stringify_keys!; self end
def deep_stringify_keys!; self end
def stringify_keys; dup end
@@ -286,6 +289,7 @@ module ActiveSupport
undef :symbolize_keys!
undef :deep_symbolize_keys!
def symbolize_keys; to_hash.symbolize_keys! end
+ alias_method :to_options, :symbolize_keys
def deep_symbolize_keys; to_hash.deep_symbolize_keys! end
def to_options!; self end
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index ce8bfbfd8c..584930e413 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "active_support"
-require "active_support/file_update_checker"
require "active_support/core_ext/array/wrap"
# :enddoc:
@@ -88,9 +87,21 @@ module I18n
when Hash, Array
Array.wrap(fallbacks)
else # TrueClass
- []
+ [I18n.default_locale]
end
+ if args.empty? || args.first.is_a?(Hash)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Using I18n fallbacks with an empty `defaults` sets the defaults to
+ include the `default_locale`. This behavior will change in Rails 6.1.
+ If you desire the default locale to be included in the defaults, please
+ explicitly configure it with `config.i18n.fallbacks.defaults =
+ [I18n.default_locale]` or `config.i18n.fallbacks = [I18n.default_locale,
+ {...}]`
+ MSG
+ args.unshift I18n.default_locale
+ end
+
I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
end
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index 7e5dff1d6d..2b86d233e5 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -2,7 +2,6 @@
require "concurrent/map"
require "active_support/core_ext/array/prepend_and_append"
-require "active_support/core_ext/regexp"
require "active_support/i18n"
require "active_support/deprecation"
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 339b93b8da..1af9833d46 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "active_support/inflections"
-require "active_support/core_ext/regexp"
module ActiveSupport
# The Inflector transforms words from singular to plural, class names to table
@@ -74,7 +73,7 @@ module ActiveSupport
string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
end
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
- string.gsub!("/".freeze, "::".freeze)
+ string.gsub!("/", "::")
string
end
@@ -91,11 +90,11 @@ module ActiveSupport
# camelize(underscore('SSLError')) # => "SslError"
def underscore(camel_cased_word)
return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
- word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
- word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'.freeze }#{$2.downcase}" }
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
- word.tr!("-".freeze, "_".freeze)
+ word = camel_cased_word.to_s.gsub("::", "/")
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
+ word.tr!("-", "_")
word.downcase!
word
end
@@ -131,11 +130,11 @@ module ActiveSupport
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
- result.sub!(/\A_+/, "".freeze)
+ result.sub!(/\A_+/, "")
unless keep_id_suffix
- result.sub!(/_id\z/, "".freeze)
+ result.sub!(/_id\z/, "")
end
- result.tr!("_".freeze, " ".freeze)
+ result.tr!("_", " ")
result.gsub!(/([a-z\d]*)/i) do |match|
"#{inflections.acronyms[match.downcase] || match.downcase}"
@@ -200,14 +199,14 @@ module ActiveSupport
# classify('calculus') # => "Calculus"
def classify(table_name)
# strip out any leading schema name
- camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
end
# Replaces underscores with dashes in the string.
#
# dasherize('puni_puni') # => "puni-puni"
def dasherize(underscored_word)
- underscored_word.tr("_".freeze, "-".freeze)
+ underscored_word.tr("_", "-")
end
# Removes the module part from the expression in the string.
@@ -270,7 +269,7 @@ module ActiveSupport
# NameError is raised when the name is not in CamelCase or the constant is
# unknown.
def constantize(camel_cased_word)
- names = camel_cased_word.split("::".freeze)
+ names = camel_cased_word.split("::")
# Trigger a built-in NameError exception including the ill-formed constant in the message.
Object.const_get(camel_cased_word) if names.empty?
@@ -365,7 +364,7 @@ module ActiveSupport
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
# const_regexp("::") # => "::"
def const_regexp(camel_cased_word)
- parts = camel_cased_word.split("::".freeze)
+ parts = camel_cased_word.split("::")
return Regexp.escape(camel_cased_word) if parts.blank?
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index 6f2ca4999c..0d2a17970f 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -58,13 +58,13 @@ module ActiveSupport
# I18n.locale = :de
# transliterate('Jürgen')
# # => "Juergen"
- def transliterate(string, replacement = "?".freeze)
+ def transliterate(string, replacement = "?")
raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
I18n.transliterate(
- ActiveSupport::Multibyte::Unicode.normalize(
- ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
- replacement: replacement)
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
+ replacement: replacement
+ )
end
# Replaces special characters in a string so that it may be used as part of
@@ -97,7 +97,7 @@ module ActiveSupport
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
unless separator.nil? || separator.empty?
- if separator == "-".freeze
+ if separator == "-"
re_duplicate_separator = /-{2,}/
re_leading_trailing_separator = /^-|-$/i
else
@@ -108,7 +108,7 @@ module ActiveSupport
# No more than one of the separator in a row.
parameterized_string.gsub!(re_duplicate_separator, separator)
# Remove leading/trailing separator.
- parameterized_string.gsub!(re_leading_trailing_separator, "".freeze)
+ parameterized_string.gsub!(re_leading_trailing_separator, "")
end
parameterized_string.downcase! unless preserve_case
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index 8c0e016dc5..402a3fbe60 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -45,32 +45,32 @@ module ActiveSupport
private
- def convert_dates_from(data)
- case data
- when nil
- nil
- when DATE_REGEX
- begin
- Date.parse(data)
- rescue ArgumentError
+ def convert_dates_from(data)
+ case data
+ when nil
+ nil
+ when DATE_REGEX
+ begin
+ Date.parse(data)
+ rescue ArgumentError
+ data
+ end
+ when DATETIME_REGEX
+ begin
+ Time.zone.parse(data)
+ rescue ArgumentError
+ data
+ end
+ when Array
+ data.map! { |d| convert_dates_from(d) }
+ when Hash
+ data.each do |key, value|
+ data[key] = convert_dates_from(value)
+ end
+ else
data
end
- when DATETIME_REGEX
- begin
- Time.zone.parse(data)
- rescue ArgumentError
- data
- end
- when Array
- data.map! { |d| convert_dates_from(d) }
- when Hash
- data.each do |key, value|
- data[key] = convert_dates_from(value)
- end
- else
- data
end
- end
end
end
end
diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb
index 78f7d7ca8d..00edcdd05a 100644
--- a/activesupport/lib/active_support/key_generator.rb
+++ b/activesupport/lib/active_support/key_generator.rb
@@ -59,7 +59,7 @@ module ActiveSupport
if secret.blank?
raise ArgumentError, "A secret is required to generate an integrity hash " \
"for cookie session data. Set a secret_key_base of at least " \
- "#{SECRET_MIN_LENGTH} characters in via `bin/rails credentials:edit`."
+ "#{SECRET_MIN_LENGTH} characters by running `rails credentials:edit`."
end
if secret.length < SECRET_MIN_LENGTH
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index dc8080c469..a6b096a973 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -68,7 +68,11 @@ module ActiveSupport
if options[:yield]
block.call(base)
else
- base.instance_eval(&block)
+ if base.is_a?(Module)
+ base.class_eval(&block)
+ else
+ base.instance_eval(&block)
+ end
end
end
end
diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb
index 8152a182b4..b8555c887b 100644
--- a/activesupport/lib/active_support/logger.rb
+++ b/activesupport/lib/active_support/logger.rb
@@ -6,7 +6,6 @@ require "logger"
module ActiveSupport
class Logger < ::Logger
- include ActiveSupport::LoggerThreadSafeLevel
include LoggerSilence
# Returns true if the logger destination matches one of the sources
@@ -81,20 +80,6 @@ module ActiveSupport
def initialize(*args)
super
@formatter = SimpleFormatter.new
- after_initialize if respond_to? :after_initialize
- end
-
- def add(severity, message = nil, progname = nil, &block)
- return true if @logdev.nil? || (severity || UNKNOWN) < level
- super
- end
-
- Logger::Severity.constants.each do |severity|
- class_eval(<<-EOT, __FILE__, __LINE__ + 1)
- def #{severity.downcase}? # def debug?
- Logger::#{severity} >= level # DEBUG >= level
- end # end
- EOT
end
# Simple formatter which only displays the message.
diff --git a/activesupport/lib/active_support/logger_silence.rb b/activesupport/lib/active_support/logger_silence.rb
index 89f32b6782..b2444c1e34 100644
--- a/activesupport/lib/active_support/logger_silence.rb
+++ b/activesupport/lib/active_support/logger_silence.rb
@@ -2,28 +2,44 @@
require "active_support/concern"
require "active_support/core_ext/module/attribute_accessors"
-require "concurrent"
+require "active_support/logger_thread_safe_level"
module LoggerSilence
extend ActiveSupport::Concern
included do
- cattr_accessor :silencer, default: true
+ ActiveSupport::Deprecation.warn(
+ "Including LoggerSilence is deprecated and will be removed in Rails 6.1. " \
+ "Please use `ActiveSupport::LoggerSilence` instead"
+ )
+
+ include ActiveSupport::LoggerSilence
end
+end
+
+module ActiveSupport
+ module LoggerSilence
+ extend ActiveSupport::Concern
+
+ included do
+ cattr_accessor :silencer, default: true
+ include ActiveSupport::LoggerThreadSafeLevel
+ end
- # Silences the logger for the duration of the block.
- def silence(temporary_level = Logger::ERROR)
- if silencer
- begin
- old_local_level = local_level
- self.local_level = temporary_level
+ # Silences the logger for the duration of the block.
+ def silence(temporary_level = Logger::ERROR)
+ if silencer
+ begin
+ old_local_level = local_level
+ self.local_level = temporary_level
+ yield self
+ ensure
+ self.local_level = old_local_level
+ end
+ else
yield self
- ensure
- self.local_level = old_local_level
end
- else
- yield self
end
end
end
diff --git a/activesupport/lib/active_support/logger_thread_safe_level.rb b/activesupport/lib/active_support/logger_thread_safe_level.rb
index ba32813d3d..f16c90cfc6 100644
--- a/activesupport/lib/active_support/logger_thread_safe_level.rb
+++ b/activesupport/lib/active_support/logger_thread_safe_level.rb
@@ -1,13 +1,30 @@
# frozen_string_literal: true
require "active_support/concern"
+require "active_support/core_ext/module/attribute_accessors"
+require "concurrent"
module ActiveSupport
module LoggerThreadSafeLevel # :nodoc:
extend ActiveSupport::Concern
+ included do
+ cattr_accessor :local_levels, default: Concurrent::Map.new(initial_capacity: 2), instance_accessor: false
+ end
+
+ Logger::Severity.constants.each do |severity|
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
+ def #{severity.downcase}? # def debug?
+ Logger::#{severity} >= level # DEBUG >= level
+ end # end
+ EOT
+ end
+
def after_initialize
- @local_levels = Concurrent::Map.new(initial_capacity: 2)
+ ActiveSupport::Deprecation.warn(
+ "Logger don't need to call #after_initialize directly anymore. It will be deprecated without replacement in " \
+ "Rails 6.1."
+ )
end
def local_log_id
@@ -15,19 +32,24 @@ module ActiveSupport
end
def local_level
- @local_levels[local_log_id]
+ self.class.local_levels[local_log_id]
end
def local_level=(level)
if level
- @local_levels[local_log_id] = level
+ self.class.local_levels[local_log_id] = level
else
- @local_levels.delete(local_log_id)
+ self.class.local_levels.delete(local_log_id)
end
end
def level
local_level || super
end
+
+ def add(severity, message = nil, progname = nil, &block) # :nodoc:
+ return true if @logdev.nil? || (severity || UNKNOWN) < level
+ super
+ end
end
end
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 8b73270894..6f7302e732 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -182,7 +182,7 @@ module ActiveSupport
def _decrypt(encrypted_message, purpose)
cipher = new_cipher
- encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }
+ encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) }
# Currently the OpenSSL bindings do not raise an error if auth_tag is
# truncated, which would allow an attacker to easily forge it. See
@@ -210,9 +210,7 @@ module ActiveSupport
OpenSSL::Cipher.new(@cipher)
end
- def verifier
- @verifier
- end
+ attr_reader :verifier
def aead_mode?
@aead_mode ||= new_cipher.authenticated?
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 83c39c0a86..64c557bec6 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -122,7 +122,7 @@ module ActiveSupport
def valid_message?(signed_message)
return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
- data, digest = signed_message.split("--".freeze)
+ data, digest = signed_message.split("--")
data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
end
@@ -150,7 +150,7 @@ module ActiveSupport
def verified(signed_message, purpose: nil, **)
if valid_message?(signed_message)
begin
- data = signed_message.split("--".freeze)[0]
+ data = signed_message.split("--")[0]
message = Messages::Metadata.verify(decode(data), purpose)
@serializer.load(message) if message
rescue ArgumentError => argument_error
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 8152b8fd22..a1e23aeaca 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -4,7 +4,6 @@ require "active_support/json"
require "active_support/core_ext/string/access"
require "active_support/core_ext/string/behavior"
require "active_support/core_ext/module/delegation"
-require "active_support/core_ext/regexp"
module ActiveSupport #:nodoc:
module Multibyte #:nodoc:
@@ -18,7 +17,7 @@ module ActiveSupport #:nodoc:
# through the +mb_chars+ method. Methods which would normally return a
# String object now return a Chars object so methods can be chained.
#
- # 'The Perfect String '.mb_chars.downcase.strip.normalize
+ # 'The Perfect String '.mb_chars.downcase.strip
# # => #<ActiveSupport::Multibyte::Chars:0x007fdc434ccc10 @wrapped_string="the perfect string">
#
# Chars objects are perfectly interchangeable with String objects as long as
@@ -77,6 +76,11 @@ module ActiveSupport #:nodoc:
# Returns +true+ when the proxy class can handle the string. Returns
# +false+ otherwise.
def self.consumes?(string)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Chars.consumes? is deprecated and will be
+ removed from Rails 6.1. Use string.is_utf8? instead.
+ MSG
+
string.encoding == Encoding::UTF_8
end
@@ -109,7 +113,7 @@ module ActiveSupport #:nodoc:
#
# 'Café'.mb_chars.reverse.to_s # => 'éfaC'
def reverse
- chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack("U*"))
+ chars(@wrapped_string.scan(/\X/).reverse.join)
end
# Limits the byte size of the string to a number of bytes without breaking
@@ -118,35 +122,7 @@ module ActiveSupport #:nodoc:
#
# 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
def limit(limit)
- slice(0...translate_offset(limit))
- end
-
- # Converts characters in the string to uppercase.
- #
- # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
- def upcase
- chars Unicode.upcase(@wrapped_string)
- end
-
- # Converts characters in the string to lowercase.
- #
- # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
- def downcase
- chars Unicode.downcase(@wrapped_string)
- end
-
- # Converts characters in the string to the opposite case.
- #
- # 'El Cañón'.mb_chars.swapcase.to_s # => "eL cAÑÓN"
- def swapcase
- chars Unicode.swapcase(@wrapped_string)
- end
-
- # Converts the first character to uppercase and the remainder to lowercase.
- #
- # 'über'.mb_chars.capitalize.to_s # => "Über"
- def capitalize
- (slice(0) || chars("")).upcase + (slice(1..-1) || chars("")).downcase
+ truncate_bytes(limit, omission: nil)
end
# Capitalizes the first letter of every word, when possible.
@@ -154,7 +130,7 @@ module ActiveSupport #:nodoc:
# "ÉL QUE SE ENTERÓ".mb_chars.titleize.to_s # => "Él Que Se Enteró"
# "日本語".mb_chars.titleize.to_s # => "日本語"
def titleize
- chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1) })
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { $1.upcase })
end
alias_method :titlecase, :titleize
@@ -166,7 +142,24 @@ module ActiveSupport #:nodoc:
# <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
# ActiveSupport::Multibyte::Unicode.default_normalization_form
def normalize(form = nil)
- chars(Unicode.normalize(@wrapped_string, form))
+ form ||= Unicode.default_normalization_form
+
+ # See https://www.unicode.org/reports/tr15, Table 1
+ if alias_form = Unicode::NORMALIZATION_FORM_ALIASES[form]
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
+ removed from Rails 6.1. Use #unicode_normalize(:#{alias_form}) instead.
+ MSG
+
+ send(:unicode_normalize, alias_form)
+ else
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
+ removed from Rails 6.1. Use #unicode_normalize instead.
+ MSG
+
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
+ end
end
# Performs canonical decomposition on all the characters.
@@ -190,7 +183,7 @@ module ActiveSupport #:nodoc:
# 'क्षि'.mb_chars.length # => 4
# 'क्षि'.mb_chars.grapheme_length # => 3
def grapheme_length
- Unicode.unpack_graphemes(@wrapped_string).length
+ @wrapped_string.scan(/\X/).length
end
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
@@ -206,7 +199,7 @@ module ActiveSupport #:nodoc:
to_s.as_json(options)
end
- %w(capitalize downcase reverse tidy_bytes upcase).each do |method|
+ %w(reverse tidy_bytes).each do |method|
define_method("#{method}!") do |*args|
@wrapped_string = send(method, *args).to_s
self
@@ -215,18 +208,6 @@ module ActiveSupport #:nodoc:
private
- def translate_offset(byte_offset)
- return nil if byte_offset.nil?
- return 0 if @wrapped_string == ""
-
- begin
- @wrapped_string.byteslice(0...byte_offset).unpack("U*").length
- rescue ArgumentError
- byte_offset -= 1
- retry
- end
- end
-
def chars(string)
self.class.new(string)
end
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 4f0e1165ef..ce8ecece69 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -6,10 +6,17 @@ module ActiveSupport
extend self
# A list of all available normalization forms.
- # See http://www.unicode.org/reports/tr15/tr15-29.html for more
+ # See https://www.unicode.org/reports/tr15/tr15-29.html for more
# information about normalization.
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
+ NORMALIZATION_FORM_ALIASES = { # :nodoc:
+ c: :nfc,
+ d: :nfd,
+ kc: :nfkc,
+ kd: :nfkd
+ }
+
# The Unicode version that is supported by the implementation
UNICODE_VERSION = RbConfig::CONFIG["UNICODE_VERSION"]
@@ -27,6 +34,11 @@ module ActiveSupport
# Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
# Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
def unpack_graphemes(string)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#unpack_graphemes is deprecated and will be
+ removed from Rails 6.1. Use string.scan(/\X/).map(&:codepoints) instead.
+ MSG
+
string.scan(/\X/).map(&:codepoints)
end
@@ -34,6 +46,11 @@ module ActiveSupport
#
# Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
def pack_graphemes(unpacked)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#pack_graphemes is deprecated and will be
+ removed from Rails 6.1. Use array.flatten.pack("U*") instead.
+ MSG
+
unpacked.flatten.pack("U*")
end
@@ -100,31 +117,34 @@ module ActiveSupport
# Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
def normalize(string, form = nil)
form ||= @default_normalization_form
- # See http://www.unicode.org/reports/tr15, Table 1
- case form
- when :d
- string.unicode_normalize(:nfd)
- when :c
- string.unicode_normalize(:nfc)
- when :kd
- string.unicode_normalize(:nfkd)
- when :kc
- string.unicode_normalize(:nfkc)
+
+ # See https://www.unicode.org/reports/tr15, Table 1
+ if alias_form = NORMALIZATION_FORM_ALIASES[form]
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#normalize is deprecated and will be
+ removed from Rails 6.1. Use String#unicode_normalize(:#{alias_form}) instead.
+ MSG
+
+ string.unicode_normalize(alias_form)
else
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#normalize is deprecated and will be
+ removed from Rails 6.1. Use String#unicode_normalize instead.
+ MSG
+
raise ArgumentError, "#{form} is not a valid normalization variant", caller
end
end
- def downcase(string)
- string.downcase
- end
+ %w(downcase upcase swapcase).each do |method|
+ define_method(method) do |string|
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode##{method} is deprecated and
+ will be removed from Rails 6.1. Use String methods directly.
+ MSG
- def upcase(string)
- string.upcase
- end
-
- def swapcase(string)
- string.swapcase
+ string.send(method)
+ end
end
private
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 6207de8094..01cc363e2b 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -34,7 +34,7 @@ module ActiveSupport
# name # => String, name of the event (such as 'render' from above)
# start # => Time, when the instrumented block started execution
# finish # => Time, when the instrumented block ended execution
- # id # => String, unique ID for this notification
+ # id # => String, unique ID for the instrumenter that fired the event
# payload # => Hash, the payload
# end
#
@@ -59,7 +59,7 @@ module ActiveSupport
# event.payload # => { extra: :information }
#
# The block in the <tt>subscribe</tt> call gets the name of the event, start
- # timestamp, end timestamp, a string with a unique identifier for that event
+ # timestamp, end timestamp, a string with a unique identifier for that event's instrumenter
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
# that order.
#
@@ -171,6 +171,24 @@ module ActiveSupport
end
end
+ # Subscribe to a given event name with the passed +block+.
+ #
+ # You can subscribe to events by passing a String to match exact event
+ # names, or by passing a Regexp to match all events that match a pattern.
+ #
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
+ # ...
+ # end
+ #
+ # The +block+ will receive five parameters with information about the event:
+ #
+ # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
+ # name # => String, name of the event (such as 'render' from above)
+ # start # => Time, when the instrumented block started execution
+ # finish # => Time, when the instrumented block ended execution
+ # id # => String, unique ID for the instrumenter that fired the event
+ # payload # => Hash, the payload
+ # end
def subscribe(*args, &block)
notifier.subscribe(*args, &block)
end
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 25aab175b4..4e4ca70942 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -70,12 +70,29 @@ module ActiveSupport
module Subscribers # :nodoc:
def self.new(pattern, listener)
+ subscriber_class = Timed
+
if listener.respond_to?(:start) && listener.respond_to?(:finish)
- subscriber = Evented.new pattern, listener
+ subscriber_class = Evented
else
- subscriber = Timed.new pattern, listener
+ # Doing all this to detect a block like `proc { |x| }` vs
+ # `proc { |*x| }` or `proc { |**x| }`
+ if listener.respond_to?(:parameters)
+ params = listener.parameters
+ if params.length == 1 && params.first.first == :opt
+ subscriber_class = EventObject
+ end
+ end
end
+ wrap_all pattern, subscriber_class.new(pattern, listener)
+ end
+
+ def self.event_object_subscriber(pattern, block)
+ wrap_all pattern, EventObject.new(pattern, block)
+ end
+
+ def self.wrap_all(pattern, subscriber)
unless pattern
AllMessages.new(subscriber)
else
@@ -130,6 +147,27 @@ module ActiveSupport
end
end
+ class EventObject < Evented
+ def start(name, id, payload)
+ stack = Thread.current[:_event_stack] ||= []
+ event = build_event name, id, payload
+ event.start!
+ stack.push event
+ end
+
+ def finish(name, id, payload)
+ stack = Thread.current[:_event_stack]
+ event = stack.pop
+ event.finish!
+ @delegate.call event
+ end
+
+ private
+ def build_event(name, id, payload)
+ ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
+ end
+ end
+
class AllMessages # :nodoc:
def initialize(delegate)
@delegate = delegate
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index e99f5ee688..125c06f37a 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -52,8 +52,13 @@ module ActiveSupport
end
class Event
- attr_reader :name, :time, :transaction_id, :payload, :children
- attr_accessor :end
+ attr_reader :name, :time, :end, :transaction_id, :payload, :children
+
+ def self.clock_gettime_supported? # :nodoc:
+ defined?(Process::CLOCK_PROCESS_CPUTIME_ID) &&
+ !Gem.win_platform?
+ end
+ private_class_method :clock_gettime_supported?
def initialize(name, start, ending, transaction_id, payload)
@name = name
@@ -63,6 +68,47 @@ module ActiveSupport
@end = ending
@children = []
@duration = nil
+ @cpu_time_start = nil
+ @cpu_time_finish = nil
+ @allocation_count_start = 0
+ @allocation_count_finish = 0
+ end
+
+ # Record information at the time this event starts
+ def start!
+ @time = now
+ @cpu_time_start = now_cpu
+ @allocation_count_start = now_allocations
+ end
+
+ # Record information at the time this event finishes
+ def finish!
+ @cpu_time_finish = now_cpu
+ @end = now
+ @allocation_count_finish = now_allocations
+ end
+
+ def end=(ending)
+ ActiveSupport::Deprecation.deprecation_warning(:end=, :finish!)
+ @end = ending
+ end
+
+ # Returns the CPU time (in milliseconds) passed since the call to
+ # +start!+ and the call to +finish!+
+ def cpu_time
+ (@cpu_time_finish - @cpu_time_start) * 1000
+ end
+
+ # Returns the idle time time (in milliseconds) passed since the call to
+ # +start!+ and the call to +finish!+
+ def idle_time
+ duration - cpu_time
+ end
+
+ # Returns the number of allocations made since the call to +start!+ and
+ # the call to +finish!+
+ def allocations
+ @allocation_count_finish - @allocation_count_start
end
# Returns the difference in milliseconds between when the execution of the
@@ -88,6 +134,31 @@ module ActiveSupport
def parent_of?(event)
@children.include? event
end
+
+ private
+ def now
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end
+
+ if clock_gettime_supported?
+ def now_cpu
+ Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
+ end
+ else
+ def now_cpu
+ 0
+ end
+ end
+
+ if defined?(JRUBY_VERSION)
+ def now_allocations
+ 0
+ end
+ else
+ def now_allocations
+ GC.stat :total_allocated_objects
+ end
+ end
end
end
end
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index 8fd6e932f1..d19a2f64d4 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/dependencies/autoload"
+
module ActiveSupport
module NumberHelper
extend ActiveSupport::Autoload
@@ -85,6 +87,9 @@ module ActiveSupport
# number given by <tt>:format</tt>). Accepts the same fields
# than <tt>:format</tt>, except <tt>%n</tt> is here the
# absolute value of the number.
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
+ # insignificant zeros after the decimal separator (defaults to
+ # +false+).
#
# ==== Examples
#
@@ -100,6 +105,8 @@ module ActiveSupport
# # => "&pound;1234567890,50"
# number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
# # => "1234567890,50 &pound;"
+ # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
+ # # => "$1,234,567,890.5"
def number_to_currency(number, options = {})
NumberToCurrencyConverter.convert(number, options)
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
index a25e22cbd3..0e8ae82dd5 100644
--- a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToCurrencyConverter < NumberConverter # :nodoc:
@@ -15,7 +17,7 @@ module ActiveSupport
end
rounded_number = NumberToRoundedConverter.convert(number, options)
- format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, options[:unit])
+ format.gsub("%n", rounded_number).gsub("%u", options[:unit])
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
index d5b5706705..467a580a2e 100644
--- a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToDelimitedConverter < NumberConverter #:nodoc:
@@ -14,7 +16,7 @@ module ActiveSupport
private
def parts
- left, right = number.to_s.split(".".freeze)
+ left, right = number.to_s.split(".")
left.gsub!(delimiter_pattern) do |digit_to_delimit|
"#{digit_to_delimit}#{options[:delimiter]}"
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_human_converter.rb b/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
index 03eb6671ec..494408fc01 100644
--- a/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToHumanConverter < NumberConverter # :nodoc:
@@ -25,7 +27,7 @@ module ActiveSupport
rounded_number = NumberToRoundedConverter.convert(number, options)
unit = determine_unit(units, exponent)
- format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, unit).strip
+ format.gsub("%n", rounded_number).gsub("%u", unit).strip
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
index 842f2fc8df..91262fa656 100644
--- a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToHumanSizeConverter < NumberConverter #:nodoc:
@@ -22,7 +24,7 @@ module ActiveSupport
human_size = number / (base**exponent)
number_to_format = NumberToRoundedConverter.convert(human_size, options)
end
- conversion_format.gsub("%n".freeze, number_to_format).gsub("%u".freeze, unit)
+ conversion_format.gsub("%n", number_to_format).gsub("%u", unit)
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb b/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
index 4dcdad2e2c..0c2e190f8a 100644
--- a/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToPercentageConverter < NumberConverter # :nodoc:
@@ -7,7 +9,7 @@ module ActiveSupport
def convert
rounded_number = NumberToRoundedConverter.convert(number, options)
- options[:format].gsub("%n".freeze, rounded_number)
+ options[:format].gsub("%n", rounded_number)
end
end
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb b/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb
index 96410f4995..d5e72981b4 100644
--- a/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToPhoneConverter < NumberConverter #:nodoc:
diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
index eb528a0583..6ceb9a572e 100644
--- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/number_helper/number_converter"
+
module ActiveSupport
module NumberHelper
class NumberToRoundedConverter < NumberConverter # :nodoc:
@@ -20,9 +22,9 @@ module ActiveSupport
formatted_string =
if BigDecimal === rounded_number && rounded_number.finite?
s = rounded_number.to_s("F")
- s << "0".freeze * precision
- a, b = s.split(".".freeze, 2)
- a << ".".freeze
+ s << "0" * precision
+ a, b = s.split(".", 2)
+ a << "."
a << b[0, precision]
else
"%00.#{precision}f" % rounded_number
diff --git a/activesupport/lib/active_support/parameter_filter.rb b/activesupport/lib/active_support/parameter_filter.rb
new file mode 100644
index 0000000000..1389d82523
--- /dev/null
+++ b/activesupport/lib/active_support/parameter_filter.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/duplicable"
+require "active_support/core_ext/array/extract"
+
+module ActiveSupport
+ # +ParameterFilter+ allows you to specify keys for sensitive data from
+ # hash-like object and replace corresponding value. Filtering only certain
+ # sub-keys from a hash is possible by using the dot notation:
+ # 'credit_card.number'. If a proc is given, each key and value of a hash and
+ # all sub-hashes are passed to it, where the value or the key can be replaced
+ # using String#replace or similar methods.
+ #
+ # ActiveSupport::ParameterFilter.new([:password])
+ # => replaces the value to all keys matching /password/i with "[FILTERED]"
+ #
+ # ActiveSupport::ParameterFilter.new([:foo, "bar"])
+ # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
+ #
+ # ActiveSupport::ParameterFilter.new(["credit_card.code"])
+ # => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
+ # change { file: { code: "xxxx"} }
+ #
+ # ActiveSupport::ParameterFilter.new([-> (k, v) do
+ # v.reverse! if k =~ /secret/i
+ # end])
+ # => reverses the value to all keys matching /secret/i
+ class ParameterFilter
+ FILTERED = "[FILTERED]" # :nodoc:
+
+ # Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
+ # Other types of filters are treated as +String+ using +to_s+.
+ # For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
+ #
+ # ==== Options
+ #
+ # * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
+ def initialize(filters = [], mask: FILTERED)
+ @filters = filters
+ @mask = mask
+ end
+
+ # Mask value of +params+ if key matches one of filters.
+ def filter(params)
+ compiled_filter.call(params)
+ end
+
+ # Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
+ def filter_param(key, value)
+ @filters.empty? ? value : compiled_filter.value_for_key(key, value)
+ end
+
+ private
+
+ def compiled_filter
+ @compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
+ end
+
+ class CompiledFilter # :nodoc:
+ def self.compile(filters, mask:)
+ return lambda { |params| params.dup } if filters.empty?
+
+ strings, regexps, blocks = [], [], []
+
+ filters.each do |item|
+ case item
+ when Proc
+ blocks << item
+ when Regexp
+ regexps << item
+ else
+ strings << Regexp.escape(item.to_s)
+ end
+ end
+
+ deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.") }
+ deep_strings = strings.extract! { |s| s.include?("\\.") }
+
+ regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
+ deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
+
+ new regexps, deep_regexps, blocks, mask: mask
+ end
+
+ attr_reader :regexps, :deep_regexps, :blocks
+
+ def initialize(regexps, deep_regexps, blocks, mask:)
+ @regexps = regexps
+ @deep_regexps = deep_regexps.any? ? deep_regexps : nil
+ @blocks = blocks
+ @mask = mask
+ end
+
+ def call(params, parents = [], original_params = params)
+ filtered_params = params.class.new
+
+ params.each do |key, value|
+ filtered_params[key] = value_for_key(key, value, parents, original_params)
+ end
+
+ filtered_params
+ end
+
+ def value_for_key(key, value, parents = [], original_params = nil)
+ parents.push(key) if deep_regexps
+ if regexps.any? { |r| r.match?(key) }
+ value = @mask
+ elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
+ value = @mask
+ elsif value.is_a?(Hash)
+ value = call(value, parents, original_params)
+ elsif value.is_a?(Array)
+ value = value.map { |v| v.is_a?(Hash) ? call(v, parents, original_params) : v }
+ elsif blocks.any?
+ key = key.dup if key.duplicable?
+ value = value.dup if value.duplicable?
+ blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
+ end
+ parents.pop if deep_regexps
+ value
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/reloader.rb b/activesupport/lib/active_support/reloader.rb
index b26d9c3665..fea18e9712 100644
--- a/activesupport/lib/active_support/reloader.rb
+++ b/activesupport/lib/active_support/reloader.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support/execution_wrapper"
+require "active_support/executor"
module ActiveSupport
#--
diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb
index 8ad39f7a05..f3e902f9dd 100644
--- a/activesupport/lib/active_support/subscriber.rb
+++ b/activesupport/lib/active_support/subscriber.rb
@@ -79,25 +79,24 @@ module ActiveSupport
end
def start(name, id, payload)
- e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
+ event = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload)
+ event.start!
parent = event_stack.last
- parent << e if parent
+ parent << event if parent
- event_stack.push e
+ event_stack.push event
end
def finish(name, id, payload)
- finished = Time.now
- event = event_stack.pop
- event.end = finished
+ event = event_stack.pop
+ event.finish!
event.payload.merge!(payload)
- method = name.split(".".freeze).first
+ method = name.split(".").first
send(method, event)
end
private
-
def event_stack
SubscriberQueueRegistry.instance.get_queue(@queue_key)
end
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index b069ac94d4..d8a86d997e 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -46,7 +46,7 @@ module ActiveSupport
def current_tags
# We use our object ID here to avoid conflicting with other instances
- thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}".freeze
+ thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
Thread.current[thread_key] ||= []
end
@@ -61,8 +61,15 @@ module ActiveSupport
end
def self.new(logger)
- # Ensure we set a default formatter so we aren't extending nil!
- logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
+ logger = logger.dup
+
+ if logger.formatter
+ logger.formatter = logger.formatter.dup
+ else
+ # Ensure we set a default formatter so we aren't extending nil!
+ logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
+ end
+
logger.formatter.extend Formatter
logger.extend(self)
end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index f17743b6db..ef12c6b9b0 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -65,8 +65,8 @@ module ActiveSupport
#
# parallelize(workers: 2, with: :threads)
#
- # The threaded parallelization uses Minitest's parallel executor directly.
- # The processes parallelization uses a Ruby Drb server.
+ # The threaded parallelization uses minitest's parallel executor directly.
+ # The processes parallelization uses a Ruby DRb server.
def parallelize(workers: 2, with: :processes)
workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index a891ff616d..b27ac7ce99 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -113,11 +113,23 @@ module ActiveSupport
# post :create, params: { article: invalid_attributes }
# end
#
+ # A lambda can be passed in and evaluated.
+ #
+ # assert_no_difference -> { Article.count } do
+ # post :create, params: { article: invalid_attributes }
+ # end
+ #
# An error message can be specified.
#
# assert_no_difference 'Article.count', 'An Article should not be created' do
# post :create, params: { article: invalid_attributes }
# end
+ #
+ # An array of expressions can also be passed in and evaluated.
+ #
+ # assert_no_difference [ 'Article.count', -> { Post.count } ] do
+ # post :create, params: { article: invalid_attributes }
+ # end
def assert_no_difference(expression, message = nil, &block)
assert_difference expression, 0, message, &block
end
@@ -176,7 +188,9 @@ module ActiveSupport
assert before != after, error
unless to == UNTRACKED
- error = "#{expression.inspect} didn't change to #{to}"
+ error = "#{expression.inspect} didn't change to as expected\n"
+ error = "#{error}Expected: #{to.inspect}\n"
+ error = "#{error} Actual: #{after.inspect}"
error = "#{message}.\n#{error}" if message
assert to === after, error
end
diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb
index f655435729..18d63d2780 100644
--- a/activesupport/lib/active_support/testing/deprecation.rb
+++ b/activesupport/lib/active_support/testing/deprecation.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "active_support/deprecation"
-require "active_support/core_ext/regexp"
module ActiveSupport
module Testing
diff --git a/activesupport/lib/active_support/testing/file_fixtures.rb b/activesupport/lib/active_support/testing/file_fixtures.rb
index ad923d1aab..4eb7a88576 100644
--- a/activesupport/lib/active_support/testing/file_fixtures.rb
+++ b/activesupport/lib/active_support/testing/file_fixtures.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/concern"
+
module ActiveSupport
module Testing
# Adds simple access to sample files called file fixtures.
diff --git a/activesupport/lib/active_support/testing/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb
index c6358002ea..eba41aa907 100644
--- a/activesupport/lib/active_support/testing/method_call_assertions.rb
+++ b/activesupport/lib/active_support/testing/method_call_assertions.rb
@@ -17,7 +17,7 @@ module ActiveSupport
assert_equal times, times_called, error
end
- def assert_called_with(object, method_name, args = [], returns: nil)
+ def assert_called_with(object, method_name, args, returns: nil)
mock = Minitest::Mock.new
if args.all? { |arg| arg.is_a?(Array) }
@@ -35,6 +35,35 @@ module ActiveSupport
assert_called(object, method_name, message, times: 0, &block)
end
+ # TODO: No need to resort to #send once support for Ruby 2.4 is
+ # dropped.
+ def assert_called_on_instance_of(klass, method_name, message = nil, times: 1, returns: nil)
+ times_called = 0
+ klass.send(:define_method, "stubbed_#{method_name}") do |*|
+ times_called += 1
+
+ returns
+ end
+
+ klass.send(:alias_method, "original_#{method_name}", method_name)
+ klass.send(:alias_method, method_name, "stubbed_#{method_name}")
+
+ yield
+
+ error = "Expected #{method_name} to be called #{times} times, but was called #{times_called} times"
+ error = "#{message}.\n#{error}" if message
+
+ assert_equal times, times_called, error
+ ensure
+ klass.send(:alias_method, method_name, "original_#{method_name}")
+ klass.send(:undef_method, "original_#{method_name}")
+ klass.send(:undef_method, "stubbed_#{method_name}")
+ end
+
+ def assert_not_called_on_instance_of(klass, method_name, message = nil, &block)
+ assert_called_on_instance_of(klass, method_name, message, times: 0, &block)
+ end
+
def stub_any_instance(klass, instance: klass.new)
klass.stub(:new, instance) { yield instance }
end
diff --git a/activesupport/lib/active_support/testing/parallelization.rb b/activesupport/lib/active_support/testing/parallelization.rb
index 59c8486f41..c5d3a88131 100644
--- a/activesupport/lib/active_support/testing/parallelization.rb
+++ b/activesupport/lib/active_support/testing/parallelization.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
require "drb"
-require "drb/unix"
+require "drb/unix" unless Gem.win_platform?
+require "active_support/core_ext/module/attribute_accessors"
module ActiveSupport
module Testing
@@ -14,37 +15,36 @@ module ActiveSupport
end
def record(reporter, result)
+ raise DRb::DRbConnError if result.is_a?(DRb::DRbUnknown)
+
reporter.synchronize do
reporter.record(result)
end
end
def <<(o)
+ o[2] = DRbObject.new(o[2]) if o
@queue << o
end
def pop; @queue.pop; end
end
- @after_fork_hooks = []
+ @@after_fork_hooks = []
def self.after_fork_hook(&blk)
- @after_fork_hooks << blk
+ @@after_fork_hooks << blk
end
- def self.after_fork_hooks
- @after_fork_hooks
- end
+ cattr_reader :after_fork_hooks
- @run_cleanup_hooks = []
+ @@run_cleanup_hooks = []
def self.run_cleanup_hook(&blk)
- @run_cleanup_hooks << blk
+ @@run_cleanup_hooks << blk
end
- def self.run_cleanup_hooks
- @run_cleanup_hooks
- end
+ cattr_reader :run_cleanup_hooks
def initialize(queue_size)
@queue_size = queue_size
@@ -69,22 +69,31 @@ module ActiveSupport
def start
@pool = @queue_size.times.map do |worker|
fork do
- DRb.stop_service
-
- after_fork(worker)
-
- queue = DRbObject.new_with_uri(@url)
-
- while job = queue.pop
- klass = job[0]
- method = job[1]
- reporter = job[2]
- result = Minitest.run_one_method(klass, method)
-
- queue.record(reporter, result)
+ begin
+ DRb.stop_service
+
+ after_fork(worker)
+
+ queue = DRbObject.new_with_uri(@url)
+
+ while job = queue.pop
+ klass = job[0]
+ method = job[1]
+ reporter = job[2]
+ result = Minitest.run_one_method(klass, method)
+
+ begin
+ queue.record(reporter, result)
+ rescue DRb::DRbConnError
+ result.failures.each do |failure|
+ failure.exception = DRb::DRbRemoteError.new(failure.exception)
+ end
+ queue.record(reporter, result)
+ end
+ end
+ ensure
+ run_cleanup(worker)
end
-
- run_cleanup(worker)
end
end
end
diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb
index 801ea2909b..f160e66971 100644
--- a/activesupport/lib/active_support/testing/time_helpers.rb
+++ b/activesupport/lib/active_support/testing/time_helpers.rb
@@ -158,7 +158,7 @@ module ActiveSupport
end
# Returns the current time back to its original state, by removing the stubs added by
- # +travel+ and +travel_to+.
+ # +travel+, +travel_to+, and +freeze_time+.
#
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
# travel_to Time.zone.local(2004, 11, 24, 01, 04, 44)
@@ -168,6 +168,7 @@ module ActiveSupport
def travel_back
simple_stubs.unstub_all!
end
+ alias_method :unfreeze_time, :travel_back
# Calls +travel_to+ with +Time.now+.
#
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 7e71318404..3be5f6f7b5 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -43,8 +43,8 @@ module ActiveSupport
"Time"
end
- PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N".freeze }
- PRECISIONS[0] = "%FT%T".freeze
+ PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N" }
+ PRECISIONS[0] = "%FT%T"
include Comparable, DateAndTime::Compatibility
attr_reader :time_zone
@@ -147,7 +147,7 @@ module ActiveSupport
#
# Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
def xmlschema(fraction_digits = 0)
- "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}"
+ "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z')}"
end
alias_method :iso8601, :xmlschema
alias_method :rfc3339, :xmlschema
@@ -286,8 +286,10 @@ module ActiveSupport
alias_method :since, :+
alias_method :in, :+
- # Returns a new TimeWithZone object that represents the difference between
- # the current object's time and the +other+ time.
+ # Subtracts an interval of time and returns a new TimeWithZone object unless
+ # the other value `acts_like?` time. Then it will return a Float of the difference
+ # between the two times that represents the difference between the current
+ # object's time and the +other+ time.
#
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
# now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
@@ -302,6 +304,12 @@ module ActiveSupport
#
# now - 24.hours # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
# now - 1.day # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
+ #
+ # If both the TimeWithZone object and the other value act like Time, a Float
+ # will be returned.
+ #
+ # Time.zone.now - 1.day.ago # => 86399.999967
+ #
def -(other)
if other.acts_like?(:time)
to_time - other.to_time
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 5f709c5fd9..d9e033e23b 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -182,8 +182,9 @@ module ActiveSupport
"Samoa" => "Pacific/Apia"
}
- UTC_OFFSET_WITH_COLON = "%s%02d:%02d"
- UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "")
+ UTC_OFFSET_WITH_COLON = "%s%02d:%02d" # :nodoc:
+ UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "") # :nodoc:
+ private_constant :UTC_OFFSET_WITH_COLON, :UTC_OFFSET_WITHOUT_COLON
@lazy_zones_map = Concurrent::Map.new
@country_zones = Concurrent::Map.new
@@ -265,7 +266,7 @@ module ActiveSupport
private
def load_country_zones(code)
country = TZInfo::Country.get(code)
- country.zone_identifiers.map do |tz_id|
+ country.zone_identifiers.flat_map do |tz_id|
if MAPPING.value?(tz_id)
MAPPING.inject([]) do |memo, (key, value)|
memo << self[key] if value == tz_id
@@ -274,7 +275,7 @@ module ActiveSupport
else
create(tz_id, nil, TZInfo::Timezone.new(tz_id))
end
- end.flatten(1).sort!
+ end.sort!
end
def zones_map
@@ -354,8 +355,13 @@ module ActiveSupport
# Time.zone = 'Hawaii' # => "Hawaii"
# Time.utc(2000).to_f # => 946684800.0
# Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
- def at(secs)
- Time.at(secs).utc.in_time_zone(self)
+ #
+ # A second argument can be supplied to specify sub-second precision.
+ #
+ # Time.zone = 'Hawaii' # => "Hawaii"
+ # Time.at(946684800, 123456.789).nsec # => 123456789
+ def at(*args)
+ Time.at(*args).utc.in_time_zone(self)
end
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index e42eee07a3..be298bf0a1 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -71,7 +71,7 @@ module ActiveSupport
begin
BigDecimal(number)
rescue ArgumentError
- BigDecimal("0")
+ BigDecimal(number.to_f.to_s)
end
else
BigDecimal(number)
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 7f94a64016..32fe6ade28 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -18,7 +18,7 @@ module ActiveSupport
module XmlMini_JDOM #:nodoc:
extend self
- CONTENT_KEY = "__content__".freeze
+ CONTENT_KEY = "__content__"
NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE
DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE
@@ -169,7 +169,7 @@ module ActiveSupport
# element::
# XML element to be checked.
def empty_content?(element)
- text = "".dup
+ text = +""
child_nodes = element.child_nodes
(0...child_nodes.length).each do |i|
item = child_nodes.item(i)
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index 0b000fea60..c2e999ef6c 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -34,7 +34,7 @@ module LibXML #:nodoc:
end
module Node #:nodoc:
- CONTENT_ROOT = "__content__".freeze
+ CONTENT_ROOT = "__content__"
# Convert XML document to hash.
#
@@ -55,7 +55,7 @@ module LibXML #:nodoc:
if c.element?
c.to_hash(node_hash)
elsif c.text? || c.cdata?
- node_hash[CONTENT_ROOT] ||= "".dup
+ node_hash[CONTENT_ROOT] ||= +""
node_hash[CONTENT_ROOT] << c.content
end
end
diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
index dcf16e6084..ac8acdfc3c 100644
--- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb
+++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
@@ -13,8 +13,8 @@ module ActiveSupport
class HashBuilder
include LibXML::XML::SaxParser::Callbacks
- CONTENT_KEY = "__content__".freeze
- HASH_SIZE_KEY = "__hash_size__".freeze
+ CONTENT_KEY = "__content__"
+ HASH_SIZE_KEY = "__hash_size__"
attr_reader :hash
@@ -23,7 +23,7 @@ module ActiveSupport
end
def on_start_document
- @hash = { CONTENT_KEY => "".dup }
+ @hash = { CONTENT_KEY => +"" }
@hash_stack = [@hash]
end
@@ -33,7 +33,7 @@ module ActiveSupport
end
def on_start_element(name, attrs = {})
- new_hash = { CONTENT_KEY => "".dup }.merge!(attrs)
+ new_hash = { CONTENT_KEY => +"" }.merge!(attrs)
new_hash[HASH_SIZE_KEY] = new_hash.size + 1
case current_hash[name]
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index 5ee6fc8159..f76513f48b 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -38,7 +38,7 @@ module ActiveSupport
end
module Node #:nodoc:
- CONTENT_ROOT = "__content__".freeze
+ CONTENT_ROOT = "__content__"
# Convert XML document to hash.
#
@@ -59,7 +59,7 @@ module ActiveSupport
if c.element?
c.to_hash(node_hash)
elsif c.text? || c.cdata?
- node_hash[CONTENT_ROOT] ||= "".dup
+ node_hash[CONTENT_ROOT] ||= +""
node_hash[CONTENT_ROOT] << c.content
end
end
diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
index b01ed00a14..55cd72e093 100644
--- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
@@ -16,8 +16,8 @@ module ActiveSupport
# Class that will build the hash while the XML document
# is being parsed using SAX events.
class HashBuilder < Nokogiri::XML::SAX::Document
- CONTENT_KEY = "__content__".freeze
- HASH_SIZE_KEY = "__hash_size__".freeze
+ CONTENT_KEY = "__content__"
+ HASH_SIZE_KEY = "__hash_size__"
attr_reader :hash
@@ -39,7 +39,7 @@ module ActiveSupport
end
def start_element(name, attrs = [])
- new_hash = { CONTENT_KEY => "".dup }.merge!(Hash[attrs])
+ new_hash = { CONTENT_KEY => +"" }.merge!(Hash[attrs])
new_hash[HASH_SIZE_KEY] = new_hash.size + 1
case current_hash[name]
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index 32458d5b0d..8d6e3af066 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -8,7 +8,7 @@ module ActiveSupport
module XmlMini_REXML #:nodoc:
extend self
- CONTENT_KEY = "__content__".freeze
+ CONTENT_KEY = "__content__"
# Parse an XML Document string or IO into a simple hash.
#
@@ -76,7 +76,7 @@ module ActiveSupport
hash
else
# must use value to prevent double-escaping
- texts = "".dup
+ texts = +""
element.texts.each { |t| texts << t.value }
merge!(hash, CONTENT_KEY, texts)
end
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index f214898145..168d3655d3 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -29,17 +29,18 @@ I18n.enforce_available_locales = false
class ActiveSupport::TestCase
include ActiveSupport::Testing::MethodCallAssertions
- # Skips the current run on Rubinius using Minitest::Assertions#skip
- private def rubinius_skip(message = "")
- skip message if RUBY_ENGINE == "rbx"
- end
-
- # Skips the current run on JRuby using Minitest::Assertions#skip
- private def jruby_skip(message = "")
- skip message if defined?(JRUBY_VERSION)
- end
-
- def frozen_error_class
- Object.const_defined?(:FrozenError) ? FrozenError : RuntimeError
- end
+ private
+ # Skips the current run on Rubinius using Minitest::Assertions#skip
+ def rubinius_skip(message = "")
+ skip message if RUBY_ENGINE == "rbx"
+ end
+
+ # Skips the current run on JRuby using Minitest::Assertions#skip
+ def jruby_skip(message = "")
+ skip message if defined?(JRUBY_VERSION)
+ end
+
+ def frozen_error_class
+ Object.const_defined?(:FrozenError) ? FrozenError : RuntimeError
+ end
end
diff --git a/activesupport/test/autoloading_fixtures/module_folder/nested_with_require.rb b/activesupport/test/autoloading_fixtures/module_folder/nested_with_require.rb
new file mode 100644
index 0000000000..f9d6e675d7
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/module_folder/nested_with_require.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require "dependencies/module_folder/lib_class"
+
+module ModuleFolder
+ class NestedWithRequire
+ end
+end
diff --git a/activesupport/test/autoloading_fixtures/nested_with_require_parent.rb b/activesupport/test/autoloading_fixtures/nested_with_require_parent.rb
new file mode 100644
index 0000000000..e8fb321077
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/nested_with_require_parent.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class NestedWithRequireParent
+ ModuleFolder::NestedWithRequire
+end
diff --git a/activesupport/test/benchmarkable_test.rb b/activesupport/test/benchmarkable_test.rb
index cb7a69cccf..59a71d99be 100644
--- a/activesupport/test/benchmarkable_test.rb
+++ b/activesupport/test/benchmarkable_test.rb
@@ -59,13 +59,13 @@ class BenchmarkableTest < ActiveSupport::TestCase
def test_within_level
logger.level = ActiveSupport::Logger::DEBUG
- benchmark("included_debug_run", level: :debug) {}
+ benchmark("included_debug_run", level: :debug) { }
assert_last_logged "included_debug_run"
end
def test_outside_level
logger.level = ActiveSupport::Logger::ERROR
- benchmark("skipped_debug_run", level: :debug) {}
+ benchmark("skipped_debug_run", level: :debug) { }
assert_no_match(/skipped_debug_run/, buffer.last)
ensure
logger.level = ActiveSupport::Logger::DEBUG
diff --git a/activesupport/test/broadcast_logger_test.rb b/activesupport/test/broadcast_logger_test.rb
index 181113e70a..7dfa8a62bd 100644
--- a/activesupport/test/broadcast_logger_test.rb
+++ b/activesupport/test/broadcast_logger_test.rb
@@ -114,7 +114,17 @@ module ActiveSupport
assert_equal [[::Logger::FATAL, "seen", nil]], log2.adds
end
+ test "Including top constant LoggerSilence is deprecated" do
+ assert_deprecated("Please use `ActiveSupport::LoggerSilence`") do
+ Class.new(CustomLogger) do
+ include ::LoggerSilence
+ end
+ end
+ end
+
class CustomLogger
+ include ActiveSupport::LoggerSilence
+
attr_reader :adds, :closed, :chevrons
attr_accessor :level, :progname, :formatter, :local_level
@@ -166,7 +176,6 @@ module ActiveSupport
end
class FakeLogger < CustomLogger
- include LoggerSilence
end
end
end
diff --git a/activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb b/activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb
index 4e8ff60eb3..a4abdd37b9 100644
--- a/activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb
@@ -2,7 +2,7 @@
module CacheInstrumentationBehavior
def test_fetch_multi_uses_write_multi_entries_store_provider_interface
- assert_called_with(@cache, :write_multi_entries) do
+ assert_called(@cache, :write_multi_entries) do
@cache.fetch_multi "a", "b", "c" do |key|
key * 2
end
diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb
index f9153ffe2a..9f54b1e7de 100644
--- a/activesupport/test/cache/behaviors/cache_store_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb
@@ -52,6 +52,13 @@ module CacheStoreBehavior
end
end
+ def test_fetch_cache_miss_with_skip_nil
+ assert_not_called(@cache, :write) do
+ assert_nil @cache.fetch("foo", skip_nil: true) { nil }
+ assert_equal false, @cache.exist?("foo")
+ end
+ end
+
def test_fetch_with_forced_cache_miss_with_block
@cache.write("foo", "bar")
assert_equal "foo_bar", @cache.fetch("foo", force: true) { "foo_bar" }
@@ -141,7 +148,7 @@ module CacheStoreBehavior
end
end
- # Use strings that are guarenteed to compress well, so we can easily tell if
+ # Use strings that are guaranteed to compress well, so we can easily tell if
# the compression kicked in or not.
SMALL_STRING = "0" * 100
LARGE_STRING = "0" * 2.kilobytes
@@ -283,6 +290,55 @@ module CacheStoreBehavior
assert_equal "bar", @cache.read("fu/foo")
end
+ InstanceTest = Struct.new(:name, :id) do
+ def cache_key
+ "#{name}/#{id}"
+ end
+
+ def to_param
+ "hello"
+ end
+ end
+
+ def test_array_with_single_instance_as_cache_key_uses_cache_key_method
+ test_instance_one = InstanceTest.new("test", 1)
+ test_instance_two = InstanceTest.new("test", 2)
+
+ @cache.write([test_instance_one], "one")
+ @cache.write([test_instance_two], "two")
+
+ assert_equal "one", @cache.read([test_instance_one])
+ assert_equal "two", @cache.read([test_instance_two])
+ end
+
+ def test_array_with_multiple_instances_as_cache_key_uses_cache_key_method
+ test_instance_one = InstanceTest.new("test", 1)
+ test_instance_two = InstanceTest.new("test", 2)
+ test_instance_three = InstanceTest.new("test", 3)
+
+ @cache.write([test_instance_one, test_instance_three], "one")
+ @cache.write([test_instance_two, test_instance_three], "two")
+
+ assert_equal "one", @cache.read([test_instance_one, test_instance_three])
+ assert_equal "two", @cache.read([test_instance_two, test_instance_three])
+ end
+
+ def test_format_of_expanded_key_for_single_instance
+ test_instance_one = InstanceTest.new("test", 1)
+
+ expanded_key = @cache.send(:expanded_key, test_instance_one)
+
+ assert_equal expanded_key, test_instance_one.cache_key
+ end
+
+ def test_format_of_expanded_key_for_single_instance_in_array
+ test_instance_one = InstanceTest.new("test", 1)
+
+ expanded_key = @cache.send(:expanded_key, [test_instance_one])
+
+ assert_equal expanded_key, test_instance_one.cache_key
+ end
+
def test_hash_as_cache_key
@cache.write({ foo: 1, fu: 2 }, "bar")
assert_equal "bar", @cache.read("foo=1/fu=2")
@@ -312,7 +368,7 @@ module CacheStoreBehavior
end
def test_original_store_objects_should_not_be_immutable
- bar = "bar".dup
+ bar = +"bar"
@cache.write("foo", bar)
assert_nothing_raised { bar.gsub!(/.*/, "baz") }
end
@@ -417,7 +473,7 @@ module CacheStoreBehavior
@events << ActiveSupport::Notifications::Event.new(*args)
end
assert @cache.write(key, "1", raw: true)
- assert @cache.fetch(key) {}
+ assert @cache.fetch(key) { }
assert_equal 1, @events.length
assert_equal "cache_read.active_support", @events[0].name
assert_equal :fetch, @events[0].payload[:super_operation]
@@ -431,7 +487,7 @@ module CacheStoreBehavior
ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args|
@events << ActiveSupport::Notifications::Event.new(*args)
end
- assert_not @cache.fetch("bad_key") {}
+ assert_not @cache.fetch("bad_key") { }
assert_equal 3, @events.length
assert_equal "cache_read.active_support", @events[0].name
assert_equal "cache_generate.active_support", @events[1].name
diff --git a/activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb b/activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb
index da16142496..842400f4a3 100644
--- a/activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb
+++ b/activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb
@@ -6,7 +6,7 @@
module EncodedKeyCacheBehavior
Encoding.list.each do |encoding|
define_method "test_#{encoding.name.underscore}_encoded_values" do
- key = "foo".dup.force_encoding(encoding)
+ key = (+"foo").force_encoding(encoding)
assert @cache.write(key, "1", raw: true)
assert_equal "1", @cache.read(key)
assert_equal "1", @cache.fetch(key)
@@ -18,7 +18,7 @@ module EncodedKeyCacheBehavior
end
def test_common_utf8_values
- key = "\xC3\xBCmlaut".dup.force_encoding(Encoding::UTF_8)
+ key = (+"\xC3\xBCmlaut").force_encoding(Encoding::UTF_8)
assert @cache.write(key, "1", raw: true)
assert_equal "1", @cache.read(key)
assert_equal "1", @cache.fetch(key)
@@ -29,7 +29,7 @@ module EncodedKeyCacheBehavior
end
def test_retains_encoding
- key = "\xC3\xBCmlaut".dup.force_encoding(Encoding::UTF_8)
+ key = (+"\xC3\xBCmlaut").force_encoding(Encoding::UTF_8)
assert @cache.write(key, "1", raw: true)
assert_equal Encoding::UTF_8, key.encoding
end
diff --git a/activesupport/test/cache/cache_entry_test.rb b/activesupport/test/cache/cache_entry_test.rb
index d7baaa5c72..ec20a288e1 100644
--- a/activesupport/test/cache/cache_entry_test.rb
+++ b/activesupport/test/cache/cache_entry_test.rb
@@ -6,9 +6,9 @@ require "active_support/cache"
class CacheEntryTest < ActiveSupport::TestCase
def test_expired
entry = ActiveSupport::Cache::Entry.new("value")
- assert !entry.expired?, "entry not expired"
+ assert_not entry.expired?, "entry not expired"
entry = ActiveSupport::Cache::Entry.new("value", expires_in: 60)
- assert !entry.expired?, "entry not expired"
+ assert_not entry.expired?, "entry not expired"
Time.stub(:now, Time.now + 61) do
assert entry.expired?, "entry is expired"
end
diff --git a/activesupport/test/cache/cache_key_test.rb b/activesupport/test/cache/cache_key_test.rb
index 84e656f504..c2240d03c2 100644
--- a/activesupport/test/cache/cache_key_test.rb
+++ b/activesupport/test/cache/cache_key_test.rb
@@ -47,7 +47,7 @@ class CacheKeyTest < ActiveSupport::TestCase
end
def test_expand_cache_key_respond_to_cache_key
- key = "foo".dup
+ key = +"foo"
def key.cache_key
:foo_key
end
@@ -55,7 +55,7 @@ class CacheKeyTest < ActiveSupport::TestCase
end
def test_expand_cache_key_array_with_something_that_responds_to_cache_key
- key = "foo".dup
+ key = +"foo"
def key.cache_key
:foo_key
end
diff --git a/activesupport/test/cache/local_cache_middleware_test.rb b/activesupport/test/cache/local_cache_middleware_test.rb
index e59fae0b4c..e46fa59784 100644
--- a/activesupport/test/cache/local_cache_middleware_test.rb
+++ b/activesupport/test/cache/local_cache_middleware_test.rb
@@ -17,7 +17,7 @@ module ActiveSupport
})
_, _, body = middleware.call({})
assert LocalCacheRegistry.cache_for(key), "should still have a cache"
- body.each {}
+ body.each { }
assert LocalCacheRegistry.cache_for(key), "should still have a cache"
body.close
assert_nil LocalCacheRegistry.cache_for(key)
diff --git a/activesupport/test/cache/stores/memory_store_test.rb b/activesupport/test/cache/stores/memory_store_test.rb
index 340fb517cb..4c0a4f549d 100644
--- a/activesupport/test/cache/stores/memory_store_test.rb
+++ b/activesupport/test/cache/stores/memory_store_test.rb
@@ -33,9 +33,9 @@ class MemoryStorePruningTest < ActiveSupport::TestCase
@cache.prune(@record_size * 3)
assert @cache.exist?(5)
assert @cache.exist?(4)
- assert !@cache.exist?(3), "no entry"
+ assert_not @cache.exist?(3), "no entry"
assert @cache.exist?(2)
- assert !@cache.exist?(1), "no entry"
+ assert_not @cache.exist?(1), "no entry"
end
def test_prune_size_on_write
@@ -57,12 +57,12 @@ class MemoryStorePruningTest < ActiveSupport::TestCase
assert @cache.exist?(9)
assert @cache.exist?(8)
assert @cache.exist?(7)
- assert !@cache.exist?(6), "no entry"
- assert !@cache.exist?(5), "no entry"
+ assert_not @cache.exist?(6), "no entry"
+ assert_not @cache.exist?(5), "no entry"
assert @cache.exist?(4)
- assert !@cache.exist?(3), "no entry"
+ assert_not @cache.exist?(3), "no entry"
assert @cache.exist?(2)
- assert !@cache.exist?(1), "no entry"
+ assert_not @cache.exist?(1), "no entry"
end
def test_prune_size_on_write_based_on_key_length
@@ -82,11 +82,11 @@ class MemoryStorePruningTest < ActiveSupport::TestCase
assert @cache.exist?(8)
assert @cache.exist?(7)
assert @cache.exist?(6)
- assert !@cache.exist?(5), "no entry"
- assert !@cache.exist?(4), "no entry"
- assert !@cache.exist?(3), "no entry"
- assert !@cache.exist?(2), "no entry"
- assert !@cache.exist?(1), "no entry"
+ assert_not @cache.exist?(5), "no entry"
+ assert_not @cache.exist?(4), "no entry"
+ assert_not @cache.exist?(3), "no entry"
+ assert_not @cache.exist?(2), "no entry"
+ assert_not @cache.exist?(1), "no entry"
end
def test_pruning_is_capped_at_a_max_time
diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb
index 24c4c5c481..305a2c184d 100644
--- a/activesupport/test/cache/stores/redis_cache_store_test.rb
+++ b/activesupport/test/cache/stores/redis_cache_store_test.rb
@@ -103,9 +103,7 @@ module ActiveSupport::Cache::RedisCacheStoreTests
private
def build(**kwargs)
- ActiveSupport::Cache::RedisCacheStore.new(driver: DRIVER, **kwargs).tap do |cache|
- cache.redis
- end
+ ActiveSupport::Cache::RedisCacheStore.new(driver: DRIVER, **kwargs).tap(&:redis)
end
end
@@ -141,6 +139,46 @@ module ActiveSupport::Cache::RedisCacheStoreTests
end
end
end
+
+ def test_increment_expires_in
+ assert_called_with @cache.redis, :incrby, [ "#{@namespace}:foo", 1 ] do
+ assert_called_with @cache.redis, :expire, [ "#{@namespace}:foo", 60 ] do
+ @cache.increment "foo", 1, expires_in: 60
+ end
+ end
+
+ # key and ttl exist
+ @cache.redis.setex "#{@namespace}:bar", 120, 1
+ assert_not_called @cache.redis, :expire do
+ @cache.increment "bar", 1, expires_in: 2.minutes
+ end
+
+ # key exist but not have expire
+ @cache.redis.set "#{@namespace}:dar", 10
+ assert_called_with @cache.redis, :expire, [ "#{@namespace}:dar", 60 ] do
+ @cache.increment "dar", 1, expires_in: 60
+ end
+ end
+
+ def test_decrement_expires_in
+ assert_called_with @cache.redis, :decrby, [ "#{@namespace}:foo", 1 ] do
+ assert_called_with @cache.redis, :expire, [ "#{@namespace}:foo", 60 ] do
+ @cache.decrement "foo", 1, expires_in: 60
+ end
+ end
+
+ # key and ttl exist
+ @cache.redis.setex "#{@namespace}:bar", 120, 1
+ assert_not_called @cache.redis, :expire do
+ @cache.decrement "bar", 1, expires_in: 2.minutes
+ end
+
+ # key exist but not have expire
+ @cache.redis.set "#{@namespace}:dar", 10
+ assert_called_with @cache.redis, :expire, [ "#{@namespace}:dar", 60 ] do
+ @cache.decrement "dar", 1, expires_in: 60
+ end
+ end
end
class ConnectionPoolBehaviourTest < StoreTest
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 5c9a3b29e7..466b364e9d 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -953,7 +953,7 @@ module CallbacksTest
def test_proc_arity_2
assert_raises(ArgumentError) do
- klass = build_class(->(x, y) {})
+ klass = build_class(->(x, y) { })
klass.new.run
end
end
@@ -1032,7 +1032,7 @@ module CallbacksTest
def test_proc_arity2
assert_raises(ArgumentError) do
- object = build_class(->(a, b) {}).new
+ object = build_class(->(a, b) { }).new
object.run
end
end
diff --git a/activesupport/test/clean_backtrace_test.rb b/activesupport/test/clean_backtrace_test.rb
index 1b44c7c9bf..a0a7056952 100644
--- a/activesupport/test/clean_backtrace_test.rb
+++ b/activesupport/test/clean_backtrace_test.rb
@@ -74,3 +74,43 @@ class BacktraceCleanerFilterAndSilencerTest < ActiveSupport::TestCase
assert_equal [ "/class.rb" ], @bc.clean([ "/mongrel/class.rb" ])
end
end
+
+class BacktraceCleanerDefaultFilterAndSilencerTest < ActiveSupport::TestCase
+ def setup
+ @bc = ActiveSupport::BacktraceCleaner.new
+ end
+
+ test "should format installed gems correctly" do
+ backtrace = [ "#{Gem.default_dir}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
+ result = @bc.clean(backtrace, :all)
+ assert_equal "nosuchgem (1.2.3) lib/foo.rb", result[0]
+ end
+
+ test "should format installed gems not in Gem.default_dir correctly" do
+ target_dir = Gem.path.detect { |p| p != Gem.default_dir }
+ # skip this test if default_dir is the only directory on Gem.path
+ if target_dir
+ backtrace = [ "#{target_dir}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
+ result = @bc.clean(backtrace, :all)
+ assert_equal "nosuchgem (1.2.3) lib/foo.rb", result[0]
+ end
+ end
+
+ test "should format gems installed by bundler" do
+ backtrace = [ "#{Gem.default_dir}/bundler/gems/nosuchgem-1.2.3/lib/foo.rb" ]
+ result = @bc.clean(backtrace, :all)
+ assert_equal "nosuchgem (1.2.3) lib/foo.rb", result[0]
+ end
+
+ test "should silence gems from the backtrace" do
+ backtrace = [ "#{Gem.path[0]}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
+ result = @bc.clean(backtrace)
+ assert_empty result
+ end
+
+ test "should silence stdlib" do
+ backtrace = ["#{RbConfig::CONFIG["rubylibdir"]}/lib/foo.rb"]
+ result = @bc.clean(backtrace)
+ assert_empty result
+ end
+end
diff --git a/activesupport/test/concern_test.rb b/activesupport/test/concern_test.rb
index 98d8f3ee0d..4b3cfcd1d2 100644
--- a/activesupport/test/concern_test.rb
+++ b/activesupport/test/concern_test.rb
@@ -128,4 +128,12 @@ class ConcernTest < ActiveSupport::TestCase
end
end
end
+
+ def test_no_raise_on_same_included_call
+ assert_nothing_raised do
+ 2.times do
+ load File.expand_path("../fixtures/concern/some_concern.rb", __FILE__)
+ end
+ end
+ end
end
diff --git a/activesupport/test/core_ext/array/extract_test.rb b/activesupport/test/core_ext/array/extract_test.rb
new file mode 100644
index 0000000000..f26e055033
--- /dev/null
+++ b/activesupport/test/core_ext/array/extract_test.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "active_support/core_ext/array"
+
+class ExtractTest < ActiveSupport::TestCase
+ def test_extract
+ numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ array_id = numbers.object_id
+
+ odd_numbers = numbers.extract!(&:odd?)
+
+ assert_equal [1, 3, 5, 7, 9], odd_numbers
+ assert_equal [0, 2, 4, 6, 8], numbers
+ assert_equal array_id, numbers.object_id
+ end
+
+ def test_extract_without_block
+ numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ array_id = numbers.object_id
+
+ extract_enumerator = numbers.extract!
+
+ assert_instance_of Enumerator, extract_enumerator
+ assert_equal numbers.size, extract_enumerator.size
+
+ odd_numbers = extract_enumerator.each(&:odd?)
+
+ assert_equal [1, 3, 5, 7, 9], odd_numbers
+ assert_equal [0, 2, 4, 6, 8], numbers
+ assert_equal array_id, numbers.object_id
+ end
+
+ def test_extract_on_empty_array
+ empty_array = []
+ array_id = empty_array.object_id
+
+ new_empty_array = empty_array.extract! { }
+
+ assert_equal [], new_empty_array
+ assert_equal [], empty_array
+ assert_equal array_id, empty_array.object_id
+ end
+end
diff --git a/activesupport/test/core_ext/date_and_time_compatibility_test.rb b/activesupport/test/core_ext/date_and_time_compatibility_test.rb
index 266829a452..58a24b60b6 100644
--- a/activesupport/test/core_ext/date_and_time_compatibility_test.rb
+++ b/activesupport/test/core_ext/date_and_time_compatibility_test.rb
@@ -248,7 +248,7 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase
def test_string_to_time_frozen_preserves_timezone
with_preserve_timezone(true) do
with_env_tz "US/Eastern" do
- source = "2016-04-23T15:11:12+01:00".freeze
+ source = "2016-04-23T15:11:12+01:00"
time = source.to_time
assert_instance_of Time, time
@@ -262,7 +262,7 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase
def test_string_to_time_frozen_does_not_preserve_time_zone
with_preserve_timezone(false) do
with_env_tz "US/Eastern" do
- source = "2016-04-23T15:11:12+01:00".freeze
+ source = "2016-04-23T15:11:12+01:00"
time = source.to_time
assert_instance_of Time, time
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 894fb80cba..f9f6b21c9b 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -152,8 +152,8 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal DateTime.civil(2005, 2, 22, 11, 10, 10), DateTime.civil(2005, 2, 22, 10, 10, 10).since(3600)
assert_equal DateTime.civil(2005, 2, 24, 10, 10, 10), DateTime.civil(2005, 2, 22, 10, 10, 10).since(86400 * 2)
assert_equal DateTime.civil(2005, 2, 24, 11, 10, 35), DateTime.civil(2005, 2, 22, 10, 10, 10).since(86400 * 2 + 3600 + 25)
- assert_equal DateTime.civil(2005, 2, 22, 10, 10, 11), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.333)
- assert_equal DateTime.civil(2005, 2, 22, 10, 10, 12), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.667)
+ assert_not_equal DateTime.civil(2005, 2, 22, 10, 10, 11), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.333)
+ assert_not_equal DateTime.civil(2005, 2, 22, 10, 10, 12), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.667)
end
def test_change
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 240ae3bde0..63934e2433 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -158,6 +158,18 @@ class DurationTest < ActiveSupport::TestCase
assert_equal Date.civil(2017, 1, 3), Date.civil(2017, 1, 1) + 1.day * 2
end
+ def test_date_added_with_multiplied_duration_larger_than_one_month
+ assert_equal Date.civil(2017, 2, 15), Date.civil(2017, 1, 1) + 1.day * 45
+ end
+
+ def test_date_added_with_divided_duration
+ assert_equal Date.civil(2017, 1, 3), Date.civil(2017, 1, 1) + 4.days / 2
+ end
+
+ def test_date_added_with_divided_duration_larger_than_one_month
+ assert_equal Date.civil(2017, 2, 15), Date.civil(2017, 1, 1) + 90.days / 2
+ end
+
def test_plus_with_time
assert_equal 1 + 1.second, 1.second + 1, "Duration + Numeric should == Numeric + Duration"
end
diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index 8d71320931..b63464a36a 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -179,6 +179,21 @@ class EnumerableTests < ActiveSupport::TestCase
payments.index_by.each(&:price))
end
+ def test_index_with
+ payments = GenericEnumerable.new([ Payment.new(5), Payment.new(15), Payment.new(10) ])
+
+ assert_equal({ Payment.new(5) => 5, Payment.new(15) => 15, Payment.new(10) => 10 }, payments.index_with(&:price))
+
+ assert_equal({ title: nil, body: nil }, %i( title body ).index_with(nil))
+ assert_equal({ title: [], body: [] }, %i( title body ).index_with([]))
+ assert_equal({ title: {}, body: {} }, %i( title body ).index_with({}))
+
+ assert_equal Enumerator, payments.index_with.class
+ assert_nil payments.index_with.size
+ assert_equal 42, (1..42).index_with.size
+ assert_equal({ Payment.new(5) => 5, Payment.new(15) => 15, Payment.new(10) => 10 }, payments.index_with.each(&:price))
+ end
+
def test_many
assert_equal false, GenericEnumerable.new([]).many?
assert_equal false, GenericEnumerable.new([ 1 ]).many?
diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb
index 9c97700e5d..186c863f91 100644
--- a/activesupport/test/core_ext/file_test.rb
+++ b/activesupport/test/core_ext/file_test.rb
@@ -59,6 +59,20 @@ class AtomicWriteTest < ActiveSupport::TestCase
File.unlink(file_name) rescue nil
end
+ def test_atomic_write_preserves_file_permissions_same_directory
+ Dir.mktmpdir do |temp_dir|
+ File.chmod 0700, temp_dir
+
+ probed_permissions = File.probe_stat_in(temp_dir).mode.to_s(8)
+
+ File.atomic_write(File.join(temp_dir, file_name), &:close)
+
+ actual_permissions = File.stat(File.join(temp_dir, file_name)).mode.to_s(8)
+
+ assert_equal actual_permissions, probed_permissions
+ end
+ end
+
def test_atomic_write_returns_result_from_yielded_block
block_return_value = File.atomic_write(file_name, Dir.pwd) do |file|
"Hello world!"
diff --git a/activesupport/test/core_ext/module/concerning_test.rb b/activesupport/test/core_ext/module/concerning_test.rb
index 374114c11b..38fd60463d 100644
--- a/activesupport/test/core_ext/module/concerning_test.rb
+++ b/activesupport/test/core_ext/module/concerning_test.rb
@@ -5,7 +5,7 @@ require "active_support/core_ext/module/concerning"
class ModuleConcerningTest < ActiveSupport::TestCase
def test_concerning_declares_a_concern_and_includes_it_immediately
- klass = Class.new { concerning(:Foo) {} }
+ klass = Class.new { concerning(:Foo) { } }
assert_includes klass.ancestors, klass::Foo, klass.ancestors.inspect
end
end
diff --git a/activesupport/test/core_ext/module/introspection_test.rb b/activesupport/test/core_ext/module/introspection_test.rb
index 76d3012239..d8409d5e44 100644
--- a/activesupport/test/core_ext/module/introspection_test.rb
+++ b/activesupport/test/core_ext/module/introspection_test.rb
@@ -15,25 +15,43 @@ module ParentA
end
class IntrospectionTest < ActiveSupport::TestCase
+ def test_module_parent_name
+ assert_equal "ParentA", ParentA::B.module_parent_name
+ assert_equal "ParentA::B", ParentA::B::C.module_parent_name
+ assert_nil ParentA.module_parent_name
+ end
+
+ def test_module_parent_name_when_frozen
+ assert_equal "ParentA", ParentA::FrozenB.module_parent_name
+ assert_equal "ParentA::B", ParentA::B::FrozenC.module_parent_name
+ end
+
def test_parent_name
- assert_equal "ParentA", ParentA::B.parent_name
- assert_equal "ParentA::B", ParentA::B::C.parent_name
- assert_nil ParentA.parent_name
+ assert_deprecated do
+ assert_equal "ParentA", ParentA::B.parent_name
+ end
end
- def test_parent_name_when_frozen
- assert_equal "ParentA", ParentA::FrozenB.parent_name
- assert_equal "ParentA::B", ParentA::B::FrozenC.parent_name
+ def test_module_parent
+ assert_equal ParentA::B, ParentA::B::C.module_parent
+ assert_equal ParentA, ParentA::B.module_parent
+ assert_equal Object, ParentA.module_parent
end
def test_parent
- assert_equal ParentA::B, ParentA::B::C.parent
- assert_equal ParentA, ParentA::B.parent
- assert_equal Object, ParentA.parent
+ assert_deprecated do
+ assert_equal ParentA, ParentA::B.parent
+ end
+ end
+
+ def test_module_parents
+ assert_equal [ParentA::B, ParentA, Object], ParentA::B::C.module_parents
+ assert_equal [ParentA, Object], ParentA::B.module_parents
end
def test_parents
- assert_equal [ParentA::B, ParentA, Object], ParentA::B::C.parents
- assert_equal [ParentA, Object], ParentA::B.parents
+ assert_deprecated do
+ assert_equal [ParentA, Object], ParentA::B.parents
+ end
end
end
diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb
index 635dd7f281..5203434ae6 100644
--- a/activesupport/test/core_ext/object/duplicable_test.rb
+++ b/activesupport/test/core_ext/object/duplicable_test.rb
@@ -19,7 +19,7 @@ class DuplicableTest < ActiveSupport::TestCase
"* https://github.com/rubinius/rubinius/issues/3089"
RAISE_DUP.each do |v|
- assert !v.duplicable?, "#{ v.inspect } should not be duplicable"
+ assert_not v.duplicable?, "#{ v.inspect } should not be duplicable"
assert_raises(TypeError, v.class.name) { v.dup }
end
diff --git a/activesupport/test/core_ext/object/instance_variables_test.rb b/activesupport/test/core_ext/object/instance_variables_test.rb
index a3d8daab5b..9052d209a3 100644
--- a/activesupport/test/core_ext/object/instance_variables_test.rb
+++ b/activesupport/test/core_ext/object/instance_variables_test.rb
@@ -19,15 +19,15 @@ class ObjectInstanceVariableTest < ActiveSupport::TestCase
end
def test_instance_exec_passes_arguments_to_block
- assert_equal %w(hello goodbye), "hello".dup.instance_exec("goodbye") { |v| [self, v] }
+ assert_equal %w(hello goodbye), (+"hello").instance_exec("goodbye") { |v| [self, v] }
end
def test_instance_exec_with_frozen_obj
- assert_equal %w(olleh goodbye), "hello".freeze.instance_exec("goodbye") { |v| [reverse, v] }
+ assert_equal %w(olleh goodbye), "hello".instance_exec("goodbye") { |v| [reverse, v] }
end
def test_instance_exec_nested
- assert_equal %w(goodbye olleh bar), "hello".dup.instance_exec("goodbye") { |arg|
+ assert_equal %w(goodbye olleh bar), (+"hello").instance_exec("goodbye") { |arg|
[arg] + instance_exec("bar") { |v| [reverse, v] } }
end
end
diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb
index 7593bcfa4d..561dadbbcf 100644
--- a/activesupport/test/core_ext/object/to_query_test.rb
+++ b/activesupport/test/core_ext/object/to_query_test.rb
@@ -77,6 +77,20 @@ class ToQueryTest < ActiveSupport::TestCase
assert_equal "name=Nakshay&type=human", hash.to_query
end
+ def test_hash_not_sorted_lexicographically_for_nested_structure
+ params = {
+ "foo" => {
+ "contents" => [
+ { "name" => "gorby", "id" => "123" },
+ { "name" => "puff", "d" => "true" }
+ ]
+ }
+ }
+ expected = "foo[contents][][name]=gorby&foo[contents][][id]=123&foo[contents][][name]=puff&foo[contents][][d]=true"
+
+ assert_equal expected, URI.decode_www_form_component(params.to_query)
+ end
+
private
def assert_query_equal(expected, actual)
assert_equal expected.split("&"), actual.to_query.split("&")
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index 7c7a78f461..4b8efb8a93 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -108,14 +108,14 @@ class RangeTest < ActiveSupport::TestCase
def test_each_on_time_with_zone
twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Eastern Time (US & Canada)"], Time.utc(2006, 11, 28, 10, 30))
assert_raises TypeError do
- ((twz - 1.hour)..twz).each {}
+ ((twz - 1.hour)..twz).each { }
end
end
def test_step_on_time_with_zone
twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Eastern Time (US & Canada)"], Time.utc(2006, 11, 28, 10, 30))
assert_raises TypeError do
- ((twz - 1.hour)..twz).step(1) {}
+ ((twz - 1.hour)..twz).step(1) { }
end
end
@@ -131,11 +131,11 @@ class RangeTest < ActiveSupport::TestCase
def test_date_time_with_each
datetime = DateTime.now
- assert(((datetime - 1.hour)..datetime).each {})
+ assert(((datetime - 1.hour)..datetime).each { })
end
def test_date_time_with_step
datetime = DateTime.now
- assert(((datetime - 1.hour)..datetime).step(1) {})
+ assert(((datetime - 1.hour)..datetime).step(1) { })
end
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index b8de16cc5e..2468fe3603 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -25,7 +25,7 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_strip_heredoc_on_a_frozen_string
- assert "".freeze.strip_heredoc.frozen?
+ assert "".strip_heredoc.frozen?
end
def test_strip_heredoc_on_a_string_with_no_lines
@@ -245,8 +245,8 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_string_squish
- original = %{\u205f\u3000 A string surrounded by various unicode spaces,
- with tabs(\t\t), newlines(\n\n), unicode nextlines(\u0085\u0085) and many spaces( ). \u00a0\u2007}.dup
+ original = +%{\u205f\u3000 A string surrounded by various unicode spaces,
+ with tabs(\t\t), newlines(\n\n), unicode nextlines(\u0085\u0085) and many spaces( ). \u00a0\u2007}
expected = "A string surrounded by various unicode spaces, " \
"with tabs( ), newlines( ), unicode nextlines( ) and many spaces( )."
@@ -378,8 +378,8 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_truncate_multibyte
- assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".dup.force_encoding(Encoding::UTF_8),
- "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".dup.force_encoding(Encoding::UTF_8).truncate(10)
+ assert_equal (+"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...").force_encoding(Encoding::UTF_8),
+ (+"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244").force_encoding(Encoding::UTF_8).truncate(10)
end
def test_truncate_should_not_be_html_safe
@@ -400,7 +400,7 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_remove!
- original = "This is a very good day to die".dup
+ original = +"This is a very good day to die"
assert_equal "This is a good day to die", original.remove!(" very")
assert_equal "This is a good day to die", original
assert_equal "This is a good day", original.remove!(" to ", /die/)
@@ -469,6 +469,15 @@ class StringAccessTest < ActiveSupport::TestCase
assert_not_same different_string, string
end
+ test "#first with negative Integer is deprecated" do
+ string = "hello"
+ message = "Calling String#first with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ assert_deprecated(message) do
+ string.first(-1)
+ end
+ end
+
test "#last returns the last character" do
assert_equal "o", "hello".last
assert_equal "x", "x".last
@@ -487,6 +496,15 @@ class StringAccessTest < ActiveSupport::TestCase
assert_not_same different_string, string
end
+ test "#last with negative Integer is deprecated" do
+ string = "hello"
+ message = "Calling String#last with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ assert_deprecated(message) do
+ string.last(-1)
+ end
+ end
+
test "access returns a real string" do
hash = {}
hash["h"] = true
@@ -733,7 +751,7 @@ end
class OutputSafetyTest < ActiveSupport::TestCase
def setup
- @string = "hello".dup
+ @string = +"hello"
@object = Class.new(Object) do
def to_s
"other"
@@ -809,7 +827,7 @@ class OutputSafetyTest < ActiveSupport::TestCase
end
test "Concatting safe onto unsafe yields unsafe" do
- @other_string = "other".dup
+ @other_string = +"other"
string = @string.html_safe
@other_string.concat(string)
@@ -832,7 +850,7 @@ class OutputSafetyTest < ActiveSupport::TestCase
end
test "Concatting safe onto unsafe with << yields unsafe" do
- @other_string = "other".dup
+ @other_string = +"other"
string = @string.html_safe
@other_string << string
@@ -888,7 +906,55 @@ class OutputSafetyTest < ActiveSupport::TestCase
test "Concatting an integer to safe always yields safe" do
string = @string.html_safe
string = string.concat(13)
- assert_equal "hello".dup.concat(13), string
+ assert_equal (+"hello").concat(13), string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Inserting safe into safe yields safe" do
+ string = "foo".html_safe
+ string.insert(0, "<b>".html_safe)
+
+ assert_equal "<b>foo", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Inserting unsafe into safe yields escaped safe" do
+ string = "foo".html_safe
+ string.insert(0, "<b>")
+
+ assert_equal "&lt;b&gt;foo", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Replacing safe with safe yields safe" do
+ string = "foo".html_safe
+ string.replace("<b>".html_safe)
+
+ assert_equal "<b>", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Replacing safe with unsafe yields escaped safe" do
+ string = "foo".html_safe
+ string.replace("<b>")
+
+ assert_equal "&lt;b&gt;", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Replacing index of safe with safe yields safe" do
+ string = "foo".html_safe
+ string[0] = "<b>".html_safe
+
+ assert_equal "<b>oo", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Replacing index of safe with unsafe yields escaped safe" do
+ string = "foo".html_safe
+ string[0] = "<b>"
+
+ assert_equal "&lt;b&gt;oo", string
assert_predicate string, :html_safe?
end
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index e1cb22fda8..7078f3506d 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -950,39 +950,39 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
class TimeExtMarshalingTest < ActiveSupport::TestCase
- def test_marshaling_with_utc_instance
+ def test_marshalling_with_utc_instance
t = Time.utc(2000)
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal "UTC", unmarshaled.zone
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal "UTC", unmarshalled.zone
+ assert_equal t, unmarshalled
end
- def test_marshaling_with_local_instance
+ def test_marshalling_with_local_instance
t = Time.local(2000)
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal t.zone, unmarshaled.zone
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal t.zone, unmarshalled.zone
+ assert_equal t, unmarshalled
end
- def test_marshaling_with_frozen_utc_instance
+ def test_marshalling_with_frozen_utc_instance
t = Time.utc(2000).freeze
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal "UTC", unmarshaled.zone
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal "UTC", unmarshalled.zone
+ assert_equal t, unmarshalled
end
- def test_marshaling_with_frozen_local_instance
+ def test_marshalling_with_frozen_local_instance
t = Time.local(2000).freeze
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal t.zone, unmarshaled.zone
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal t.zone, unmarshalled.zone
+ assert_equal t, unmarshalled
end
def test_marshalling_preserves_fractional_seconds
t = Time.parse("00:00:00.500")
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal t.to_f, unmarshaled.to_f
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal t.to_f, unmarshalled.to_f
+ assert_equal t, unmarshalled
end
def test_last_quarter_on_31st
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index e650209268..f6e836e446 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -1105,7 +1105,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase
def test_use_zone_raises_on_invalid_timezone
Time.zone = "Alaska"
assert_raise ArgumentError do
- Time.use_zone("No such timezone exists") {}
+ Time.use_zone("No such timezone exists") { }
end
assert_equal ActiveSupport::TimeZone["Alaska"], Time.zone
end
diff --git a/activesupport/test/dependencies/module_folder/lib_class.rb b/activesupport/test/dependencies/module_folder/lib_class.rb
new file mode 100644
index 0000000000..c6b52610c1
--- /dev/null
+++ b/activesupport/test/dependencies/module_folder/lib_class.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+ConstFromLib = 1
+
+module ModuleFolder
+ class LibClass
+ end
+end
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index a4fbab7b55..b1b3070891 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -282,6 +282,32 @@ class DependenciesTest < ActiveSupport::TestCase
remove_constants(:ModuleFolder)
end
+ def test_module_with_nested_class_requiring_lib_class
+ with_autoloading_fixtures do
+ _ = ModuleFolder::NestedWithRequire # assignment to silence parse-time warning "possibly useless use of :: in void context"
+
+ assert defined?(ModuleFolder::LibClass)
+ assert_not ActiveSupport::Dependencies.autoloaded_constants.include?("ModuleFolder::LibClass")
+ assert_not ActiveSupport::Dependencies.autoloaded_constants.include?("ConstFromLib")
+ end
+ ensure
+ remove_constants(:ModuleFolder)
+ remove_constants(:ConstFromLib)
+ end
+
+ def test_module_with_nested_class_and_parent_requiring_lib_class
+ with_autoloading_fixtures do
+ _ = NestedWithRequireParent # assignment to silence parse-time warning "possibly useless use of a constant in void context"
+
+ assert defined?(ModuleFolder::LibClass)
+ assert_not ActiveSupport::Dependencies.autoloaded_constants.include?("ModuleFolder::LibClass")
+ assert_not ActiveSupport::Dependencies.autoloaded_constants.include?("ConstFromLib")
+ end
+ ensure
+ remove_constants(:ModuleFolder)
+ remove_constants(:ConstFromLib)
+ end
+
def test_directories_may_manifest_as_nested_classes
with_autoloading_fixtures do
assert_kind_of Class, ClassFolder
@@ -755,7 +781,7 @@ class DependenciesTest < ActiveSupport::TestCase
Object.const_set :EM, Class.new
with_autoloading_fixtures do
require_dependency "em"
- assert ! ActiveSupport::Dependencies.autoloaded?(:EM), "EM shouldn't be marked autoloaded!"
+ assert_not ActiveSupport::Dependencies.autoloaded?(:EM), "EM shouldn't be marked autoloaded!"
ActiveSupport::Dependencies.clear
end
ensure
@@ -782,7 +808,7 @@ class DependenciesTest < ActiveSupport::TestCase
Object.const_set :M, Module.new
ActiveSupport::Dependencies.clear
- assert ! defined?(M), "Dependencies should unload unloadable constants each time"
+ assert_not defined?(M), "Dependencies should unload unloadable constants each time"
end
end
@@ -816,7 +842,7 @@ class DependenciesTest < ActiveSupport::TestCase
end
def test_new_contants_in_without_constants
- assert_equal [], (ActiveSupport::Dependencies.new_constants_in(Object) {})
+ assert_equal [], (ActiveSupport::Dependencies.new_constants_in(Object) { })
assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k, v| v.empty? }
end
@@ -892,7 +918,7 @@ class DependenciesTest < ActiveSupport::TestCase
def test_new_constants_in_with_illegal_module_name_raises_correct_error
assert_raise(NameError) do
- ActiveSupport::Dependencies.new_constants_in("Illegal-Name") {}
+ ActiveSupport::Dependencies.new_constants_in("Illegal-Name") { }
end
end
@@ -980,10 +1006,10 @@ class DependenciesTest < ActiveSupport::TestCase
def test_autoload_doesnt_shadow_no_method_error_with_relative_constant
with_autoloading_fixtures do
- assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it hasn't been referenced yet!"
+ assert_not defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it hasn't been referenced yet!"
2.times do
assert_raise(NoMethodError) { RaisesNoMethodError }
- assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!"
+ assert_not defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!"
end
end
ensure
@@ -992,10 +1018,10 @@ class DependenciesTest < ActiveSupport::TestCase
def test_autoload_doesnt_shadow_no_method_error_with_absolute_constant
with_autoloading_fixtures do
- assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it hasn't been referenced yet!"
+ assert_not defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it hasn't been referenced yet!"
2.times do
assert_raise(NoMethodError) { ::RaisesNoMethodError }
- assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!"
+ assert_not defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!"
end
end
ensure
@@ -1020,13 +1046,13 @@ class DependenciesTest < ActiveSupport::TestCase
::RaisesNameError::FooBarBaz.object_id
end
assert_equal "uninitialized constant RaisesNameError::FooBarBaz", e.message
- assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!"
+ assert_not defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!"
end
assert_not defined?(::RaisesNameError)
2.times do
assert_raise(NameError) { ::RaisesNameError }
- assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!"
+ assert_not defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!"
end
end
ensure
@@ -1130,3 +1156,52 @@ class DependenciesTest < ActiveSupport::TestCase
ActiveSupport::Dependencies.hook!
end
end
+
+class DependenciesLogging < ActiveSupport::TestCase
+ MESSAGE = "message"
+
+ def with_settings(logger, verbose)
+ original_logger = ActiveSupport::Dependencies.logger
+ original_verbose = ActiveSupport::Dependencies.verbose
+
+ ActiveSupport::Dependencies.logger = logger
+ ActiveSupport::Dependencies.verbose = verbose
+
+ yield
+ ensure
+ ActiveSupport::Dependencies.logger = original_logger
+ ActiveSupport::Dependencies.verbose = original_verbose
+ end
+
+ def fake_logger
+ Class.new do
+ def self.debug(message)
+ message
+ end
+ end
+ end
+
+ test "does not log if the logger is nil and verbose is false" do
+ with_settings(nil, false) do
+ assert_nil ActiveSupport::Dependencies.log(MESSAGE)
+ end
+ end
+
+ test "does not log if the logger is nil and verbose is true" do
+ with_settings(nil, true) do
+ assert_nil ActiveSupport::Dependencies.log(MESSAGE)
+ end
+ end
+
+ test "does not log if the logger is set and verbose is false" do
+ with_settings(fake_logger, false) do
+ assert_nil ActiveSupport::Dependencies.log(MESSAGE)
+ end
+ end
+
+ test "logs if the logger is set and verbose is true" do
+ with_settings(fake_logger, true) do
+ assert_equal "autoloading: #{MESSAGE}", ActiveSupport::Dependencies.log(MESSAGE)
+ end
+ end
+end
diff --git a/activesupport/test/deprecation/method_wrappers_test.rb b/activesupport/test/deprecation/method_wrappers_test.rb
index 439e117c1d..18729941bc 100644
--- a/activesupport/test/deprecation/method_wrappers_test.rb
+++ b/activesupport/test/deprecation/method_wrappers_test.rb
@@ -21,6 +21,13 @@ class MethodWrappersTest < ActiveSupport::TestCase
end
end
+ def test_deprecate_methods_without_alternate_method
+ warning = /old_method is deprecated and will be removed from Rails \d.\d./
+ ActiveSupport::Deprecation.deprecate_methods(@klass, :old_method)
+
+ assert_deprecated(warning) { assert_equal "abc", @klass.new.old_method }
+ end
+
def test_deprecate_methods_warning_default
warning = /old_method is deprecated and will be removed from Rails \d.\d \(use new_method instead\)/
ActiveSupport::Deprecation.deprecate_methods(@klass, old_method: :new_method)
@@ -55,4 +62,39 @@ class MethodWrappersTest < ActiveSupport::TestCase
assert(@klass.private_method_defined?(:old_private_method))
end
+
+ def test_deprecate_class_method
+ mod = Module.new do
+ extend self
+
+ def old_method
+ "abc"
+ end
+ end
+ ActiveSupport::Deprecation.deprecate_methods(mod, old_method: :new_method)
+
+ warning = /old_method is deprecated and will be removed from Rails \d.\d \(use new_method instead\)/
+ assert_deprecated(warning) { assert_equal "abc", mod.old_method }
+ end
+
+ def test_deprecate_method_when_class_extends_module
+ mod = Module.new do
+ def old_method
+ "abc"
+ end
+ end
+ @klass.extend mod
+ ActiveSupport::Deprecation.deprecate_methods(mod, old_method: :new_method)
+
+ warning = /old_method is deprecated and will be removed from Rails \d.\d \(use new_method instead\)/
+ assert_deprecated(warning) { assert_equal "abc", @klass.old_method }
+ end
+
+ def test_method_with_without_deprecation_is_exposed
+ ActiveSupport::Deprecation.deprecate_methods(@klass, old_method: :new_method)
+
+ warning = /old_method is deprecated and will be removed from Rails \d.\d \(use new_method instead\)/
+ assert_deprecated(warning) { assert_equal "abc", @klass.new.old_method_with_deprecation }
+ assert_equal "abc", @klass.new.old_method_without_deprecation
+ end
end
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index 60673c032b..f25c704586 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -31,6 +31,9 @@ class Deprecatee
def f=(v); end
deprecate :f=
+ deprecate :g
+ def g; end
+
module B
C = 1
end
@@ -182,6 +185,14 @@ class DeprecationTest < ActiveSupport::TestCase
end
end
+ def test_default_invalid_behavior
+ e = assert_raises(ArgumentError) do
+ ActiveSupport::Deprecation.behavior = :invalid
+ end
+
+ assert_equal ":invalid is not a valid deprecation behavior.", e.message
+ end
+
def test_deprecated_instance_variable_proxy
assert_not_deprecated { @dtc.request.size }
@@ -417,6 +428,10 @@ class DeprecationTest < ActiveSupport::TestCase
end
end
+ def test_deprecate_work_before_define_method
+ assert_deprecated { @dtc.g }
+ end
+
private
def deprecator_with_messages
klass = Class.new(ActiveSupport::Deprecation)
diff --git a/activesupport/test/encrypted_configuration_test.rb b/activesupport/test/encrypted_configuration_test.rb
index 93ccf457de..387d6e1c1f 100644
--- a/activesupport/test/encrypted_configuration_test.rb
+++ b/activesupport/test/encrypted_configuration_test.rb
@@ -42,6 +42,12 @@ class EncryptedConfigurationTest < ActiveSupport::TestCase
assert @credentials.something[:good]
end
+ test "reading comment-only configuration" do
+ @credentials.write("# comment")
+
+ assert_equal @credentials.config, {}
+ end
+
test "change configuration by key file" do
@credentials.write({ something: { good: true } }.to_yaml)
@credentials.change do |config_file|
diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb
index d3af0dbef3..a557608986 100644
--- a/activesupport/test/evented_file_update_checker_test.rb
+++ b/activesupport/test/evented_file_update_checker_test.rb
@@ -38,7 +38,7 @@ class EventedFileUpdateCheckerTest < ActiveSupport::TestCase
FileUtils.touch(tmpfiles)
- checker = new_checker(tmpfiles) {}
+ checker = new_checker(tmpfiles) { }
assert_not_predicate checker, :updated?
# Pipes used for flow control across fork.
diff --git a/activesupport/test/executor_test.rb b/activesupport/test/executor_test.rb
index af441064dd..3026f002c3 100644
--- a/activesupport/test/executor_test.rb
+++ b/activesupport/test/executor_test.rb
@@ -23,7 +23,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.to_run { @foo = true }
executor.to_complete { result = @foo }
- executor.wrap {}
+ executor.wrap { }
assert result
end
@@ -85,7 +85,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook)
- executor.wrap {}
+ executor.wrap { }
assert_equal :some_state, supplied_state
end
@@ -105,7 +105,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook)
- executor.wrap {}
+ executor.wrap { }
assert_nil supplied_state
end
@@ -129,7 +129,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook)
assert_raises(DummyError) do
- executor.wrap {}
+ executor.wrap { }
end
assert_equal :none, supplied_state
@@ -154,7 +154,7 @@ class ExecutorTest < ActiveSupport::TestCase
end
assert_raises(DummyError) do
- executor.wrap {}
+ executor.wrap { }
end
assert_equal :some_state, supplied_state
@@ -187,7 +187,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook_class.new(:c), outer: true)
executor.register_hook(hook_class.new(:d))
- executor.wrap {}
+ executor.wrap { }
assert_equal [:run_c, :run_a, :run_b, :run_d, :complete_a, :complete_b, :complete_d, :complete_c], invoked
assert_equal [:state_a, :state_b, :state_d, :state_c], supplied_state
@@ -209,9 +209,9 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook)
before = RubyVM.stat(:class_serial)
- executor.wrap {}
- executor.wrap {}
- executor.wrap {}
+ executor.wrap { }
+ executor.wrap { }
+ executor.wrap { }
after = RubyVM.stat(:class_serial)
assert_equal before, after
diff --git a/activesupport/test/fixtures/concern/some_concern.rb b/activesupport/test/fixtures/concern/some_concern.rb
new file mode 100644
index 0000000000..87f660a81e
--- /dev/null
+++ b/activesupport/test/fixtures/concern/some_concern.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require "active_support/concern"
+
+module SomeConcern
+ extend ActiveSupport::Concern
+
+ included do
+ # shouldn't raise when module is loaded more than once
+ end
+end
diff --git a/activesupport/test/gzip_test.rb b/activesupport/test/gzip_test.rb
index 05ce12fe86..3d790f69c4 100644
--- a/activesupport/test/gzip_test.rb
+++ b/activesupport/test/gzip_test.rb
@@ -18,7 +18,7 @@ class GzipTest < ActiveSupport::TestCase
compressed = ActiveSupport::Gzip.compress("")
assert_equal Encoding.find("binary"), compressed.encoding
- assert !compressed.blank?, "a compressed blank string should not be blank"
+ assert_not compressed.blank?, "a compressed blank string should not be blank"
end
def test_compress_should_return_gzipped_string_by_compression_level
diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
index a20c428bf8..f81e0dc70f 100644
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ b/activesupport/test/hash_with_indifferent_access_test.rb
@@ -57,6 +57,13 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_equal @symbols, @mixed.with_indifferent_access.symbolize_keys
end
+ def test_to_options_for_hash_with_indifferent_access
+ assert_instance_of Hash, @symbols.with_indifferent_access.to_options
+ assert_equal @symbols, @symbols.with_indifferent_access.to_options
+ assert_equal @symbols, @strings.with_indifferent_access.to_options
+ assert_equal @symbols, @mixed.with_indifferent_access.to_options
+ end
+
def test_deep_symbolize_keys_for_hash_with_indifferent_access
assert_instance_of Hash, @nested_symbols.with_indifferent_access.deep_symbolize_keys
assert_equal @nested_symbols, @nested_symbols.with_indifferent_access.deep_symbolize_keys
@@ -606,7 +613,7 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
def test_assorted_keys_not_stringified
original = { Object.new => 2, 1 => 2, [] => true }
indiff = original.with_indifferent_access
- assert(!indiff.keys.any? { |k| k.kind_of? String }, "A key was converted to a string!")
+ assert_not(indiff.keys.any? { |k| k.kind_of? String }, "A key was converted to a string!")
end
def test_deep_merge_on_indifferent_access
@@ -672,6 +679,17 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_equal "bender", slice["login"]
end
+ def test_indifferent_without
+ original = { a: "x", b: "y", c: 10 }.with_indifferent_access
+ expected = { c: 10 }.with_indifferent_access
+
+ [["a", "b"], [:a, :b]].each do |keys|
+ # Should return a new hash without the given keys.
+ assert_equal expected, original.without(*keys), keys.inspect
+ assert_not_equal expected, original
+ end
+ end
+
def test_indifferent_extract
original = { :a => 1, "b" => 2, :c => 3, "d" => 4 }.with_indifferent_access
expected = { a: 1, b: 2 }.with_indifferent_access
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 340a2abf75..8062873386 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -3,7 +3,6 @@
require "securerandom"
require "abstract_unit"
require "active_support/core_ext/string/inflections"
-require "active_support/core_ext/regexp"
require "active_support/json"
require "active_support/time"
require "time_zone_test_helpers"
@@ -158,6 +157,16 @@ class TestJSONEncoding < ActiveSupport::TestCase
assert_equal({ "foo" => "hello" }, JSON.parse(json))
end
+ def test_struct_to_json_with_options_nested
+ klass = Struct.new(:foo, :bar)
+ struct = klass.new "hello", "world"
+ parent_struct = klass.new struct, "world"
+ json = parent_struct.to_json only: [:foo]
+
+ assert_equal({ "foo" => { "foo" => "hello" } }, JSON.parse(json))
+ end
+
+
def test_hash_should_pass_encoding_options_to_children_in_as_json
person = {
name: "John",
diff --git a/activesupport/test/key_generator_test.rb b/activesupport/test/key_generator_test.rb
index cdde2c573a..9dfc0b2154 100644
--- a/activesupport/test/key_generator_test.rb
+++ b/activesupport/test/key_generator_test.rb
@@ -9,9 +9,6 @@ rescue LoadError, NameError
$stderr.puts "Skipping KeyGenerator test: broken OpenSSL install"
else
- require "active_support/time"
- require "active_support/json"
-
class KeyGeneratorTest < ActiveSupport::TestCase
def setup
@secret = SecureRandom.hex(64)
diff --git a/activesupport/test/lazy_load_hooks_test.rb b/activesupport/test/lazy_load_hooks_test.rb
index 721d44d0c1..50a703e49f 100644
--- a/activesupport/test/lazy_load_hooks_test.rb
+++ b/activesupport/test/lazy_load_hooks_test.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "abstract_unit"
+require "active_support/core_ext/module/remove_method"
class LazyLoadHooksTest < ActiveSupport::TestCase
def test_basic_hook
@@ -125,6 +126,54 @@ class LazyLoadHooksTest < ActiveSupport::TestCase
assert_equal 7, i
end
+ def test_hook_uses_class_eval_when_base_is_a_class
+ ActiveSupport.on_load(:uses_class_eval) do
+ def first_wrestler
+ "John Cena"
+ end
+ end
+
+ ActiveSupport.run_load_hooks(:uses_class_eval, FakeContext)
+ assert_equal "John Cena", FakeContext.new(0).first_wrestler
+ ensure
+ FakeContext.remove_possible_method(:first_wrestler)
+ end
+
+ def test_hook_uses_class_eval_when_base_is_a_module
+ mod = Module.new
+ ActiveSupport.on_load(:uses_class_eval2) do
+ def last_wrestler
+ "Dwayne Johnson"
+ end
+ end
+ ActiveSupport.run_load_hooks(:uses_class_eval2, mod)
+
+ klass = Class.new do
+ include mod
+ end
+
+ assert_equal "Dwayne Johnson", klass.new.last_wrestler
+ end
+
+ def test_hook_uses_instance_eval_when_base_is_an_instance
+ ActiveSupport.on_load(:uses_instance_eval) do
+ def second_wrestler
+ "Hulk Hogan"
+ end
+ end
+
+ context = FakeContext.new(1)
+ ActiveSupport.run_load_hooks(:uses_instance_eval, context)
+
+ assert_raises NoMethodError do
+ FakeContext.new(2).second_wrestler
+ end
+ assert_raises NoMethodError do
+ FakeContext.second_wrestler
+ end
+ assert_equal "Hulk Hogan", context.second_wrestler
+ end
+
private
def incr_amt
diff --git a/activesupport/test/log_subscriber_test.rb b/activesupport/test/log_subscriber_test.rb
index 2af9b1de30..7f05459493 100644
--- a/activesupport/test/log_subscriber_test.rb
+++ b/activesupport/test/log_subscriber_test.rb
@@ -75,6 +75,22 @@ class SyncLogSubscriberTest < ActiveSupport::TestCase
assert_kind_of ActiveSupport::Notifications::Event, @log_subscriber.event
end
+ def test_event_attributes
+ ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber
+ instrument "some_event.my_log_subscriber"
+ wait
+ event = @log_subscriber.event
+ if defined?(JRUBY_VERSION)
+ assert_equal 0, event.cpu_time
+ assert_equal 0, event.allocations
+ else
+ assert_operator event.cpu_time, :>, 0
+ assert_operator event.allocations, :>, 0
+ end
+ assert_operator event.duration, :>, 0
+ assert_operator event.idle_time, :>, 0
+ end
+
def test_does_not_send_the_event_if_it_doesnt_match_the_class
ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber
instrument "unknown_event.my_log_subscriber"
diff --git a/activesupport/test/logger_test.rb b/activesupport/test/logger_test.rb
index 5efbd10a7d..160e1156b6 100644
--- a/activesupport/test/logger_test.rb
+++ b/activesupport/test/logger_test.rb
@@ -5,6 +5,7 @@ require "multibyte_test_helpers"
require "stringio"
require "fileutils"
require "tempfile"
+require "tmpdir"
require "concurrent/atomics"
class LoggerTest < ActiveSupport::TestCase
@@ -39,7 +40,7 @@ class LoggerTest < ActiveSupport::TestCase
logger = Logger.new f
logger.level = Logger::DEBUG
- str = "\x80".dup
+ str = +"\x80"
str.force_encoding("ASCII-8BIT")
logger.add Logger::DEBUG, str
@@ -57,7 +58,7 @@ class LoggerTest < ActiveSupport::TestCase
logger = Logger.new f
logger.level = Logger::DEBUG
- str = "\x80".dup
+ str = +"\x80"
str.force_encoding("ASCII-8BIT")
logger.add Logger::DEBUG, str
diff --git a/activesupport/test/metadata/shared_metadata_tests.rb b/activesupport/test/metadata/shared_metadata_tests.rb
index 08bb0c648e..cf571223e5 100644
--- a/activesupport/test/metadata/shared_metadata_tests.rb
+++ b/activesupport/test/metadata/shared_metadata_tests.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
module SharedMessageMetadataTests
- def teardown
- travel_back
- super
- end
-
def null_serializing?
false
end
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 061446c782..5f4e3f3fd3 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -53,7 +53,7 @@ class MultibyteCharsTest < ActiveSupport::TestCase
end
def test_forwarded_method_with_non_string_result_should_be_returned_verbatim
- str = "".dup
+ str = +""
str.singleton_class.class_eval { def __method_for_multibyte_testing_with_integer_result; 1; end }
@chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing_with_integer_result; 1; end }
@@ -61,26 +61,32 @@ class MultibyteCharsTest < ActiveSupport::TestCase
end
def test_should_concatenate
- mb_a = "a".dup.mb_chars
- mb_b = "b".dup.mb_chars
+ mb_a = (+"a").mb_chars
+ mb_b = (+"b").mb_chars
assert_equal "ab", mb_a + "b"
assert_equal "ab", "a" + mb_b
assert_equal "ab", mb_a + mb_b
assert_equal "ab", mb_a << "b"
- assert_equal "ab", "a".dup << mb_b
+ assert_equal "ab", (+"a") << mb_b
assert_equal "abb", mb_a << mb_b
end
def test_consumes_utf8_strings
- assert @proxy_class.consumes?(UNICODE_STRING)
- assert @proxy_class.consumes?(ASCII_STRING)
- assert_not @proxy_class.consumes?(BYTE_STRING)
+ ActiveSupport::Deprecation.silence do
+ assert @proxy_class.consumes?(UNICODE_STRING)
+ assert @proxy_class.consumes?(ASCII_STRING)
+ assert_not @proxy_class.consumes?(BYTE_STRING)
+ end
+ end
+
+ def test_consumes_is_deprecated
+ assert_deprecated { @proxy_class.consumes?(UNICODE_STRING) }
end
def test_concatenation_should_return_a_proxy_class_instance
assert_equal ActiveSupport::Multibyte.proxy_class, ("a".mb_chars + "b").class
- assert_equal ActiveSupport::Multibyte.proxy_class, ("a".dup.mb_chars << "b").class
+ assert_equal ActiveSupport::Multibyte.proxy_class, ((+"a").mb_chars << "b").class
end
def test_ascii_strings_are_treated_at_utf8_strings
@@ -90,8 +96,8 @@ class MultibyteCharsTest < ActiveSupport::TestCase
def test_concatenate_should_return_proxy_instance
assert(("a".mb_chars + "b").kind_of?(@proxy_class))
assert(("a".mb_chars + "b".mb_chars).kind_of?(@proxy_class))
- assert(("a".dup.mb_chars << "b").kind_of?(@proxy_class))
- assert(("a".dup.mb_chars << "b".mb_chars).kind_of?(@proxy_class))
+ assert(((+"a").mb_chars << "b").kind_of?(@proxy_class))
+ assert(((+"a").mb_chars << "b".mb_chars).kind_of?(@proxy_class))
end
def test_should_return_string_as_json
@@ -135,7 +141,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_tidy_bytes_bang_should_change_wrapped_string
- original = " Un bUen café \x92".dup
+ original = +" Un bUen café \x92"
proxy = chars(original.dup)
proxy.tidy_bytes!
assert_not_equal original, proxy.to_s
@@ -152,7 +158,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_string_methods_are_chainable
- assert chars("".dup).insert(0, "").kind_of?(ActiveSupport::Multibyte.proxy_class)
+ assert chars(+"").insert(0, "").kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").rjust(1).kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").ljust(1).kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").center(1).kind_of?(ActiveSupport::Multibyte.proxy_class)
@@ -165,7 +171,9 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
assert chars("").upcase.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").downcase.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").capitalize.kind_of?(ActiveSupport::Multibyte.proxy_class)
- assert chars("").normalize.kind_of?(ActiveSupport::Multibyte.proxy_class)
+ ActiveSupport::Deprecation.silence do
+ assert chars("").normalize.kind_of?(ActiveSupport::Multibyte.proxy_class)
+ end
assert chars("").decompose.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").compose.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").tidy_bytes.kind_of?(ActiveSupport::Multibyte.proxy_class)
@@ -197,7 +205,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_should_use_character_offsets_for_insert_offsets
- assert_equal "", "".dup.mb_chars.insert(0, "")
+ assert_equal "", (+"").mb_chars.insert(0, "")
assert_equal "こわにちわ", @chars.insert(1, "わ")
assert_equal "こわわわにちわ", @chars.insert(2, "わわ")
assert_equal "わこわわわにちわ", @chars.insert(0, "わ")
@@ -383,10 +391,12 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
def test_reverse_should_work_with_normalized_strings
str = "bös"
reversed_str = "söb"
- assert_equal chars(reversed_str).normalize(:kc), chars(str).normalize(:kc).reverse
- assert_equal chars(reversed_str).normalize(:c), chars(str).normalize(:c).reverse
- assert_equal chars(reversed_str).normalize(:d), chars(str).normalize(:d).reverse
- assert_equal chars(reversed_str).normalize(:kd), chars(str).normalize(:kd).reverse
+ ActiveSupport::Deprecation.silence do
+ assert_equal chars(reversed_str).normalize(:kc), chars(str).normalize(:kc).reverse
+ assert_equal chars(reversed_str).normalize(:c), chars(str).normalize(:c).reverse
+ assert_equal chars(reversed_str).normalize(:d), chars(str).normalize(:d).reverse
+ assert_equal chars(reversed_str).normalize(:kd), chars(str).normalize(:kd).reverse
+ end
assert_equal chars(reversed_str).decompose, chars(str).decompose.reverse
assert_equal chars(reversed_str).compose, chars(str).compose.reverse
end
@@ -420,13 +430,13 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_slice_bang_removes_the_slice_from_the_receiver
- chars = "úüù".dup.mb_chars
+ chars = (+"úüù").mb_chars
chars.slice!(0, 2)
assert_equal "ù", chars
end
def test_slice_bang_returns_nil_and_does_not_modify_receiver_if_out_of_bounds
- string = "úüù".dup
+ string = +"úüù"
chars = string.mb_chars
assert_nil chars.slice!(4, 5)
assert_equal "úüù", chars
@@ -477,7 +487,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
def test_method_works_for_proxyed_methods
assert_equal "ll", "hello".mb_chars.method(:slice).call(2..3) # Defined on Chars
- chars = "hello".mb_chars
+ chars = +"hello".mb_chars
assert_equal "Hello", chars.method(:capitalize!).call # Defined on Chars
assert_equal "Hello", chars
assert_equal "jello", "hello".mb_chars.method(:gsub).call(/h/, "j") # Defined on String
@@ -568,7 +578,9 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
def test_composition_exclusion_is_set_up_properly
# Normalization of DEVANAGARI LETTER QA breaks when composition exclusion isn't used correctly
qa = [0x915, 0x93c].pack("U*")
- assert_equal qa, chars(qa).normalize(:c)
+ ActiveSupport::Deprecation.silence do
+ assert_equal qa, chars(qa).normalize(:c)
+ end
end
# Test for the Public Review Issue #29, bad explanation of composition might lead to a
@@ -578,17 +590,21 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
[0x0B47, 0x0300, 0x0B3E],
[0x1100, 0x0300, 0x1161]
].map { |c| c.pack("U*") }.each do |c|
- assert_equal_codepoints c, chars(c).normalize(:c)
+ ActiveSupport::Deprecation.silence do
+ assert_equal_codepoints c, chars(c).normalize(:c)
+ end
end
end
def test_normalization_shouldnt_strip_null_bytes
null_byte_str = "Test\0test"
- assert_equal null_byte_str, chars(null_byte_str).normalize(:kc)
- assert_equal null_byte_str, chars(null_byte_str).normalize(:c)
- assert_equal null_byte_str, chars(null_byte_str).normalize(:d)
- assert_equal null_byte_str, chars(null_byte_str).normalize(:kd)
+ ActiveSupport::Deprecation.silence do
+ assert_equal null_byte_str, chars(null_byte_str).normalize(:kc)
+ assert_equal null_byte_str, chars(null_byte_str).normalize(:c)
+ assert_equal null_byte_str, chars(null_byte_str).normalize(:d)
+ assert_equal null_byte_str, chars(null_byte_str).normalize(:kd)
+ end
assert_equal null_byte_str, chars(null_byte_str).decompose
assert_equal null_byte_str, chars(null_byte_str).compose
end
@@ -601,11 +617,13 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
323 # COMBINING DOT BELOW
].pack("U*")
- assert_equal_codepoints "", chars("").normalize
- assert_equal_codepoints [44, 105, 106, 328, 323].pack("U*"), chars(comp_str).normalize(:kc).to_s
- assert_equal_codepoints [44, 307, 328, 323].pack("U*"), chars(comp_str).normalize(:c).to_s
- assert_equal_codepoints [44, 307, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:d).to_s
- assert_equal_codepoints [44, 105, 106, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:kd).to_s
+ ActiveSupport::Deprecation.silence do
+ assert_equal_codepoints "", chars("").normalize
+ assert_equal_codepoints [44, 105, 106, 328, 323].pack("U*"), chars(comp_str).normalize(:kc).to_s
+ assert_equal_codepoints [44, 307, 328, 323].pack("U*"), chars(comp_str).normalize(:c).to_s
+ assert_equal_codepoints [44, 307, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:d).to_s
+ assert_equal_codepoints [44, 105, 106, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:kd).to_s
+ end
end
def test_should_compute_grapheme_length
@@ -719,6 +737,51 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
assert_equal BYTE_STRING.dup.mb_chars.class, ActiveSupport::Multibyte::Chars
end
+ def test_unicode_normalize_deprecation
+ # String#unicode_normalize default form is `:nfc`, and
+ # different than Multibyte::Unicode default, `:nkfc`.
+ # Deprecation should suggest the right form if no params
+ # are given and default is used.
+ assert_deprecated(/unicode_normalize\(:nfkc\)/) do
+ ActiveSupport::Multibyte::Unicode.normalize("")
+ end
+
+ assert_deprecated(/unicode_normalize\(:nfd\)/) do
+ ActiveSupport::Multibyte::Unicode.normalize("", :d)
+ end
+ end
+
+ def test_chars_normalize_deprecation
+ # String#unicode_normalize default form is `:nfc`, and
+ # different than Multibyte::Unicode default, `:nkfc`.
+ # Deprecation should suggest the right form if no params
+ # are given and default is used.
+ assert_deprecated(/unicode_normalize\(:nfkc\)/) do
+ "".mb_chars.normalize
+ end
+
+ assert_deprecated(/unicode_normalize\(:nfc\)/) { "".mb_chars.normalize(:c) }
+ assert_deprecated(/unicode_normalize\(:nfd\)/) { "".mb_chars.normalize(:d) }
+ assert_deprecated(/unicode_normalize\(:nfkc\)/) { "".mb_chars.normalize(:kc) }
+ assert_deprecated(/unicode_normalize\(:nfkd\)/) { "".mb_chars.normalize(:kd) }
+ end
+
+ def test_unicode_deprecations
+ assert_deprecated { ActiveSupport::Multibyte::Unicode.downcase("") }
+ assert_deprecated { ActiveSupport::Multibyte::Unicode.upcase("") }
+ assert_deprecated { ActiveSupport::Multibyte::Unicode.swapcase("") }
+ end
+
+ def test_normalize_non_unicode_string
+ # Fullwidth Latin Capital Letter A in Windows 31J
+ str = "\u{ff21}".encode(Encoding::Windows_31J)
+ assert_raise Encoding::CompatibilityError do
+ ActiveSupport::Deprecation.silence do
+ ActiveSupport::Multibyte::Unicode.normalize(str)
+ end
+ end
+ end
+
private
def string_from_classes(classes)
@@ -732,21 +795,3 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
end.pack("U*")
end
end
-
-class MultibyteInternalsTest < ActiveSupport::TestCase
- include MultibyteTestHelpers
-
- test "Chars translates a character offset to a byte offset" do
- example = chars("Puisque c'était son erreur, il m'a aidé")
- [
- [0, 0],
- [3, 3],
- [12, 11],
- [14, 13],
- [41, 39]
- ].each do |byte_offset, character_offset|
- assert_equal character_offset, example.send(:translate_offset, byte_offset),
- "Expected byte offset #{byte_offset} to translate to #{character_offset}"
- end
- end
-end
diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb
index 748e8d16e1..b19689723f 100644
--- a/activesupport/test/multibyte_conformance_test.rb
+++ b/activesupport/test/multibyte_conformance_test.rb
@@ -3,15 +3,10 @@
require "abstract_unit"
require "multibyte_test_helpers"
-require "fileutils"
-require "open-uri"
-require "tmpdir"
-
class MultibyteConformanceTest < ActiveSupport::TestCase
include MultibyteTestHelpers
UNIDATA_FILE = "/NormalizationTest.txt"
- FileUtils.mkdir_p(CACHE_DIR)
RUN_P = begin
Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE)
rescue
@@ -23,64 +18,72 @@ class MultibyteConformanceTest < ActiveSupport::TestCase
end
def test_normalizations_C
- each_line_of_norm_tests do |*cols|
- col1, col2, col3, col4, col5, comment = *cols
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do |*cols|
+ col1, col2, col3, col4, col5, comment = *cols
- # CONFORMANCE:
- # 1. The following invariants must be true for all conformant implementations
- #
- # NFC
- # c2 == NFC(c1) == NFC(c2) == NFC(c3)
- assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}"
- assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}"
- assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}"
- #
- # c4 == NFC(c4) == NFC(c5)
- assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}"
+ # CONFORMANCE:
+ # 1. The following invariants must be true for all conformant implementations
+ #
+ # NFC
+ # c2 == NFC(c1) == NFC(c2) == NFC(c3)
+ assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}"
+ assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}"
+ assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}"
+ #
+ # c4 == NFC(c4) == NFC(c5)
+ assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}"
+ end
end
end
def test_normalizations_D
- each_line_of_norm_tests do |*cols|
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFD
- # c3 == NFD(c1) == NFD(c2) == NFD(c3)
- assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}"
- assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}"
- assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}"
- # c5 == NFD(c4) == NFD(c5)
- assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do |*cols|
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFD
+ # c3 == NFD(c1) == NFD(c2) == NFD(c3)
+ assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}"
+ assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}"
+ assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}"
+ # c5 == NFD(c4) == NFD(c5)
+ assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}"
+ end
end
end
def test_normalizations_KC
- each_line_of_norm_tests do | *cols |
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFKC
- # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
- assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do | *cols |
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFKC
+ # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
+ assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}"
+ end
end
end
def test_normalizations_KD
- each_line_of_norm_tests do | *cols |
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFKD
- # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
- assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do | *cols |
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFKD
+ # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
+ assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}"
+ end
end
end
diff --git a/activesupport/test/multibyte_grapheme_break_conformance_test.rb b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
index fac74cd80f..97963279af 100644
--- a/activesupport/test/multibyte_grapheme_break_conformance_test.rb
+++ b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
@@ -3,10 +3,6 @@
require "abstract_unit"
require "multibyte_test_helpers"
-require "fileutils"
-require "open-uri"
-require "tmpdir"
-
class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase
include MultibyteTestHelpers
@@ -21,10 +17,12 @@ class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase
end
def test_breaks
- each_line_of_break_tests do |*cols|
- *clusters, comment = *cols
- packed = ActiveSupport::Multibyte::Unicode.pack_graphemes(clusters)
- assert_equal clusters, ActiveSupport::Multibyte::Unicode.unpack_graphemes(packed), comment
+ ActiveSupport::Deprecation.silence do
+ each_line_of_break_tests do |*cols|
+ *clusters, comment = *cols
+ packed = ActiveSupport::Multibyte::Unicode.pack_graphemes(clusters)
+ assert_equal clusters, ActiveSupport::Multibyte::Unicode.unpack_graphemes(packed), comment
+ end
end
end
diff --git a/activesupport/test/multibyte_normalization_conformance_test.rb b/activesupport/test/multibyte_normalization_conformance_test.rb
index 1173a94e81..82edf69294 100644
--- a/activesupport/test/multibyte_normalization_conformance_test.rb
+++ b/activesupport/test/multibyte_normalization_conformance_test.rb
@@ -3,10 +3,6 @@
require "abstract_unit"
require "multibyte_test_helpers"
-require "fileutils"
-require "open-uri"
-require "tmpdir"
-
class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase
include MultibyteTestHelpers
@@ -22,64 +18,72 @@ class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase
end
def test_normalizations_C
- each_line_of_norm_tests do |*cols|
- col1, col2, col3, col4, col5, comment = *cols
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do |*cols|
+ col1, col2, col3, col4, col5, comment = *cols
- # CONFORMANCE:
- # 1. The following invariants must be true for all conformant implementations
- #
- # NFC
- # c2 == NFC(c1) == NFC(c2) == NFC(c3)
- assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}"
- assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}"
- assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}"
- #
- # c4 == NFC(c4) == NFC(c5)
- assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}"
+ # CONFORMANCE:
+ # 1. The following invariants must be true for all conformant implementations
+ #
+ # NFC
+ # c2 == NFC(c1) == NFC(c2) == NFC(c3)
+ assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}"
+ assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}"
+ assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}"
+ #
+ # c4 == NFC(c4) == NFC(c5)
+ assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}"
+ end
end
end
def test_normalizations_D
- each_line_of_norm_tests do |*cols|
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFD
- # c3 == NFD(c1) == NFD(c2) == NFD(c3)
- assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}"
- assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}"
- assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}"
- # c5 == NFD(c4) == NFD(c5)
- assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do |*cols|
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFD
+ # c3 == NFD(c1) == NFD(c2) == NFD(c3)
+ assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}"
+ assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}"
+ assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}"
+ # c5 == NFD(c4) == NFD(c5)
+ assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}"
+ end
end
end
def test_normalizations_KC
- each_line_of_norm_tests do | *cols |
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFKC
- # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
- assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do | *cols |
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFKC
+ # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
+ assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}"
+ end
end
end
def test_normalizations_KD
- each_line_of_norm_tests do | *cols |
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFKD
- # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
- assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do | *cols |
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFKD
+ # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
+ assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}"
+ end
end
end
diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb
index f7cf993100..7565655f25 100644
--- a/activesupport/test/multibyte_test_helpers.rb
+++ b/activesupport/test/multibyte_test_helpers.rb
@@ -1,5 +1,9 @@
# frozen_string_literal: true
+require "fileutils"
+require "open-uri"
+require "tmpdir"
+
module MultibyteTestHelpers
class Downloader
def self.download(from, to)
@@ -23,9 +27,9 @@ module MultibyteTestHelpers
CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}"
FileUtils.mkdir_p(CACHE_DIR)
- UNICODE_STRING = "こにちわ".freeze
- ASCII_STRING = "ohayo".freeze
- BYTE_STRING = "\270\236\010\210\245".dup.force_encoding("ASCII-8BIT").freeze
+ UNICODE_STRING = "こにちわ"
+ ASCII_STRING = "ohayo"
+ BYTE_STRING = (+"\270\236\010\210\245").force_encoding("ASCII-8BIT").freeze
def chars(str)
ActiveSupport::Multibyte::Chars.new(str)
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index d035f993f7..54fd4345fb 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -26,6 +26,42 @@ module Notifications
end
end
+ class SubscribeEventObjects < TestCase
+ def test_subscribe_events
+ events = []
+ @notifier.subscribe do |event|
+ events << event
+ end
+
+ ActiveSupport::Notifications.instrument("foo")
+ event = events.first
+ assert event, "should have an event"
+ assert_operator event.allocations, :>, 0
+ assert_operator event.cpu_time, :>, 0
+ assert_operator event.idle_time, :>, 0
+ assert_operator event.duration, :>, 0
+ end
+
+ def test_subscribe_via_top_level_api
+ old_notifier = ActiveSupport::Notifications.notifier
+ ActiveSupport::Notifications.notifier = ActiveSupport::Notifications::Fanout.new
+
+ event = nil
+ ActiveSupport::Notifications.subscribe("foo") do |e|
+ event = e
+ end
+
+ ActiveSupport::Notifications.instrument("foo") do
+ 100.times { Object.new } # allocate at least 100 objects
+ end
+
+ assert event
+ assert_operator event.allocations, :>=, 100
+ ensure
+ ActiveSupport::Notifications.notifier = old_notifier
+ end
+ end
+
class SubscribedTest < TestCase
def test_subscribed
name = "foo"
@@ -54,7 +90,7 @@ module Notifications
ActiveSupport::Notifications.subscribe("foo", TestSubscriber.new)
ActiveSupport::Notifications.instrument("foo") do
- ActiveSupport::Notifications.subscribe("foo") {}
+ ActiveSupport::Notifications.subscribe("foo") { }
end
ensure
ActiveSupport::Notifications.notifier = old_notifier
diff --git a/activesupport/test/parameter_filter_test.rb b/activesupport/test/parameter_filter_test.rb
new file mode 100644
index 0000000000..d2dc71061d
--- /dev/null
+++ b/activesupport/test/parameter_filter_test.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "active_support/core_ext/hash"
+require "active_support/parameter_filter"
+
+class ParameterFilterTest < ActiveSupport::TestCase
+ test "process parameter filter" do
+ test_hashes = [
+ [{ "foo" => "bar" }, { "foo" => "bar" }, %w'food'],
+ [{ "foo" => "bar" }, { "foo" => "[FILTERED]" }, %w'foo'],
+ [{ "foo" => "bar", "bar" => "foo" }, { "foo" => "[FILTERED]", "bar" => "foo" }, %w'foo baz'],
+ [{ "foo" => "bar", "baz" => "foo" }, { "foo" => "[FILTERED]", "baz" => "[FILTERED]" }, %w'foo baz'],
+ [{ "bar" => { "foo" => "bar", "bar" => "foo" } }, { "bar" => { "foo" => "[FILTERED]", "bar" => "foo" } }, %w'fo'],
+ [{ "foo" => { "foo" => "bar", "bar" => "foo" } }, { "foo" => "[FILTERED]" }, %w'f banana'],
+ [{ "deep" => { "cc" => { "code" => "bar", "bar" => "foo" }, "ss" => { "code" => "bar" } } }, { "deep" => { "cc" => { "code" => "[FILTERED]", "bar" => "foo" }, "ss" => { "code" => "bar" } } }, %w'deep.cc.code'],
+ [{ "baz" => [{ "foo" => "baz" }, "1"] }, { "baz" => [{ "foo" => "[FILTERED]" }, "1"] }, [/foo/]]]
+
+ test_hashes.each do |before_filter, after_filter, filter_words|
+ parameter_filter = ActiveSupport::ParameterFilter.new(filter_words)
+ assert_equal after_filter, parameter_filter.filter(before_filter)
+
+ filter_words << "blah"
+ filter_words << lambda { |key, value|
+ value.reverse! if key =~ /bargain/
+ }
+ filter_words << lambda { |key, value, original_params|
+ value.replace("world!") if original_params["barg"]["blah"] == "bar" && key == "hello"
+ }
+
+ parameter_filter = ActiveSupport::ParameterFilter.new(filter_words)
+ before_filter["barg"] = { :bargain => "gain", "blah" => "bar", "bar" => { "bargain" => { "blah" => "foo", "hello" => "world" } } }
+ after_filter["barg"] = { :bargain => "niag", "blah" => "[FILTERED]", "bar" => { "bargain" => { "blah" => "[FILTERED]", "hello" => "world!" } } }
+
+ assert_equal after_filter, parameter_filter.filter(before_filter)
+ end
+ end
+
+ test "filter should return mask option when value is filtered" do
+ mask = Object.new.freeze
+ test_hashes = [
+ [{ "foo" => "bar" }, { "foo" => "bar" }, %w'food'],
+ [{ "foo" => "bar" }, { "foo" => mask }, %w'foo'],
+ [{ "foo" => "bar", "bar" => "foo" }, { "foo" => mask, "bar" => "foo" }, %w'foo baz'],
+ [{ "foo" => "bar", "baz" => "foo" }, { "foo" => mask, "baz" => mask }, %w'foo baz'],
+ [{ "bar" => { "foo" => "bar", "bar" => "foo" } }, { "bar" => { "foo" => mask, "bar" => "foo" } }, %w'fo'],
+ [{ "foo" => { "foo" => "bar", "bar" => "foo" } }, { "foo" => mask }, %w'f banana'],
+ [{ "deep" => { "cc" => { "code" => "bar", "bar" => "foo" }, "ss" => { "code" => "bar" } } }, { "deep" => { "cc" => { "code" => mask, "bar" => "foo" }, "ss" => { "code" => "bar" } } }, %w'deep.cc.code'],
+ [{ "baz" => [{ "foo" => "baz" }, "1"] }, { "baz" => [{ "foo" => mask }, "1"] }, [/foo/]]]
+
+ test_hashes.each do |before_filter, after_filter, filter_words|
+ parameter_filter = ActiveSupport::ParameterFilter.new(filter_words, mask: mask)
+ assert_equal after_filter, parameter_filter.filter(before_filter)
+
+ filter_words << "blah"
+ filter_words << lambda { |key, value|
+ value.reverse! if key =~ /bargain/
+ }
+ filter_words << lambda { |key, value, original_params|
+ value.replace("world!") if original_params["barg"]["blah"] == "bar" && key == "hello"
+ }
+
+ parameter_filter = ActiveSupport::ParameterFilter.new(filter_words, mask: mask)
+ before_filter["barg"] = { :bargain => "gain", "blah" => "bar", "bar" => { "bargain" => { "blah" => "foo", "hello" => "world" } } }
+ after_filter["barg"] = { :bargain => "niag", "blah" => mask, "bar" => { "bargain" => { "blah" => mask, "hello" => "world!" } } }
+
+ assert_equal after_filter, parameter_filter.filter(before_filter)
+ end
+ end
+
+ test "filter_param" do
+ parameter_filter = ActiveSupport::ParameterFilter.new(["foo", /bar/])
+ assert_equal "[FILTERED]", parameter_filter.filter_param("food", "secret vlaue")
+ assert_equal "[FILTERED]", parameter_filter.filter_param("baz.foo", "secret vlaue")
+ assert_equal "[FILTERED]", parameter_filter.filter_param("barbar", "secret vlaue")
+ assert_equal "non secret value", parameter_filter.filter_param("baz", "non secret value")
+ end
+
+ test "filter_param can work with empty filters" do
+ parameter_filter = ActiveSupport::ParameterFilter.new
+ assert_equal "bar", parameter_filter.filter_param("foo", "bar")
+ end
+
+ test "parameter filter should maintain hash with indifferent access" do
+ test_hashes = [
+ [{ "foo" => "bar" }.with_indifferent_access, ["blah"]],
+ [{ "foo" => "bar" }.with_indifferent_access, []]
+ ]
+
+ test_hashes.each do |before_filter, filter_words|
+ parameter_filter = ActiveSupport::ParameterFilter.new(filter_words)
+ assert_instance_of ActiveSupport::HashWithIndifferentAccess,
+ parameter_filter.filter(before_filter)
+ end
+ end
+
+ test "filter_param should return mask option when value is filtered" do
+ mask = Object.new.freeze
+ parameter_filter = ActiveSupport::ParameterFilter.new(["foo", /bar/], mask: mask)
+ assert_equal mask, parameter_filter.filter_param("food", "secret vlaue")
+ assert_equal mask, parameter_filter.filter_param("baz.foo", "secret vlaue")
+ assert_equal mask, parameter_filter.filter_param("barbar", "secret vlaue")
+ assert_equal "non secret value", parameter_filter.filter_param("baz", "non secret value")
+ end
+end
diff --git a/activesupport/test/reloader_test.rb b/activesupport/test/reloader_test.rb
index 976917c1a1..1b7cc253d9 100644
--- a/activesupport/test/reloader_test.rb
+++ b/activesupport/test/reloader_test.rb
@@ -35,13 +35,13 @@ class ReloaderTest < ActiveSupport::TestCase
r = new_reloader { true }
invoked = false
r.to_run { invoked = true }
- r.wrap {}
+ r.wrap { }
assert invoked
r = new_reloader { false }
invoked = false
r.to_run { invoked = true }
- r.wrap {}
+ r.wrap { }
assert_not invoked
end
@@ -53,7 +53,7 @@ class ReloaderTest < ActiveSupport::TestCase
reloader.executor.to_run { called << :executor_run }
reloader.executor.to_complete { called << :executor_complete }
- reloader.wrap {}
+ reloader.wrap { }
assert_equal [:executor_run, :reloader_run, :prepare, :reloader_complete, :executor_complete], called
called = []
@@ -63,7 +63,7 @@ class ReloaderTest < ActiveSupport::TestCase
reloader.check = lambda { false }
called = []
- reloader.wrap {}
+ reloader.wrap { }
assert_equal [:executor_run, :executor_complete], called
called = []
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index 1c19c92bb0..49a3951623 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -75,16 +75,41 @@ class SafeBufferTest < ActiveSupport::TestCase
assert_equal "my_test", str
end
- test "Should not return safe buffer from gsub" do
- altered_buffer = @buffer.gsub("", "asdf")
- assert_equal "asdf", altered_buffer
- assert_not_predicate altered_buffer, :html_safe?
- end
+ {
+ capitalize: nil,
+ chomp: nil,
+ chop: nil,
+ delete: "foo",
+ delete_prefix: "foo",
+ delete_suffix: "foo",
+ downcase: nil,
+ gsub: ["foo", "bar"],
+ lstrip: nil,
+ next: nil,
+ reverse: nil,
+ rstrip: nil,
+ slice: "foo",
+ squeeze: nil,
+ strip: nil,
+ sub: ["foo", "bar"],
+ succ: nil,
+ swapcase: nil,
+ tr: ["foo", "bar"],
+ tr_s: ["foo", "bar"],
+ unicode_normalize: nil,
+ upcase: nil,
+ }.each do |unsafe_method, dummy_args|
+ test "Should not return safe buffer from #{unsafe_method}" do
+ skip unless String.method_defined?(unsafe_method)
+ altered_buffer = @buffer.send(unsafe_method, *dummy_args)
+ assert_not_predicate altered_buffer, :html_safe?
+ end
- test "Should not return safe buffer from gsub!" do
- @buffer.gsub!("", "asdf")
- assert_equal "asdf", @buffer
- assert_not_predicate @buffer, :html_safe?
+ test "Should not return safe buffer from #{unsafe_method}!" do
+ skip unless String.method_defined?("#{unsafe_method}!")
+ @buffer.send("#{unsafe_method}!", *dummy_args)
+ assert_not_predicate @buffer, :html_safe?
+ end
end
test "Should escape dirty buffers on add" do
@@ -141,13 +166,25 @@ class SafeBufferTest < ActiveSupport::TestCase
x = "foo".html_safe.gsub!("f", '<script>alert("lolpwnd");</script>')
# calling gsub! makes the dirty flag true
- assert !x.html_safe?, "should not be safe"
+ assert_not x.html_safe?, "should not be safe"
# getting a slice of it
y = x[0..-1]
# should still be unsafe
- assert !y.html_safe?, "should not be safe"
+ assert_not y.html_safe?, "should not be safe"
+ end
+
+ test "Should continue safe on slice" do
+ x = "<div>foo</div>".html_safe
+
+ assert_predicate x, :html_safe?
+
+ # getting a slice of it
+ y = x[0..-1]
+
+ # should still be safe
+ assert_predicate y, :html_safe?
end
test "Should work with interpolation (array argument)" do
diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb
index 42fd5eefc1..34479020e1 100644
--- a/activesupport/test/share_lock_test.rb
+++ b/activesupport/test/share_lock_test.rb
@@ -11,29 +11,29 @@ class ShareLockTest < ActiveSupport::TestCase
def test_reentrancy
thread = Thread.new do
- @lock.sharing { @lock.sharing {} }
- @lock.exclusive { @lock.exclusive {} }
+ @lock.sharing { @lock.sharing { } }
+ @lock.exclusive { @lock.exclusive { } }
end
assert_threads_not_stuck thread
end
def test_sharing_doesnt_block
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_latch|
- assert_threads_not_stuck(Thread.new { @lock.sharing {} })
+ assert_threads_not_stuck(Thread.new { @lock.sharing { } })
end
end
def test_sharing_blocks_exclusive
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
@lock.exclusive(no_wait: true) { flunk } # polling should fail
- exclusive_thread = Thread.new { @lock.exclusive {} }
+ exclusive_thread = Thread.new { @lock.exclusive { } }
assert_threads_stuck_but_releasable_by_latch exclusive_thread, sharing_thread_release_latch
end
end
def test_exclusive_blocks_sharing
with_thread_waiting_in_lock_section(:exclusive) do |exclusive_thread_release_latch|
- sharing_thread = Thread.new { @lock.sharing {} }
+ sharing_thread = Thread.new { @lock.sharing { } }
assert_threads_stuck_but_releasable_by_latch sharing_thread, exclusive_thread_release_latch
end
end
@@ -42,7 +42,7 @@ class ShareLockTest < ActiveSupport::TestCase
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
exclusive_threads = (1..2).map do
Thread.new do
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
@@ -53,7 +53,7 @@ class ShareLockTest < ActiveSupport::TestCase
def test_sharing_is_upgradeable_to_exclusive
upgrading_thread = Thread.new do
@lock.sharing do
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
assert_threads_not_stuck upgrading_thread
@@ -66,7 +66,7 @@ class ShareLockTest < ActiveSupport::TestCase
upgrading_thread = Thread.new do
@lock.sharing do
in_sharing.count_down
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
@@ -81,7 +81,7 @@ class ShareLockTest < ActiveSupport::TestCase
exclusive_threads = (1..2).map do
Thread.new do
@lock.send(use_upgrading ? :sharing : :tap) do
- @lock.exclusive(purpose: :load, compatible: [:load, :unload]) {}
+ @lock.exclusive(purpose: :load, compatible: [:load, :unload]) { }
end
end
end
@@ -95,7 +95,7 @@ class ShareLockTest < ActiveSupport::TestCase
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
thread = Thread.new do
@lock.sharing do
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
@@ -105,7 +105,7 @@ class ShareLockTest < ActiveSupport::TestCase
sharing_thread_release_latch.count_down
thread = Thread.new do
- @lock.exclusive {}
+ @lock.exclusive { }
end
assert_threads_not_stuck thread
@@ -121,13 +121,13 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.send(use_upgrading ? :sharing : :tap) do
together.wait
- @lock.exclusive(purpose: :red, compatible: [:green, :purple]) {}
+ @lock.exclusive(purpose: :red, compatible: [:green, :purple]) { }
end
end,
Thread.new do
@lock.send(use_upgrading ? :sharing : :tap) do
together.wait
- @lock.exclusive(purpose: :blue, compatible: [:green]) {}
+ @lock.exclusive(purpose: :blue, compatible: [:green]) { }
end
end
]
@@ -138,7 +138,7 @@ class ShareLockTest < ActiveSupport::TestCase
# a sharing block. While it's blocked, it holds no lock, so it
# doesn't interfere with any other attempts.
no_purpose_thread = Thread.new do
- @lock.exclusive {}
+ @lock.exclusive { }
end
assert_threads_stuck no_purpose_thread
@@ -147,7 +147,7 @@ class ShareLockTest < ActiveSupport::TestCase
# lock, but as soon as that's released, it can run --
# regardless of whether those threads hold share locks.
compatible_thread = Thread.new do
- @lock.exclusive(purpose: :green, compatible: []) {}
+ @lock.exclusive(purpose: :green, compatible: []) { }
end
assert_threads_stuck compatible_thread
@@ -231,7 +231,7 @@ class ShareLockTest < ActiveSupport::TestCase
assert_threads_stuck waiting_exclusive
late_share_attempt = Thread.new do
- @lock.sharing {}
+ @lock.sharing { }
end
assert_threads_stuck late_share_attempt
@@ -252,14 +252,14 @@ class ShareLockTest < ActiveSupport::TestCase
@lock.sharing do
ready.wait
attempt_reentrancy.wait
- @lock.sharing {}
+ @lock.sharing { }
end
end
exclusive = Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
@@ -280,7 +280,7 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x, compatible: [:x], after_compatible: [:x]) {}
+ @lock.exclusive(purpose: :x, compatible: [:x], after_compatible: [:x]) { }
done.wait
end
end
@@ -297,7 +297,7 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
done.wait
end
end,
@@ -323,7 +323,7 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
done.wait
end
end,
@@ -352,7 +352,7 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
done.wait
end
end,
@@ -386,7 +386,7 @@ class ShareLockTest < ActiveSupport::TestCase
incompatible_thread = Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
end
end
@@ -418,7 +418,7 @@ class ShareLockTest < ActiveSupport::TestCase
incompatible_thread = Thread.new do
ready.wait
- @lock.exclusive(purpose: :z) {}
+ @lock.exclusive(purpose: :z) { }
end
recursive_yield_shares_thread = Thread.new do
@@ -427,7 +427,7 @@ class ShareLockTest < ActiveSupport::TestCase
@lock.yield_shares(compatible: [:y]) do
do_nesting.wait
@lock.sharing do
- @lock.yield_shares(compatible: [:x, :y]) {}
+ @lock.yield_shares(compatible: [:x, :y]) { }
end
after_nesting.wait
end
@@ -439,12 +439,12 @@ class ShareLockTest < ActiveSupport::TestCase
assert_threads_stuck incompatible_thread
compatible_thread = Thread.new do
- @lock.exclusive(purpose: :y) {}
+ @lock.exclusive(purpose: :y) { }
end
assert_threads_not_stuck compatible_thread
post_nesting_incompatible_thread = Thread.new do
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
end
assert_threads_stuck post_nesting_incompatible_thread
diff --git a/activesupport/test/silence_logger_test.rb b/activesupport/test/silence_logger_test.rb
new file mode 100644
index 0000000000..bd0c6b7f86
--- /dev/null
+++ b/activesupport/test/silence_logger_test.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "active_support/logger_silence"
+require "logger"
+
+class LoggerSilenceTest < ActiveSupport::TestCase
+ class MyLogger < ::Logger
+ include ActiveSupport::LoggerSilence
+ end
+
+ setup do
+ @io = StringIO.new
+ @logger = MyLogger.new(@io)
+ end
+
+ test "#silence silences the log" do
+ @logger.silence(Logger::ERROR) do
+ @logger.info("Foo")
+ end
+ @io.rewind
+
+ assert_empty @io.read
+ end
+
+ test "#debug? is true when setting the temporary level to Logger::DEBUG" do
+ @logger.level = Logger::INFO
+
+ @logger.silence(Logger::DEBUG) do
+ assert_predicate @logger, :debug?
+ end
+
+ assert_predicate @logger, :info?
+ end
+end
diff --git a/activesupport/test/tagged_logging_test.rb b/activesupport/test/tagged_logging_test.rb
index e2b41cf8ee..cff73472c3 100644
--- a/activesupport/test/tagged_logging_test.rb
+++ b/activesupport/test/tagged_logging_test.rb
@@ -19,9 +19,10 @@ class TaggedLoggingTest < ActiveSupport::TestCase
test "sets logger.formatter if missing and extends it with a tagging API" do
logger = Logger.new(StringIO.new)
assert_nil logger.formatter
- ActiveSupport::TaggedLogging.new(logger)
- assert_not_nil logger.formatter
- assert_respond_to logger.formatter, :tagged
+
+ other_logger = ActiveSupport::TaggedLogging.new(logger)
+ assert_not_nil other_logger.formatter
+ assert_respond_to other_logger.formatter, :tagged
end
test "tagged once" do
@@ -83,16 +84,28 @@ class TaggedLoggingTest < ActiveSupport::TestCase
end
test "keeps each tag in their own instance" do
- @other_output = StringIO.new
- @other_logger = ActiveSupport::TaggedLogging.new(MyLogger.new(@other_output))
+ other_output = StringIO.new
+ other_logger = ActiveSupport::TaggedLogging.new(MyLogger.new(other_output))
@logger.tagged("OMG") do
- @other_logger.tagged("BCX") do
+ other_logger.tagged("BCX") do
@logger.info "Cool story"
- @other_logger.info "Funky time"
+ other_logger.info "Funky time"
end
end
assert_equal "[OMG] Cool story\n", @output.string
- assert_equal "[BCX] Funky time\n", @other_output.string
+ assert_equal "[BCX] Funky time\n", other_output.string
+ end
+
+ test "does not share the same formatter instance of the original logger" do
+ other_logger = ActiveSupport::TaggedLogging.new(@logger)
+
+ @logger.tagged("OMG") do
+ other_logger.tagged("BCX") do
+ @logger.info "Cool story"
+ other_logger.info "Funky time"
+ end
+ end
+ assert_equal "[OMG] Cool story\n[BCX] Funky time\n", @output.string
end
test "cleans up the taggings on flush" do
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index 8a1ecb6b33..8698c66e6d 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -52,6 +52,22 @@ class AssertionsTest < ActiveSupport::TestCase
assert_equal "Object Changed.\n\"@object.num\" didn't change by 0.\nExpected: 0\n Actual: 1", error.message
end
+ def test_assert_no_difference_with_multiple_expressions_pass
+ another_object = @object.dup
+ assert_no_difference ["@object.num", -> { another_object.num }] do
+ # ...
+ end
+ end
+
+ def test_assert_no_difference_with_multiple_expressions_fail
+ another_object = @object.dup
+ assert_raises(Minitest::Assertion) do
+ assert_no_difference ["@object.num", -> { another_object.num }], "Another Object Changed" do
+ another_object.increment
+ end
+ end
+ end
+
def test_assert_difference
assert_difference "@object.num", +1 do
@object.increment
@@ -261,7 +277,7 @@ class AssertionsTest < ActiveSupport::TestCase
end
end
- assert_equal "@object.num should 1.\n\"@object.num\" didn't change to 1", error.message
+ assert_equal "@object.num should 1.\n\"@object.num\" didn't change to as expected\nExpected: 1\n Actual: -1", error.message
end
def test_assert_no_changes_pass
diff --git a/activesupport/test/testing/method_call_assertions_test.rb b/activesupport/test/testing/method_call_assertions_test.rb
index 4af000bb7e..669463bd31 100644
--- a/activesupport/test/testing/method_call_assertions_test.rb
+++ b/activesupport/test/testing/method_call_assertions_test.rb
@@ -1,11 +1,8 @@
# frozen_string_literal: true
require "abstract_unit"
-require "active_support/testing/method_call_assertions"
class MethodCallAssertionsTest < ActiveSupport::TestCase
- include ActiveSupport::Testing::MethodCallAssertions
-
class Level
def increment; 1; end
def decrement; end
@@ -39,6 +36,8 @@ class MethodCallAssertionsTest < ActiveSupport::TestCase
assert_called(@object, :increment, returns: 10) do
assert_equal 10, @object.increment
end
+
+ assert_equal 1, @object.increment
end
def test_assert_called_failure
@@ -61,18 +60,20 @@ class MethodCallAssertionsTest < ActiveSupport::TestCase
assert_match(/dang it.\nExpected increment/, error.message)
end
- def test_assert_called_with
- assert_called_with(@object, :increment) do
- @object.increment
- end
- end
-
def test_assert_called_with_arguments
assert_called_with(@object, :<<, [ 2 ]) do
@object << 2
end
end
+ def test_assert_called_with_arguments_and_returns
+ assert_called_with(@object, :<<, [ 2 ], returns: 10) do
+ assert_equal(10, @object << 2)
+ end
+
+ assert_nil(@object << 2)
+ end
+
def test_assert_called_with_failure
assert_raises(MockExpectationError) do
assert_called_with(@object, :<<, [ 4567 ]) do
@@ -81,19 +82,72 @@ class MethodCallAssertionsTest < ActiveSupport::TestCase
end
end
- def test_assert_called_with_returns
- assert_called_with(@object, :increment, returns: 1) do
+ def test_assert_called_with_multiple_expected_arguments
+ assert_called_with(@object, :<<, [ [ 1 ], [ 2 ] ]) do
+ @object << 1
+ @object << 2
+ end
+ end
+
+ def test_assert_called_on_instance_of_with_defaults_to_expect_once
+ assert_called_on_instance_of Level, :increment do
@object.increment
end
end
- def test_assert_called_with_multiple_expected_arguments
- assert_called_with(@object, :<<, [ [ 1 ], [ 2 ] ]) do
- @object << 1
+ def test_assert_called_on_instance_of_more_than_once
+ assert_called_on_instance_of(Level, :increment, times: 2) do
+ @object.increment
+ @object.increment
+ end
+ end
+
+ def test_assert_called_on_instance_of_with_arguments
+ assert_called_on_instance_of(Level, :<<) do
@object << 2
end
end
+ def test_assert_called_on_instance_of_returns
+ assert_called_on_instance_of(Level, :increment, returns: 10) do
+ assert_equal 10, @object.increment
+ end
+
+ assert_equal 1, @object.increment
+ end
+
+ def test_assert_called_on_instance_of_failure
+ error = assert_raises(Minitest::Assertion) do
+ assert_called_on_instance_of(Level, :increment) do
+ # Call nothing...
+ end
+ end
+
+ assert_equal "Expected increment to be called 1 times, but was called 0 times.\nExpected: 1\n Actual: 0", error.message
+ end
+
+ def test_assert_called_on_instance_of_with_message
+ error = assert_raises(Minitest::Assertion) do
+ assert_called_on_instance_of(Level, :increment, "dang it") do
+ # Call nothing...
+ end
+ end
+
+ assert_match(/dang it.\nExpected increment/, error.message)
+ end
+
+ def test_assert_called_on_instance_of_nesting
+ assert_called_on_instance_of(Level, :increment, times: 3) do
+ assert_called_on_instance_of(Level, :decrement, times: 2) do
+ @object.increment
+ @object.decrement
+ @object.increment
+ @object.decrement
+ @object.increment
+ end
+ end
+ end
+
def test_assert_not_called
assert_not_called(@object, :decrement) do
@object.increment
@@ -110,6 +164,30 @@ class MethodCallAssertionsTest < ActiveSupport::TestCase
assert_equal "Expected increment to be called 0 times, but was called 1 times.\nExpected: 0\n Actual: 1", error.message
end
+ def test_assert_not_called_on_instance_of
+ assert_not_called_on_instance_of(Level, :decrement) do
+ @object.increment
+ end
+ end
+
+ def test_assert_not_called_on_instance_of_failure
+ error = assert_raises(Minitest::Assertion) do
+ assert_not_called_on_instance_of(Level, :increment) do
+ @object.increment
+ end
+ end
+
+ assert_equal "Expected increment to be called 0 times, but was called 1 times.\nExpected: 0\n Actual: 1", error.message
+ end
+
+ def test_assert_not_called_on_instance_of_nesting
+ assert_not_called_on_instance_of(Level, :increment) do
+ assert_not_called_on_instance_of(Level, :decrement) do
+ # Call nothing...
+ end
+ end
+ end
+
def test_stub_any_instance
stub_any_instance(Level) do |instance|
assert_equal instance, Level.new
diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb
index 9c2c635f43..8c47f2cdc7 100644
--- a/activesupport/test/time_travel_test.rb
+++ b/activesupport/test/time_travel_test.rb
@@ -186,4 +186,8 @@ class TimeTravelTest < ActiveSupport::TestCase
assert_operator expected_time.to_s(:db), :<, Time.now.to_s(:db)
end
+
+ def test_time_helper_unfreeze_time
+ assert_equal method(:travel_back), method(:unfreeze_time)
+ end
end
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index b59f3e9405..6d45a6726d 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -225,6 +225,16 @@ class TimeZoneTest < ActiveSupport::TestCase
assert_equal secs, twz.to_f
end
+ def test_at_with_microseconds
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+ secs = 946684800.0
+ microsecs = 123456.789
+ twz = zone.at(secs, microsecs)
+ assert_equal zone, twz.time_zone
+ assert_equal secs, twz.to_i
+ assert_equal 123456789, twz.nsec
+ end
+
def test_iso8601
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.iso8601("1999-12-31T19:00:00")
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
index 34bf81fa75..b711619ba7 100644
--- a/activesupport/test/xml_mini/rexml_engine_test.rb
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -12,7 +12,7 @@ class REXMLEngineTest < XMLMiniEngineTest
end
def test_parse_from_frozen_string
- xml_string = "<root></root>".freeze
+ xml_string = "<root></root>"
assert_equal({ "root" => {} }, ActiveSupport::XmlMini.parse(xml_string))
end
diff --git a/activesupport/test/xml_mini/xml_mini_engine_test.rb b/activesupport/test/xml_mini/xml_mini_engine_test.rb
index 5c4c28d9b7..c62e7e32c9 100644
--- a/activesupport/test/xml_mini/xml_mini_engine_test.rb
+++ b/activesupport/test/xml_mini/xml_mini_engine_test.rb
@@ -78,7 +78,7 @@ class XMLMiniEngineTest < ActiveSupport::TestCase
end
def test_parse_from_frozen_string
- xml_string = "<root/>".freeze
+ xml_string = "<root/>"
assert_equal({ "root" => {} }, ActiveSupport::XmlMini.parse(xml_string))
end