aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG.md137
-rw-r--r--activesupport/MIT-LICENSE2
-rw-r--r--activesupport/README.rdoc3
-rw-r--r--activesupport/activesupport.gemspec11
-rw-r--r--activesupport/lib/active_support.rb2
-rw-r--r--activesupport/lib/active_support/cache.rb28
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb23
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb4
-rw-r--r--activesupport/lib/active_support/concern.rb10
-rw-r--r--activesupport/lib/active_support/configurable.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/array.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/array/prepend_and_append.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb25
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/hash/compact.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb46
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb29
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb28
-rw-r--r--activesupport/lib/active_support/core_ext/hash/transform_values.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/kernel.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/agnostics.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/module.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/module/reachable.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/inquiry.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/range/compare_range.rb33
-rw-r--r--activesupport/lib/active_support/core_ext/securerandom.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb3
-rw-r--r--activesupport/lib/active_support/current_attributes.rb6
-rw-r--r--activesupport/lib/active_support/dependencies/zeitwerk_integration.rb94
-rw-r--r--activesupport/lib/active_support/deprecation/method_wrappers.rb38
-rw-r--r--activesupport/lib/active_support/descendants_tracker.rb61
-rw-r--r--activesupport/lib/active_support/duration.rb6
-rw-r--r--activesupport/lib/active_support/encrypted_file.rb3
-rw-r--r--activesupport/lib/active_support/evented_file_update_checker.rb36
-rw-r--r--activesupport/lib/active_support/gem_version.rb2
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb13
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb4
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb2
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb29
-rw-r--r--activesupport/lib/active_support/json/decoding.rb46
-rw-r--r--activesupport/lib/active_support/key_generator.rb32
-rw-r--r--activesupport/lib/active_support/log_subscriber.rb39
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb2
-rw-r--r--activesupport/lib/active_support/message_verifier.rb2
-rw-r--r--activesupport/lib/active_support/notifications.rb25
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb73
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb27
-rw-r--r--activesupport/lib/active_support/reloader.rb8
-rw-r--r--activesupport/lib/active_support/security_utils.rb2
-rw-r--r--activesupport/lib/active_support/test_case.rb9
-rw-r--r--activesupport/lib/active_support/testing/method_call_assertions.rb14
-rw-r--r--activesupport/lib/active_support/testing/parallelization.rb47
-rw-r--r--activesupport/lib/active_support/testing/time_helpers.rb8
-rw-r--r--activesupport/lib/active_support/xml_mini.rb7
-rw-r--r--activesupport/test/abstract_unit.rb4
-rw-r--r--activesupport/test/autoloading_fixtures/raises_load_error.rb4
-rw-r--r--activesupport/test/cache/behaviors/cache_store_behavior.rb10
-rw-r--r--activesupport/test/cache/behaviors/connection_pool_behavior.rb56
-rw-r--r--activesupport/test/cache/stores/file_store_test.rb2
-rw-r--r--activesupport/test/cache/stores/mem_cache_store_test.rb23
-rw-r--r--activesupport/test/cache/stores/redis_cache_store_test.rb4
-rw-r--r--activesupport/test/callbacks_test.rb2
-rw-r--r--activesupport/test/concern_test.rb8
-rw-r--r--activesupport/test/constantize_test_cases.rb10
-rw-r--r--activesupport/test/core_ext/array/access_test.rb12
-rw-r--r--activesupport/test/core_ext/array/prepend_append_test.rb11
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb13
-rw-r--r--activesupport/test/core_ext/file_test.rb14
-rw-r--r--activesupport/test/core_ext/hash/transform_keys_test.rb64
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb90
-rw-r--r--activesupport/test/core_ext/load_error_test.rb7
-rw-r--r--activesupport/test/core_ext/module/reachable_test.rb51
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/secure_random_test.rb20
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb6
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb2
-rw-r--r--activesupport/test/current_attributes_test.rb37
-rw-r--r--activesupport/test/dependencies_test.rb6
-rw-r--r--activesupport/test/deprecation/method_wrappers_test.rb7
-rw-r--r--activesupport/test/deprecation_test.rb7
-rw-r--r--activesupport/test/descendants_tracker_test_cases.rb9
-rw-r--r--activesupport/test/evented_file_update_checker_test.rb28
-rw-r--r--activesupport/test/file_update_checker_shared_tests.rb12
-rw-r--r--activesupport/test/fixtures/concern/some_concern.rb11
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb10
-rw-r--r--activesupport/test/inflector_test.rb12
-rw-r--r--activesupport/test/json/encoding_test.rb24
-rw-r--r--activesupport/test/multibyte_chars_test.rb10
-rw-r--r--activesupport/test/notifications/evented_notification_test.rb33
-rw-r--r--activesupport/test/notifications/instrumenter_test.rb6
-rw-r--r--activesupport/test/notifications_test.rb23
-rw-r--r--activesupport/test/safe_buffer_test.rb34
-rw-r--r--activesupport/test/share_lock_test.rb104
-rw-r--r--activesupport/test/test_case_test.rb2
-rw-r--r--activesupport/test/time_travel_test.rb93
-rw-r--r--activesupport/test/time_zone_test.rb2
-rw-r--r--activesupport/test/transliterate_test.rb6
107 files changed, 1344 insertions, 754 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 98d232da33..f30249cb3f 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,134 @@
+* Fix bug in Range comparisons when comparing to an excluded-end Range
+
+ Before:
+
+ (1..10).cover?(1...11) => false
+
+ After:
+
+ (1..10).cover?(1...11) => true
+
+ With the same change for `Range#include?` and `Range#===`.
+
+ *Owen Stephens*
+
+* Use weak references in descendants tracker to allow anonymous subclasses to
+ be garbage collected.
+
+ *Edgars Beigarts*
+
+* Update `ActiveSupport::Notifications::Instrumenter#instrument` to make
+ passing a block optional. This will let users use
+ `ActiveSupport::Notifications` messaging features outside of
+ instrumentation.
+
+ *Ali Ibrahim*
+
+* Fix `Time#advance` to work with dates before 1001-03-07
+
+ Before:
+
+ Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-05 00:00:00 UTC
+
+ After
+
+ Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-06 00:00:00 UTC
+
+ Note that this doesn't affect `DateTime#advance` as that doesn't use a proleptic calendar.
+
+ *Andrew White*
+
+* In Zeitwerk mode, engines are now managed by the `main` autoloader. Engines may reference application constants, if the application is reloaded and we do not reload engines, they won't use the reloaded application code.
+
+ *Xavier Noria*
+
+* Add support for supplying `locale` to `transliterate` and `parameterize`.
+
+ I18n.backend.store_translations(:de, i18n: { transliterate: { rule: { "ü" => "ue" } } })
+
+ ActiveSupport::Inflector.transliterate("ü", locale: :de) => "ue"
+ "Fünf autos".parameterize(locale: :de) => "fuenf-autos"
+ ActiveSupport::Inflector.parameterize("Fünf autos", locale: :de) => "fuenf-autos"
+
+ *Kaan Ozkan*, *Sharang Dashputre*
+
+* Allow Array#excluding and Enumerable#excluding to deal with a passed array gracefully.
+
+ [ 1, 2, 3, 4, 5 ].excluding([4, 5]) => [ 1, 2, 3 ]
+
+ *DHH*
+
+* Renamed Array#without and Enumerable#without to Array#excluding and Enumerable#excluding, to create parity with
+ Array#including and Enumerable#including. Retained the old names as aliases.
+
+ *DHH*
+
+* Added Array#including and Enumerable#including to conveniently enlarge a collection with more members using a method rather than an operator:
+
+ [ 1, 2, 3 ].including(4, 5) => [ 1, 2, 3, 4, 5 ]
+ post.authors.including(Current.person) => All the authors plus the current person!
+
+ *DHH*
+
+
+## Rails 6.0.0.beta3 (March 11, 2019) ##
+
+* No changes.
+
+
+## Rails 6.0.0.beta2 (February 25, 2019) ##
+
+* New autoloading based on [Zeitwerk](https://github.com/fxn/zeitwerk).
+
+ *Xavier Noria*
+
+* Revise `ActiveSupport::Notifications.unsubscribe` to correctly handle Regex or other multiple-pattern subscribers.
+
+ *Zach Kemp*
+
+* Add `before_reset` callback to `CurrentAttributes` and define `after_reset` as an alias of `resets` for symmetry.
+
+ *Rosa Gutierrez*
+
+* Remove the `` Kernel#` `` override that suppresses ENOENT and accidentally returns nil on Unix systems.
+
+ *Akinori Musha*
+
+* Add `ActiveSupport::HashWithIndifferentAccess#assoc`.
+
+ `assoc` can now be called with either a string or a symbol.
+
+ *Stefan Schüßler*
+
+* Add `Hash#deep_transform_values`, and `Hash#deep_transform_values!`.
+
+ *Guillermo Iguaran*
+
+
+## Rails 6.0.0.beta1 (January 18, 2019) ##
+
+* Remove deprecated `Module#reachable?` method.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `#acronym_regex` method from `Inflections`.
+
+ *Rafael Mendonça França*
+
+* Fix `String#safe_constantize` throwing a `LoadError` for incorrectly cased constant references.
+
+ *Keenan Brock*
+
+* Preserve key order passed to `ActiveSupport::CacheStore#fetch_multi`.
+
+ `fetch_multi(*names)` now returns its results in the same order as the `*names` requested, rather than returning cache hits followed by cache misses.
+
+ *Gannon McGibbon*
+
+* 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`.
@@ -181,7 +312,7 @@
*Kasper Timm Hansen*
-* Fix bug where `ActiveSupport::Timezone.all` would fail when tzinfo data for
+* Fix bug where `ActiveSupport::TimeZone.all` would fail when tzinfo data for
any timezone defined in `ActiveSupport::TimeZone::MAPPING` is missing.
*Dominik Sander*
@@ -278,9 +409,9 @@
*Jeremy Daer*
-* Rails 6 requires Ruby 2.4.1 or newer.
+* Rails 6 requires Ruby 2.5.0 or newer.
- *Jeremy Daer*
+ *Jeremy Daer*, *Kasper Timm Hansen*
* Adds parallel testing to Rails.
diff --git a/activesupport/MIT-LICENSE b/activesupport/MIT-LICENSE
index 8f769c0767..315a4bc7c4 100644
--- a/activesupport/MIT-LICENSE
+++ b/activesupport/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2005-2018 David Heinemeier Hansson
+Copyright (c) 2005-2019 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc
index c770324be8..d8d25d86c8 100644
--- a/activesupport/README.rdoc
+++ b/activesupport/README.rdoc
@@ -5,6 +5,7 @@ extensions that were found useful for the Rails framework. These additions
reside in this package so they can be loaded as needed in Ruby projects
outside of Rails.
+You can read more about the extensions in the {Active Support Core Extensions}[https://edgeguides.rubyonrails.org/active_support_core_extensions.html] guide.
== Download and installation
@@ -28,7 +29,7 @@ Active Support is released under the MIT license:
API documentation is at:
-* http://api.rubyonrails.org
+* https://api.rubyonrails.org
Bug reports for the Ruby on Rails project can be filed here:
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index 448a2eeebb..bf0fe0f76d 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -9,13 +9,13 @@ Gem::Specification.new do |s|
s.summary = "A toolkit of support libraries and Ruby core extensions extracted from the Rails framework."
s.description = "A toolkit of support libraries and Ruby core extensions extracted from the Rails framework. Rich support for multibyte strings, internationalization, time zones, and testing."
- s.required_ruby_version = ">= 2.4.1"
+ s.required_ruby_version = ">= 2.5.0"
s.license = "MIT"
s.author = "David Heinemeier Hansson"
s.email = "david@loudthinking.com"
- s.homepage = "http://rubyonrails.org"
+ s.homepage = "https://rubyonrails.org"
s.files = Dir["CHANGELOG.md", "MIT-LICENSE", "README.rdoc", "lib/**/*"]
s.require_path = "lib"
@@ -30,8 +30,9 @@ Gem::Specification.new do |s|
# 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"
+ s.add_dependency "i18n", ">= 0.7", "< 2"
+ s.add_dependency "tzinfo", "~> 1.1"
+ s.add_dependency "minitest", "~> 5.1"
s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
+ s.add_dependency "zeitwerk", "~> 1.4", ">= 1.4.3"
end
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index a4fb697669..5589c71281 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
#--
-# Copyright (c) 2005-2018 David Heinemeier Hansson
+# Copyright (c) 2005-2019 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index e8518645d9..e055135bb4 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -438,18 +438,18 @@ module ActiveSupport
options = merged_options(options)
instrument :read_multi, names, options do |payload|
- read_multi_entries(names, options).tap do |results|
- payload[:hits] = results.keys
- payload[:super_operation] = :fetch_multi
+ reads = read_multi_entries(names, options)
+ writes = {}
+ ordered = names.each_with_object({}) do |name, hash|
+ hash[name] = reads.fetch(name) { writes[name] = yield(name) }
+ end
- writes = {}
+ payload[:hits] = reads.keys
+ payload[:super_operation] = :fetch_multi
- (names - results.keys).each do |name|
- results[name] = writes[name] = yield(name)
- end
+ write_multi(writes, options)
- write_multi writes, options
- end
+ ordered
end
end
@@ -492,7 +492,7 @@ module ActiveSupport
#
# Options are passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def delete_matched(matcher, options = nil)
raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
end
@@ -501,7 +501,7 @@ module ActiveSupport
#
# Options are passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def increment(name, amount = 1, options = nil)
raise NotImplementedError.new("#{self.class.name} does not support increment")
end
@@ -510,7 +510,7 @@ module ActiveSupport
#
# Options are passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def decrement(name, amount = 1, options = nil)
raise NotImplementedError.new("#{self.class.name} does not support decrement")
end
@@ -519,7 +519,7 @@ module ActiveSupport
#
# Options are passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def cleanup(options = nil)
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
end
@@ -529,7 +529,7 @@ module ActiveSupport
#
# The options hash is passed to the underlying cache implementation.
#
- # All implementations may not support this method.
+ # Some implementations may not support this method.
def clear(options = nil)
raise NotImplementedError.new("#{self.class.name} does not support clear")
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 53a2b07536..f43894a1ea 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -18,7 +18,6 @@ module ActiveSupport
DIR_FORMATTER = "%03X"
FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
- EXCLUDED_DIRS = [".", ".."].freeze
GITKEEP_FILES = [".gitkeep", ".keep"].freeze
def initialize(cache_path, options = nil)
@@ -35,7 +34,7 @@ module ActiveSupport
# file store directory except for .keep or .gitkeep. Be careful which directory is specified in your
# config file when using +FileStore+ because everything in that directory will be deleted.
def clear(options = nil)
- root_dirs = exclude_from(cache_path, EXCLUDED_DIRS + GITKEEP_FILES)
+ root_dirs = (Dir.children(cache_path) - GITKEEP_FILES)
FileUtils.rm_r(root_dirs.collect { |f| File.join(cache_path, f) })
rescue Errno::ENOENT
end
@@ -108,12 +107,10 @@ module ActiveSupport
def lock_file(file_name, &block)
if File.exist?(file_name)
File.open(file_name, "r+") do |f|
- begin
- f.flock File::LOCK_EX
- yield
- ensure
- f.flock File::LOCK_UN
- end
+ f.flock File::LOCK_EX
+ yield
+ ensure
+ f.flock File::LOCK_UN
end
else
yield
@@ -156,7 +153,7 @@ module ActiveSupport
# Delete empty directories in the cache.
def delete_empty_directories(dir)
return if File.realpath(dir) == File.realpath(cache_path)
- if exclude_from(dir, EXCLUDED_DIRS).empty?
+ if Dir.children(dir).empty?
Dir.delete(dir) rescue nil
delete_empty_directories(File.dirname(dir))
end
@@ -169,8 +166,7 @@ module ActiveSupport
def search_dir(dir, &callback)
return if !File.exist?(dir)
- Dir.foreach(dir) do |d|
- next if EXCLUDED_DIRS.include?(d)
+ Dir.each_child(dir) do |d|
name = File.join(dir, d)
if File.directory?(name)
search_dir(name, &callback)
@@ -195,11 +191,6 @@ module ActiveSupport
end
end
end
-
- # Exclude entries from source directory
- def exclude_from(source, excludes)
- Dir.entries(source).reject { |f| excludes.include?(f) }
- end
end
end
end
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 106b616529..629eb2dd70 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -62,13 +62,13 @@ module ActiveSupport
return if pruning?
@pruning = true
begin
- start_time = Time.now
+ start_time = Concurrent.monotonic_time
cleanup
instrument(:prune, target_size, from: @cache_size) do
keys = synchronize { @key_access.keys.sort { |a, b| @key_access[a].to_f <=> @key_access[b].to_f } }
keys.each do |key|
delete_entry(key, options)
- return if @cache_size <= target_size || (max_time && Time.now - start_time > max_time)
+ return if @cache_size <= target_size || (max_time && Concurrent.monotonic_time - start_time > max_time)
end
end
ensure
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 9acf674c40..71c23dae9b 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -67,8 +67,8 @@ module ActiveSupport
# end
# # => NameError: invalid config attribute name
#
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
#
# class User
# include ActiveSupport::Configurable
@@ -81,7 +81,7 @@ module ActiveSupport
# User.new.allowed_access = true # => NoMethodError
# User.new.allowed_access # => NoMethodError
#
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
#
# class User
# include ActiveSupport::Configurable
diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb
index a2569c798b..88b6567712 100644
--- a/activesupport/lib/active_support/core_ext/array.rb
+++ b/activesupport/lib/active_support/core_ext/array.rb
@@ -6,5 +6,4 @@ require "active_support/core_ext/array/conversions"
require "active_support/core_ext/array/extract"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/array/grouping"
-require "active_support/core_ext/array/prepend_and_append"
require "active_support/core_ext/array/inquiry"
diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb
index b7ff7a3907..10e4c6b09d 100644
--- a/activesupport/lib/active_support/core_ext/array/access.rb
+++ b/activesupport/lib/active_support/core_ext/array/access.rb
@@ -29,16 +29,28 @@ class Array
end
end
- # Returns a copy of the Array without the specified elements.
+ # Returns a new array that includes the passed elements.
#
- # people = ["David", "Rafael", "Aaron", "Todd"]
- # people.without "Aaron", "Todd"
- # # => ["David", "Rafael"]
+ # [ 1, 2, 3 ].including(4, 5) => [ 1, 2, 3, 4, 5 ]
+ # [ [ 0, 1 ] ].including([ [ 1, 0 ] ]) => [ [ 0, 1 ], [ 1, 0 ] ]
+ def including(*elements)
+ self + elements.flatten(1)
+ end
+
+ # Returns a copy of the Array excluding the specified elements.
+ #
+ # ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") => ["David", "Rafael"]
+ # [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) => [ [ 0, 1 ] ]
#
- # Note: This is an optimization of <tt>Enumerable#without</tt> that uses <tt>Array#-</tt>
+ # Note: This is an optimization of <tt>Enumerable#excluding</tt> that uses <tt>Array#-</tt>
# instead of <tt>Array#reject</tt> for performance reasons.
+ def excluding(*elements)
+ self - elements.flatten(1)
+ end
+
+ # Alias for #excluding.
def without(*elements)
- self - elements
+ excluding(*elements)
end
# Equal to <tt>self[1]</tt>.
diff --git a/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
index 661971d7cd..ba3739f640 100644
--- a/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
+++ b/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb
@@ -1,9 +1,5 @@
# frozen_string_literal: true
-class Array
- # The human way of thinking about adding stuff to the end of a list is with append.
- alias_method :append, :push unless [].respond_to?(:append)
+require "active_support/deprecation"
- # The human way of thinking about adding stuff to the beginning of a list is with prepend.
- alias_method :prepend, :unshift unless [].respond_to?(:prepend)
-end
+ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Array#append and Array#prepend natively, so requiring active_support/core_ext/array/prepend_and_append is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index fa33ff945f..255cbee55c 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -84,16 +84,17 @@ class Class
# To set a default value for the attribute, pass <tt>default:</tt>, like so:
#
# class_attribute :settings, default: {}
- def class_attribute(*attrs)
- options = attrs.extract_options!
- instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
- instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
- instance_predicate = options.fetch(:instance_predicate, true)
- default_value = options.fetch(:default, nil)
-
+ def class_attribute(
+ *attrs,
+ instance_accessor: true,
+ instance_reader: instance_accessor,
+ instance_writer: instance_accessor,
+ instance_predicate: true,
+ default: nil
+ )
attrs.each do |name|
singleton_class.silence_redefinition_of_method(name)
- define_singleton_method(name) { nil }
+ define_singleton_method(name) { default }
singleton_class.silence_redefinition_of_method("#{name}?")
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
@@ -102,9 +103,7 @@ class Class
singleton_class.silence_redefinition_of_method("#{name}=")
define_singleton_method("#{name}=") do |val|
- singleton_class.class_eval do
- redefine_method(name) { val }
- end
+ redefine_singleton_method(name) { val }
if singleton_class?
class_eval do
@@ -137,10 +136,6 @@ class Class
instance_variable_set ivar, val
end
end
-
- unless default_value.nil?
- self.send("#{name}=", default_value)
- end
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 1cd7acb05d..d03a8d3997 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -110,12 +110,13 @@ class Date
# Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
# any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
def advance(options)
- options = options.dup
d = self
- d = d >> options.delete(:years) * 12 if options[:years]
- d = d >> options.delete(:months) if options[:months]
- d = d + options.delete(:weeks) * 7 if options[:weeks]
- d = d + options.delete(:days) if options[:days]
+
+ d = d >> options[:years] * 12 if options[:years]
+ d = d >> options[:months] if options[:months]
+ d = d + options[:weeks] * 7 if options[:weeks]
+ d = d + options[:days] if options[:days]
+
d
end
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 05abd83221..e2e11545e2 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
@@ -134,7 +134,7 @@ module DateAndTime
# now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
# now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000
def beginning_of_quarter
- first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
+ first_quarter_month = month - (2 + month) % 3
beginning_of_month.change(month: first_quarter_month)
end
alias :at_beginning_of_quarter :beginning_of_quarter
@@ -149,7 +149,7 @@ module DateAndTime
# now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
# now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000
def end_of_quarter
- last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
+ last_quarter_month = month + (12 - month) % 3
beginning_of_month.change(month: last_quarter_month).end_of_month
end
alias :at_end_of_quarter :end_of_quarter
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index d87d63f287..4675c41936 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -97,23 +97,43 @@ module Enumerable
end
end
+ # Returns a new array that includes the passed elements.
+ #
+ # [ 1, 2, 3 ].including(4, 5)
+ # # => [ 1, 2, 3, 4, 5 ]
+ #
+ # ["David", "Rafael"].including %w[ Aaron Todd ]
+ # # => ["David", "Rafael", "Aaron", "Todd"]
+ def including(*elements)
+ to_a.including(*elements)
+ end
+
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
# collection does not include the object.
def exclude?(object)
!include?(object)
end
- # Returns a copy of the enumerable without the specified elements.
+ # Returns a copy of the enumerable excluding the specified elements.
+ #
+ # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
+ # # => ["David", "Rafael"]
#
- # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
# # => ["David", "Rafael"]
#
- # {foo: 1, bar: 2, baz: 3}.without :bar
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
# # => {foo: 1, baz: 3}
- def without(*elements)
+ def excluding(*elements)
+ elements.flatten!(1)
reject { |element| elements.include?(element) }
end
+ # Alias for #excluding.
+ def without(*elements)
+ excluding(*elements)
+ end
+
# Convert an enumerable to an array based on the given key.
#
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
diff --git a/activesupport/lib/active_support/core_ext/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.rb b/activesupport/lib/active_support/core_ext/hash.rb
index c4b9e5f1a0..2f0901d853 100644
--- a/activesupport/lib/active_support/core_ext/hash.rb
+++ b/activesupport/lib/active_support/core_ext/hash.rb
@@ -2,6 +2,7 @@
require "active_support/core_ext/hash/conversions"
require "active_support/core_ext/hash/deep_merge"
+require "active_support/core_ext/hash/deep_transform_values"
require "active_support/core_ext/hash/except"
require "active_support/core_ext/hash/indifferent_access"
require "active_support/core_ext/hash/keys"
diff --git a/activesupport/lib/active_support/core_ext/hash/compact.rb b/activesupport/lib/active_support/core_ext/hash/compact.rb
index 28c8d86b9b..5cb858af5c 100644
--- a/activesupport/lib/active_support/core_ext/hash/compact.rb
+++ b/activesupport/lib/active_support/core_ext/hash/compact.rb
@@ -2,4 +2,4 @@
require "active_support/deprecation"
-ActiveSupport::Deprecation.warn "Ruby 2.4+ (required by Rails 6) provides Hash#compact and Hash#compact! natively, so requiring active_support/core_ext/hash/compact is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
+ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Hash#compact and Hash#compact! natively, so requiring active_support/core_ext/hash/compact is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
diff --git a/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb b/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb
new file mode 100644
index 0000000000..720a1f67c8
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+class Hash
+ # Returns a new hash with all keys converted by the block operation.
+ # This includes the keys from the root hash and from all
+ # nested hashes and arrays.
+ #
+ # hash = { person: { name: 'Rob', age: '28' } }
+ #
+ # hash.deep_transform_values{ |value| value.to_s.upcase }
+ # # => {person: {name: "ROB", age: "28"}}
+ def deep_transform_values(&block)
+ _deep_transform_values_in_object(self, &block)
+ end
+
+ # Destructively converts all values by using the block operation.
+ # This includes the values from the root hash and from all
+ # nested hashes and arrays.
+ def deep_transform_values!(&block)
+ _deep_transform_values_in_object!(self, &block)
+ end
+
+ private
+ # support methods for deep transforming nested hashes and arrays
+ def _deep_transform_values_in_object(object, &block)
+ case object
+ when Hash
+ object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
+ when Array
+ object.map { |e| _deep_transform_values_in_object(e, &block) }
+ else
+ yield(object)
+ end
+ end
+
+ def _deep_transform_values_in_object!(object, &block)
+ case object
+ when Hash
+ object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) }
+ when Array
+ object.map! { |e| _deep_transform_values_in_object!(e, &block) }
+ else
+ yield(object)
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index bdf196ec3d..7d3495db2c 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -1,35 +1,6 @@
# frozen_string_literal: true
class Hash
- # Returns a new hash with all keys converted using the +block+ operation.
- #
- # hash = { name: 'Rob', age: '28' }
- #
- # hash.transform_keys { |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"}
- #
- # If you do not provide a +block+, it will return an Enumerator
- # for chaining with other methods:
- #
- # hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"}
- def transform_keys
- return enum_for(:transform_keys) { size } unless block_given?
- result = {}
- each_key do |key|
- result[yield(key)] = self[key]
- end
- result
- end unless method_defined? :transform_keys
-
- # Destructively converts all keys using the +block+ operations.
- # Same as +transform_keys+ but modifies +self+.
- def transform_keys!
- return enum_for(:transform_keys!) { size } unless block_given?
- keys.each do |key|
- self[yield(key)] = delete(key)
- end
- self
- end unless method_defined? :transform_keys!
-
# Returns a new hash with all keys converted to strings.
#
# hash = { name: 'Rob', age: '28' }
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index 2bd0a56ea4..3d0f8a1e62 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -1,34 +1,12 @@
# frozen_string_literal: true
class Hash
- # Slices a hash to include only the given keys. Returns a hash containing
- # the given keys.
- #
- # { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
- # # => {:a=>1, :b=>2}
- #
- # This is useful for limiting an options hash to valid keys before
- # passing to a method:
- #
- # def search(criteria = {})
- # criteria.assert_valid_keys(:mass, :velocity, :time)
- # end
- #
- # search(options.slice(:mass, :velocity, :time))
- #
- # If you have an array of keys you want to limit to, you should splat them:
- #
- # valid_keys = [:mass, :velocity, :time]
- # search(options.slice(*valid_keys))
- def slice(*keys)
- keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
- end unless method_defined?(:slice)
-
# Replaces the hash with only the given keys.
# Returns a hash containing the removed key/value pairs.
#
- # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
- # # => {:c=>3, :d=>4}
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
+ # hash.slice!(:a, :b) # => {:c=>3, :d=>4}
+ # hash # => {:a=>1, :b=>2}
def slice!(*keys)
omit = slice(*self.keys - keys)
hash = slice(*keys)
diff --git a/activesupport/lib/active_support/core_ext/hash/transform_values.rb b/activesupport/lib/active_support/core_ext/hash/transform_values.rb
index fc15130c9e..e4aeb0e891 100644
--- a/activesupport/lib/active_support/core_ext/hash/transform_values.rb
+++ b/activesupport/lib/active_support/core_ext/hash/transform_values.rb
@@ -2,4 +2,4 @@
require "active_support/deprecation"
-ActiveSupport::Deprecation.warn "Ruby 2.4+ (required by Rails 6) provides Hash#transform_values natively, so requiring active_support/core_ext/hash/transform_values is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
+ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Hash#transform_values natively, so requiring active_support/core_ext/hash/transform_values is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
diff --git a/activesupport/lib/active_support/core_ext/kernel.rb b/activesupport/lib/active_support/core_ext/kernel.rb
index 0f4356fbdd..7708069301 100644
--- a/activesupport/lib/active_support/core_ext/kernel.rb
+++ b/activesupport/lib/active_support/core_ext/kernel.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/kernel/agnostics"
require "active_support/core_ext/kernel/concern"
require "active_support/core_ext/kernel/reporting"
require "active_support/core_ext/kernel/singleton_class"
diff --git a/activesupport/lib/active_support/core_ext/kernel/agnostics.rb b/activesupport/lib/active_support/core_ext/kernel/agnostics.rb
deleted file mode 100644
index 403b5f31f0..0000000000
--- a/activesupport/lib/active_support/core_ext/kernel/agnostics.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-class Object
- # Makes backticks behave (somewhat more) similarly on all platforms.
- # On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
- # spawned shell prints a message to stderr and sets $?. We emulate
- # Unix on the former but not the latter.
- def `(command) #:nodoc:
- super
- rescue Errno::ENOENT => e
- STDERR.puts "#$0: #{e}"
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb
index d91e3fba6a..542af98c04 100644
--- a/activesupport/lib/active_support/core_ext/module.rb
+++ b/activesupport/lib/active_support/core_ext/module.rb
@@ -3,7 +3,6 @@
require "active_support/core_ext/module/aliasing"
require "active_support/core_ext/module/introspection"
require "active_support/core_ext/module/anonymous"
-require "active_support/core_ext/module/reachable"
require "active_support/core_ext/module/attribute_accessors"
require "active_support/core_ext/module/attribute_accessors_per_thread"
require "active_support/core_ext/module/attr_internal"
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
index 5850e0193f..cc1926e022 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -24,7 +24,7 @@ class Module
# end
# # => NameError: invalid attribute name: 1_Badname
#
- # If you want to opt out the creation on the instance reader method, pass
+ # To omit the instance reader method, pass
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
#
# module HairColors
@@ -91,7 +91,7 @@ class Module
# Person.new.hair_colors = [:blonde, :red]
# HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
#
- # If you want to opt out the instance writer method, pass
+ # To omit the instance writer method, pass
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
#
# module HairColors
@@ -166,8 +166,8 @@ class Module
# Citizen.new.hair_colors << :blue
# Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
#
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
#
# module HairColors
# mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
@@ -180,7 +180,7 @@ class Module
# Person.new.hair_colors = [:brown] # => NoMethodError
# Person.new.hair_colors # => NoMethodError
#
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
#
# module HairColors
# mattr_accessor :hair_colors, instance_accessor: false
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
index cb996e9e6d..a6e87aeb68 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
@@ -25,7 +25,7 @@ class Module
# end
# # => NameError: invalid attribute name: 1_Badname
#
- # If you want to opt out of the creation of the instance reader method, pass
+ # To omit the instance reader method, pass
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
#
# class Current
@@ -66,7 +66,7 @@ class Module
# Current.user = "DHH"
# Thread.current[:attr_Current_user] # => "DHH"
#
- # If you want to opt out of the creation of the instance writer method, pass
+ # To omit the instance writer method, pass
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
#
# class Current
@@ -118,8 +118,8 @@ class Module
# Customer.user # => "Rafael"
# Account.user # => "DHH"
#
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
#
# class Current
# thread_mattr_accessor :user, instance_writer: false, instance_reader: false
@@ -128,7 +128,7 @@ class Module
# Current.new.user = "DHH" # => NoMethodError
# Current.new.user # => NoMethodError
#
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
#
# class Current
# thread_mattr_accessor :user, instance_accessor: false
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 3128539112..5652f2d1cc 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -19,7 +19,7 @@ class Module
# public methods as your own.
#
# ==== Options
- # * <tt>:to</tt> - Specifies the target object
+ # * <tt>:to</tt> - Specifies the target object name as a symbol or string
# * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
# * <tt>:allow_nil</tt> - If set to true, prevents a +Module::DelegationError+
# from being raised
@@ -243,7 +243,7 @@ class Module
# end
#
# def person
- # @event.detail.person || @event.creator
+ # detail.person || creator
# end
#
# private
@@ -266,7 +266,7 @@ class Module
# end
#
# def person
- # @event.detail.person || @event.creator
+ # detail.person || creator
# end
# end
#
diff --git a/activesupport/lib/active_support/core_ext/module/reachable.rb b/activesupport/lib/active_support/core_ext/module/reachable.rb
index e9cbda5245..2020f5204c 100644
--- a/activesupport/lib/active_support/core_ext/module/reachable.rb
+++ b/activesupport/lib/active_support/core_ext/module/reachable.rb
@@ -3,9 +3,4 @@
require "active_support/core_ext/module/anonymous"
require "active_support/core_ext/string/inflections"
-class Module
- def reachable? #:nodoc:
- !anonymous? && name.safe_constantize.equal?(self)
- end
- deprecate :reachable?
-end
+ActiveSupport::Deprecation.warn("reachable is deprecated and will be removed from the framework.")
diff --git a/activesupport/lib/active_support/core_ext/numeric/inquiry.rb b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb
index e05e40825b..6b5240d051 100644
--- a/activesupport/lib/active_support/core_ext/numeric/inquiry.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb
@@ -2,4 +2,4 @@
require "active_support/deprecation"
-ActiveSupport::Deprecation.warn "Ruby 2.4+ (required by Rails 6) provides Numeric#positive? and Numeric#negative? natively, so requiring active_support/core_ext/numeric/inquiry is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
+ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Numeric#positive? and Numeric#negative? natively, so requiring active_support/core_ext/numeric/inquiry is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
diff --git a/activesupport/lib/active_support/core_ext/range/compare_range.rb b/activesupport/lib/active_support/core_ext/range/compare_range.rb
index 6f6d2a27bb..ea1dc29a76 100644
--- a/activesupport/lib/active_support/core_ext/range/compare_range.rb
+++ b/activesupport/lib/active_support/core_ext/range/compare_range.rb
@@ -3,9 +3,10 @@
module ActiveSupport
module CompareWithRange
# Extends the default Range#=== to support range comparisons.
- # (1..5) === (1..5) # => true
- # (1..5) === (2..3) # => true
- # (1..5) === (2..6) # => false
+ # (1..5) === (1..5) # => true
+ # (1..5) === (2..3) # => true
+ # (1..5) === (1...6) # => true
+ # (1..5) === (2..6) # => false
#
# The native Range#=== behavior is untouched.
# ('a'..'f') === ('c') # => true
@@ -13,17 +14,20 @@ module ActiveSupport
def ===(value)
if value.is_a?(::Range)
# 1...10 includes 1..9 but it does not include 1..10.
+ # 1..10 includes 1...11 but it does not include 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
- super(value.first) && value.last.send(operator, last)
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
+ super(value.first) && value_max.send(operator, last)
else
super
end
end
# Extends the default Range#include? to support range comparisons.
- # (1..5).include?(1..5) # => true
- # (1..5).include?(2..3) # => true
- # (1..5).include?(2..6) # => false
+ # (1..5).include?(1..5) # => true
+ # (1..5).include?(2..3) # => true
+ # (1..5).include?(1...6) # => true
+ # (1..5).include?(2..6) # => false
#
# The native Range#include? behavior is untouched.
# ('a'..'f').include?('c') # => true
@@ -31,17 +35,20 @@ module ActiveSupport
def include?(value)
if value.is_a?(::Range)
# 1...10 includes 1..9 but it does not include 1..10.
+ # 1..10 includes 1...11 but it does not include 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
- super(value.first) && value.last.send(operator, last)
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
+ super(value.first) && value_max.send(operator, last)
else
super
end
end
# Extends the default Range#cover? to support range comparisons.
- # (1..5).cover?(1..5) # => true
- # (1..5).cover?(2..3) # => true
- # (1..5).cover?(2..6) # => false
+ # (1..5).cover?(1..5) # => true
+ # (1..5).cover?(2..3) # => true
+ # (1..5).cover?(1...6) # => true
+ # (1..5).cover?(2..6) # => false
#
# The native Range#cover? behavior is untouched.
# ('a'..'f').cover?('c') # => true
@@ -49,8 +56,10 @@ module ActiveSupport
def cover?(value)
if value.is_a?(::Range)
# 1...10 covers 1..9 but it does not cover 1..10.
+ # 1..10 covers 1...11 but it does not cover 1...12.
operator = exclude_end? && !value.exclude_end? ? :< : :<=
- super(value.first) && value.last.send(operator, last)
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
+ super(value.first) && value_max.send(operator, last)
else
super
end
diff --git a/activesupport/lib/active_support/core_ext/securerandom.rb b/activesupport/lib/active_support/core_ext/securerandom.rb
index b4a491f5fd..ef812f7e1a 100644
--- a/activesupport/lib/active_support/core_ext/securerandom.rb
+++ b/activesupport/lib/active_support/core_ext/securerandom.rb
@@ -4,17 +4,18 @@ require "securerandom"
module SecureRandom
BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"]
+ BASE36_ALPHABET = ("0".."9").to_a + ("a".."z").to_a
+
# SecureRandom.base58 generates a random base58 string.
#
- # The argument _n_ specifies the length, of the random string to be generated.
+ # The argument _n_ specifies the length of the random string to be generated.
#
# If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
#
- # The result may contain alphanumeric characters except 0, O, I and l
+ # The result may contain alphanumeric characters except 0, O, I and l.
#
# p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
# p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
- #
def self.base58(n = 16)
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
idx = byte % 64
@@ -22,4 +23,23 @@ module SecureRandom
BASE58_ALPHABET[idx]
end.join
end
+
+ # SecureRandom.base36 generates a random base36 string in lowercase.
+ #
+ # The argument _n_ specifies the length of the random string to be generated.
+ #
+ # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
+ # This method can be used over +base58+ if a deterministic case key is necessary.
+ #
+ # The result will contain alphanumeric characters in lowercase.
+ #
+ # p SecureRandom.base36 # => "4kugl2pdqmscqtje"
+ # p SecureRandom.base36(24) # => "77tmhrhjfvfdwodq8w7ev2m7"
+ def self.base36(n = 16)
+ SecureRandom.random_bytes(n).unpack("C*").map do |byte|
+ idx = byte % 64
+ idx = SecureRandom.random_number(36) if idx >= 36
+ BASE36_ALPHABET[idx]
+ end.join
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 8af301734a..5eb8d9f99b 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -162,6 +162,11 @@ class String
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
#
+ # If the optional parameter +locale+ is specified,
+ # the word will be parameterized as a word of that language.
+ # By default, this parameter is set to <tt>nil</tt> and it will use
+ # the configured <tt>I18n.locale</tt>.
+ #
# class Person
# def to_param
# "#{id}-#{name.parameterize}"
@@ -187,8 +192,8 @@ class String
#
# <%= link_to(@person.name, person_path) %>
# # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
- def parameterize(separator: "-", preserve_case: false)
- ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
+ def parameterize(separator: "-", preserve_case: false, locale: nil)
+ ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
end
# Creates the name of a table like Rails does for models to table names. This method
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index 1371abbc4a..638152626b 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -201,8 +201,12 @@ module ActiveSupport #:nodoc:
super(html_escape_interpolated_argument(value))
end
- def []=(index, value)
- super(index, html_escape_interpolated_argument(value))
+ def []=(*args)
+ if args.count == 3
+ super(args[0], args[1], html_escape_interpolated_argument(args[2]))
+ else
+ super(args[0], html_escape_interpolated_argument(args[1]))
+ end
end
def +(other)
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 120768dec5..f09a6271ad 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -170,8 +170,7 @@ class Time
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
end
- d = to_date.advance(options)
- d = d.gregorian if d.julian?
+ d = to_date.gregorian.advance(options)
time_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
seconds_to_advance = \
options.fetch(:seconds, 0) +
diff --git a/activesupport/lib/active_support/current_attributes.rb b/activesupport/lib/active_support/current_attributes.rb
index 3145ff87a1..67ebe102d7 100644
--- a/activesupport/lib/active_support/current_attributes.rb
+++ b/activesupport/lib/active_support/current_attributes.rb
@@ -119,10 +119,16 @@ module ActiveSupport
end
end
+ # Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
+ def before_reset(&block)
+ set_callback :reset, :before, &block
+ end
+
# Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
def resets(&block)
set_callback :reset, :after, &block
end
+ alias_method :after_reset, :resets
delegate :set, :reset, to: :instance
diff --git a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb
new file mode 100644
index 0000000000..c6fdade006
--- /dev/null
+++ b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/string/inflections"
+
+module ActiveSupport
+ module Dependencies
+ module ZeitwerkIntegration # :nodoc: all
+ module Decorations
+ def clear
+ Dependencies.unload_interlock do
+ Rails.autoloaders.main.reload
+ end
+ end
+
+ def constantize(cpath)
+ ActiveSupport::Inflector.constantize(cpath)
+ end
+
+ def safe_constantize(cpath)
+ ActiveSupport::Inflector.safe_constantize(cpath)
+ end
+
+ def autoloaded_constants
+ cpaths = []
+ Rails.autoloaders.each do |autoloader|
+ cpaths.concat(autoloader.loaded_cpaths.to_a)
+ end
+ cpaths
+ end
+
+ def autoloaded?(object)
+ cpath = object.is_a?(Module) ? object.name : object.to_s
+ Rails.autoloaders.any? { |autoloader| autoloader.loaded?(cpath) }
+ end
+
+ def verbose=(verbose)
+ l = verbose ? logger || Rails.logger : nil
+ Rails.autoloaders.each { |autoloader| autoloader.logger = l }
+ end
+
+ def unhook!
+ :no_op
+ end
+ end
+
+ module Inflector
+ def self.camelize(basename, _abspath)
+ basename.camelize
+ end
+ end
+
+ class << self
+ def take_over
+ setup_autoloaders
+ freeze_autoload_paths
+ decorate_dependencies
+ end
+
+ private
+
+ def setup_autoloaders
+ Dependencies.autoload_paths.each do |autoload_path|
+ # Zeitwerk only accepts existing directories in `push_dir` to
+ # prevent misconfigurations.
+ next unless File.directory?(autoload_path)
+
+ if autoload_once?(autoload_path)
+ Rails.autoloaders.once.push_dir(autoload_path)
+ else
+ Rails.autoloaders.main.push_dir(autoload_path)
+ end
+ end
+
+ Rails.autoloaders.each(&:setup)
+ end
+
+ def autoload_once?(autoload_path)
+ Dependencies.autoload_once_paths.include?(autoload_path)
+ end
+
+ def freeze_autoload_paths
+ Dependencies.autoload_paths.freeze
+ Dependencies.autoload_once_paths.freeze
+ end
+
+ def decorate_dependencies
+ Dependencies.unhook!
+ Dependencies.singleton_class.prepend(Decorations)
+ Object.class_eval { alias_method :require_dependency, :require }
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb
index 81482092fe..d99571790f 100644
--- a/activesupport/lib/active_support/deprecation/method_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb
@@ -52,27 +52,37 @@ module ActiveSupport
options = method_names.extract_options!
deprecator = options.delete(:deprecator) || self
method_names += options.keys
+ mod = Module.new
method_names.each do |method_name|
- aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ""), $1
- with_method = "#{aliased_method}_with_deprecation#{punctuation}"
- without_method = "#{aliased_method}_without_deprecation#{punctuation}"
+ if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name)
+ aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ""), $1
+ with_method = "#{aliased_method}_with_deprecation#{punctuation}"
+ without_method = "#{aliased_method}_without_deprecation#{punctuation}"
- target_module.send(:define_method, with_method) do |*args, &block|
- deprecator.deprecation_warning(method_name, options[method_name])
- send(without_method, *args, &block)
- end
+ target_module.define_method(with_method) do |*args, &block|
+ deprecator.deprecation_warning(method_name, options[method_name])
+ send(without_method, *args, &block)
+ end
- target_module.send(:alias_method, without_method, method_name)
- target_module.send(:alias_method, method_name, with_method)
+ target_module.alias_method(without_method, method_name)
+ target_module.alias_method(method_name, with_method)
- case
- when target_module.protected_method_defined?(without_method)
- target_module.send(:protected, method_name)
- when target_module.private_method_defined?(without_method)
- target_module.send(:private, method_name)
+ case
+ when target_module.protected_method_defined?(without_method)
+ target_module.send(:protected, method_name)
+ when target_module.private_method_defined?(without_method)
+ target_module.send(:private, method_name)
+ end
+ else
+ mod.define_method(method_name) do |*args, &block|
+ deprecator.deprecation_warning(method_name, options[method_name])
+ super(*args, &block)
+ end
end
end
+
+ target_module.prepend(mod) unless mod.instance_methods(false).empty?
end
end
end
diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb
index a4cee788b6..2dca990712 100644
--- a/activesupport/lib/active_support/descendants_tracker.rb
+++ b/activesupport/lib/active_support/descendants_tracker.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "weakref"
+
module ActiveSupport
# This module provides an internal implementation to track descendants
# which is faster than iterating through ObjectSpace.
@@ -8,7 +10,8 @@ module ActiveSupport
class << self
def direct_descendants(klass)
- @@direct_descendants[klass] || []
+ descendants = @@direct_descendants[klass]
+ descendants ? descendants.to_a : []
end
def descendants(klass)
@@ -34,16 +37,19 @@ module ActiveSupport
# This is the only method that is not thread safe, but is only ever called
# during the eager loading phase.
def store_inherited(klass, descendant)
- (@@direct_descendants[klass] ||= []) << descendant
+ (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
end
private
- def accumulate_descendants(klass, acc)
- if direct_descendants = @@direct_descendants[klass]
- acc.concat(direct_descendants)
- direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
+
+ def accumulate_descendants(klass, acc)
+ if direct_descendants = @@direct_descendants[klass]
+ direct_descendants.each do |direct_descendant|
+ acc << direct_descendant
+ accumulate_descendants(direct_descendant, acc)
+ end
+ end
end
- end
end
def inherited(base)
@@ -58,5 +64,46 @@ module ActiveSupport
def descendants
DescendantsTracker.descendants(self)
end
+
+ # DescendantsArray is an array that contains weak references to classes.
+ class DescendantsArray # :nodoc:
+ include Enumerable
+
+ def initialize
+ @refs = []
+ end
+
+ def initialize_copy(orig)
+ @refs = @refs.dup
+ end
+
+ def <<(klass)
+ cleanup!
+ @refs << WeakRef.new(klass)
+ end
+
+ def each
+ @refs.each do |ref|
+ yield ref.__getobj__
+ rescue WeakRef::RefError
+ end
+ end
+
+ def refs_size
+ @refs.size
+ end
+
+ def cleanup!
+ @refs.delete_if { |ref| !ref.weakref_alive? }
+ end
+
+ def reject!
+ @refs.reject! do |ref|
+ yield ref.__getobj__
+ rescue WeakRef::RefError
+ true
+ end
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 88897f811e..97b4634d7b 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -214,8 +214,11 @@ module ActiveSupport
end
def coerce(other) #:nodoc:
- if Scalar === other
+ case other
+ when Scalar
[other, self]
+ when Duration
+ [Scalar.new(other.value), self]
else
[Scalar.new(other), self]
end
@@ -373,7 +376,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/encrypted_file.rb b/activesupport/lib/active_support/encrypted_file.rb
index c66f1b557e..2b7db568a5 100644
--- a/activesupport/lib/active_support/encrypted_file.rb
+++ b/activesupport/lib/active_support/encrypted_file.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "pathname"
+require "tmpdir"
require "active_support/message_encryptor"
module ActiveSupport
@@ -67,7 +68,7 @@ module ActiveSupport
write(updated_contents) if updated_contents != contents
ensure
- FileUtils.rm(tmp_path) if tmp_path.exist?
+ FileUtils.rm(tmp_path) if tmp_path&.exist?
end
diff --git a/activesupport/lib/active_support/evented_file_update_checker.rb b/activesupport/lib/active_support/evented_file_update_checker.rb
index 97e982eb05..3893b0de0e 100644
--- a/activesupport/lib/active_support/evented_file_update_checker.rb
+++ b/activesupport/lib/active_support/evented_file_update_checker.rb
@@ -52,16 +52,17 @@ module ActiveSupport
@pid = Process.pid
@boot_mutex = Mutex.new
- if (@dtw = directories_to_watch).any?
+ dtw = directories_to_watch
+ @dtw, @missing = dtw.partition(&:exist?)
+
+ if @dtw.any?
# Loading listen triggers warnings. These are originated by a legit
# usage of attr_* macros for private attributes, but adds a lot of noise
# to our test suite. Thus, we lazy load it and disable warnings locally.
silence_warnings do
- begin
- require "listen"
- rescue LoadError => e
- raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
- end
+ require "listen"
+ rescue LoadError => e
+ raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
end
end
boot!
@@ -75,6 +76,19 @@ module ActiveSupport
@updated.make_true
end
end
+
+ if @missing.any?(&:exist?)
+ @boot_mutex.synchronize do
+ appeared, @missing = @missing.partition(&:exist?)
+ shutdown!
+
+ @dtw += appeared
+ boot!
+
+ @updated.make_true
+ end
+ end
+
@updated.true?
end
@@ -96,6 +110,10 @@ module ActiveSupport
Listen.to(*@dtw, &method(:changed)).start
end
+ def shutdown!
+ Listen.stop
+ end
+
def changed(modified, added, removed)
unless updated?
@updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
@@ -113,7 +131,9 @@ module ActiveSupport
ext = @ph.normalize_extension(file.extname)
file.dirname.ascend do |dir|
- if @dirs.fetch(dir, []).include?(ext)
+ matching = @dirs[dir]
+
+ if matching && (matching.empty? || matching.include?(ext))
break true
elsif dir == @lcsp || dir.root?
break false
@@ -123,7 +143,7 @@ module ActiveSupport
end
def directories_to_watch
- dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
+ dtw = @files.map(&:dirname) + @dirs.keys
dtw.compact!
dtw.uniq!
diff --git a/activesupport/lib/active_support/gem_version.rb b/activesupport/lib/active_support/gem_version.rb
index c951ad16a3..cf17922d7d 100644
--- a/activesupport/lib/active_support/gem_version.rb
+++ b/activesupport/lib/active_support/gem_version.rb
@@ -10,7 +10,7 @@ module ActiveSupport
MAJOR = 6
MINOR = 0
TINY = 0
- PRE = "alpha"
+ PRE = "beta3"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index f1af76019a..3a2b2652c4 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -164,6 +164,19 @@ module ActiveSupport
super(convert_key(key))
end
+ # Same as <tt>Hash#assoc</tt> where the key passed as argument can be
+ # either a string or a symbol:
+ #
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
+ # counters[:foo] = 1
+ #
+ # counters.assoc('foo') # => ["foo", 1]
+ # counters.assoc(:foo) # => ["foo", 1]
+ # counters.assoc(:zoo) # => nil
+ def assoc(key)
+ super(convert_key(key))
+ end
+
# Same as <tt>Hash#fetch</tt> where the key passed as argument can be
# either a string or a symbol:
#
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index 2b86d233e5..88cdd99dbd 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "concurrent/map"
-require "active_support/core_ext/array/prepend_and_append"
require "active_support/i18n"
require "active_support/deprecation"
@@ -66,8 +65,7 @@ module ActiveSupport
@__instance__[locale] ||= new
end
- attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
- deprecate :acronym_regex
+ attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms
attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 1af9833d46..ee193add6f 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -328,6 +328,8 @@ module ActiveSupport
e.name.to_s == camel_cased_word.to_s)
rescue ArgumentError => e
raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
+ rescue LoadError => e
+ raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(e.message)
end
# Returns the suffix that should be added to a number to denote the position
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index 0d2a17970f..ec6e9ccb59 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -51,19 +51,18 @@ module ActiveSupport
#
# Now you can have different transliterations for each locale:
#
- # I18n.locale = :en
- # transliterate('Jürgen')
+ # transliterate('Jürgen', locale: :en)
# # => "Jurgen"
#
- # I18n.locale = :de
- # transliterate('Jürgen')
+ # transliterate('Jürgen', locale: :de)
# # => "Juergen"
- def transliterate(string, replacement = "?")
+ def transliterate(string, replacement = "?", locale: nil)
raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
I18n.transliterate(
ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
- replacement: replacement
+ replacement: replacement,
+ locale: locale
)
end
@@ -75,8 +74,8 @@ module ActiveSupport
#
# To use a custom separator, override the +separator+ argument.
#
- # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
- # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
#
# To preserve the case of the characters in a string, use the +preserve_case+ argument.
#
@@ -85,13 +84,17 @@ module ActiveSupport
#
# It preserves dashes and underscores unless they are used as separators:
#
- # parameterize("^très|Jolie__ ") # => "tres-jolie__"
- # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
- # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
#
- def parameterize(string, separator: "-", preserve_case: false)
+ # If the optional parameter +locale+ is specified,
+ # the word will be parameterized as a word of that language.
+ # By default, this parameter is set to <tt>nil</tt> and it will use
+ # the configured <tt>I18n.locale<tt>.
+ def parameterize(string, separator: "-", preserve_case: false, locale: nil)
# Replace accented chars with their ASCII equivalents.
- parameterized_string = transliterate(string)
+ parameterized_string = transliterate(string, locale: locale)
# Turn unwanted chars into the separator.
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
diff --git a/activesupport/lib/active_support/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 00edcdd05a..8b61982883 100644
--- a/activesupport/lib/active_support/key_generator.rb
+++ b/activesupport/lib/active_support/key_generator.rb
@@ -38,36 +38,4 @@ module ActiveSupport
@cache_keys[args.join] ||= @key_generator.generate_key(*args)
end
end
-
- class LegacyKeyGenerator # :nodoc:
- SECRET_MIN_LENGTH = 30 # Characters
-
- def initialize(secret)
- ensure_secret_secure(secret)
- @secret = secret
- end
-
- def generate_key(salt)
- @secret
- end
-
- private
-
- # To prevent users from using something insecure like "Password" we make sure that the
- # secret they've provided is at least 30 characters in length.
- def ensure_secret_secure(secret)
- 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 by running `rails credentials:edit`."
- end
-
- if secret.length < SECRET_MIN_LENGTH
- raise ArgumentError, "Secret should be something secure, " \
- "like \"#{SecureRandom.hex(16)}\". The value you " \
- "provided, \"#{secret}\", is shorter than the minimum length " \
- "of #{SECRET_MIN_LENGTH} characters."
- end
- end
- end
end
diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb
index 0f7be06c8e..938cfdb914 100644
--- a/activesupport/lib/active_support/log_subscriber.rb
+++ b/activesupport/lib/active_support/log_subscriber.rb
@@ -5,8 +5,8 @@ require "active_support/core_ext/class/attribute"
require "active_support/subscriber"
module ActiveSupport
- # ActiveSupport::LogSubscriber is an object set to consume
- # ActiveSupport::Notifications with the sole purpose of logging them.
+ # <tt>ActiveSupport::LogSubscriber</tt> is an object set to consume
+ # <tt>ActiveSupport::Notifications</tt> with the sole purpose of logging them.
# The log subscriber dispatches notifications to a registered object based
# on its given namespace.
#
@@ -16,7 +16,7 @@ module ActiveSupport
# module ActiveRecord
# class LogSubscriber < ActiveSupport::LogSubscriber
# def sql(event)
- # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
+ # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
# end
# end
# end
@@ -29,13 +29,36 @@ module ActiveSupport
# subscriber, the line above should be called after your
# <tt>ActiveRecord::LogSubscriber</tt> definition.
#
- # After configured, whenever a "sql.active_record" notification is published,
- # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
- # the sql method.
+ # After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
+ # it will properly dispatch the event
+ # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
+ #
+ # Being an <tt>ActiveSupport::Notifications</tt> consumer,
+ # <tt>ActiveSupport::LogSubscriber</tt> exposes a simple interface to check if
+ # instrumented code raises an exception. It is common to log a different
+ # message in case of an error, and this can be achieved by extending
+ # the previous example:
+ #
+ # module ActiveRecord
+ # class LogSubscriber < ActiveSupport::LogSubscriber
+ # def sql(event)
+ # exception = event.payload[:exception]
+ #
+ # if exception
+ # exception_object = event.payload[:exception_object]
+ #
+ # error "[ERROR] #{event.payload[:name]}: #{exception.join(', ')} " \
+ # "(#{exception_object.backtrace.first})"
+ # else
+ # # standard logger code
+ # end
+ # end
+ # end
+ # end
#
# Log subscriber also has some helpers to deal with logging and automatically
- # flushes all logs when the request finishes (via action_dispatch.callback
- # notification) in a Rails environment.
+ # flushes all logs when the request finishes
+ # (via <tt>action_dispatch.callback</tt> notification) in a Rails environment.
class LogSubscriber < Subscriber
# Embed in a String to clear all previous ANSI sequences.
CLEAR = "\e[0m"
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 6f7302e732..7d6f8937f0 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -53,7 +53,7 @@ module ActiveSupport
# crypt.encrypt_and_sign(parcel, expires_in: 1.month)
# crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
#
- # Then the messages can be verified and returned upto the expire time.
+ # Then the messages can be verified and returned up to the expire time.
# Thereafter, verifying returns +nil+.
#
# === Rotating keys
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 64c557bec6..c4a4afe95f 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -71,7 +71,7 @@ module ActiveSupport
# @verifier.generate(parcel, expires_in: 1.month)
# @verifier.generate(doowad, expires_at: Time.now.end_of_year)
#
- # Then the messages can be verified and returned upto the expire time.
+ # Then the messages can be verified and returned up to the expire time.
# Thereafter, the +verified+ method returns +nil+ while +verify+ raises
# <tt>ActiveSupport::MessageVerifier::InvalidSignature</tt>.
#
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 01cc363e2b..d9e93b530c 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -67,9 +67,12 @@ module ActiveSupport
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
# the name of the exception class, and the exception message.
# The <tt>:exception_object</tt> key of the payload will have the exception
- # itself as the value.
+ # itself as the value:
#
- # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
+ # event.payload[:exception] # => ["ArgumentError", "Invalid value"]
+ # event.payload[:exception_object] # => #<ArgumentError: Invalid value>
+ #
+ # As the earlier example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
# is able to take the arguments as they come and provide an object-oriented
# interface to that data.
#
@@ -150,6 +153,15 @@ module ActiveSupport
#
# ActiveSupport::Notifications.unsubscribe("render")
#
+ # Subscribers using a regexp or other pattern-matching object will remain subscribed
+ # to all events that match their original pattern, unless those events match a string
+ # passed to `unsubscribe`:
+ #
+ # subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
+ # ActiveSupport::Notifications.unsubscribe('render_template.action_view')
+ # subscriber.matches?('render_template.action_view') # => false
+ # subscriber.matches?('render_partial.action_view') # => true
+ #
# == Default Queue
#
# Notifications ships with a queue implementation that consumes and publishes events
@@ -177,7 +189,7 @@ module ActiveSupport
# names, or by passing a Regexp to match all events that match a pattern.
#
# ActiveSupport::Notifications.subscribe(/render/) do |*args|
- # ...
+ # @event = ActiveSupport::Notifications::Event.new(*args)
# end
#
# The +block+ will receive five parameters with information about the event:
@@ -189,6 +201,13 @@ module ActiveSupport
# id # => String, unique ID for the instrumenter that fired the event
# payload # => Hash, the payload
# end
+ #
+ # If the block passed to the method only takes one parameter,
+ # it will yield an event object to the block:
+ #
+ # ActiveSupport::Notifications.subscribe(/render/) do |event|
+ # @event = event
+ # end
def subscribe(*args, &block)
notifier.subscribe(*args, &block)
end
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 4e4ca70942..c506b35b1e 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -2,6 +2,7 @@
require "mutex_m"
require "concurrent/map"
+require "set"
module ActiveSupport
module Notifications
@@ -13,16 +14,22 @@ module ActiveSupport
include Mutex_m
def initialize
- @subscribers = []
+ @string_subscribers = Hash.new { |h, k| h[k] = [] }
+ @other_subscribers = []
@listeners_for = Concurrent::Map.new
super
end
- def subscribe(pattern = nil, block = Proc.new)
- subscriber = Subscribers.new pattern, block
+ def subscribe(pattern = nil, callable = nil, &block)
+ subscriber = Subscribers.new(pattern, callable || block)
synchronize do
- @subscribers << subscriber
- @listeners_for.clear
+ if String === pattern
+ @string_subscribers[pattern] << subscriber
+ @listeners_for.delete(pattern)
+ else
+ @other_subscribers << subscriber
+ @listeners_for.clear
+ end
end
subscriber
end
@@ -31,12 +38,19 @@ module ActiveSupport
synchronize do
case subscriber_or_name
when String
- @subscribers.reject! { |s| s.matches?(subscriber_or_name) }
+ @string_subscribers[subscriber_or_name].clear
+ @listeners_for.delete(subscriber_or_name)
+ @other_subscribers.each { |sub| sub.unsubscribe!(subscriber_or_name) }
else
- @subscribers.delete(subscriber_or_name)
+ pattern = subscriber_or_name.try(:pattern)
+ if String === pattern
+ @string_subscribers[pattern].delete(subscriber_or_name)
+ @listeners_for.delete(pattern)
+ else
+ @other_subscribers.delete(subscriber_or_name)
+ @listeners_for.clear
+ end
end
-
- @listeners_for.clear
end
end
@@ -56,7 +70,8 @@ module ActiveSupport
# this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
@listeners_for[name] || synchronize do
# use synchronisation when accessing @subscribers
- @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
+ @listeners_for[name] ||=
+ @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
end
end
@@ -100,9 +115,33 @@ module ActiveSupport
end
end
+ class Matcher #:nodoc:
+ attr_reader :pattern, :exclusions
+
+ def self.wrap(pattern)
+ return pattern if String === pattern
+ new(pattern)
+ end
+
+ def initialize(pattern)
+ @pattern = pattern
+ @exclusions = Set.new
+ end
+
+ def unsubscribe!(name)
+ exclusions << -name if pattern === name
+ end
+
+ def ===(name)
+ pattern === name && !exclusions.include?(name)
+ end
+ end
+
class Evented #:nodoc:
+ attr_reader :pattern
+
def initialize(pattern, delegate)
- @pattern = pattern
+ @pattern = Matcher.wrap(pattern)
@delegate = delegate
@can_publish = delegate.respond_to?(:publish)
end
@@ -122,11 +161,15 @@ module ActiveSupport
end
def subscribed_to?(name)
- @pattern === name
+ pattern === name
end
def matches?(name)
- @pattern && @pattern === name
+ pattern && pattern === name
+ end
+
+ def unsubscribe!(name)
+ pattern.unsubscribe!(name)
end
end
@@ -189,6 +232,10 @@ module ActiveSupport
true
end
+ def unsubscribe!(*)
+ false
+ end
+
alias :matches? :===
end
end
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index f8344912bb..a03e7e483e 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -13,14 +13,15 @@ module ActiveSupport
@notifier = notifier
end
- # Instrument the given block by measuring the time taken to execute it
- # and publish it. Notice that events get sent even if an error occurs
- # in the passed-in block.
+ # Given a block, instrument it by measuring the time taken to execute
+ # and publish it. Without a block, simply send a message via the
+ # notifier. Notice that events get sent even if an error occurs in the
+ # passed-in block.
def instrument(name, payload = {})
# some of the listeners might have state
listeners_state = start name, payload
begin
- yield payload
+ yield payload if block_given?
rescue Exception => e
payload[:exception] = [e.class.name, e.message]
payload[:exception_object] = e
@@ -52,8 +53,13 @@ module ActiveSupport
end
class Event
- attr_reader :name, :time, :transaction_id, :payload, :children
- attr_accessor :end
+ attr_reader :name, :time, :end, :transaction_id, :payload, :children
+
+ def self.clock_gettime_supported? # :nodoc:
+ defined?(Process::CLOCK_PROCESS_CPUTIME_ID) &&
+ !Gem.win_platform?
+ end
+ private_class_method :clock_gettime_supported?
def initialize(name, start, ending, transaction_id, payload)
@name = name
@@ -83,6 +89,11 @@ module ActiveSupport
@allocation_count_finish = now_allocations
end
+ def end=(ending)
+ ActiveSupport::Deprecation.deprecation_warning(:end=, :finish!)
+ @end = ending
+ end
+
# Returns the CPU time (in milliseconds) passed since the call to
# +start!+ and the call to +finish!+
def cpu_time
@@ -127,10 +138,10 @@ module ActiveSupport
private
def now
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ Concurrent.monotonic_time
end
- if defined?(Process::CLOCK_PROCESS_CPUTIME_ID)
+ if clock_gettime_supported?
def now_cpu
Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
end
diff --git a/activesupport/lib/active_support/reloader.rb b/activesupport/lib/active_support/reloader.rb
index fea18e9712..2f81cd4f80 100644
--- a/activesupport/lib/active_support/reloader.rb
+++ b/activesupport/lib/active_support/reloader.rb
@@ -50,11 +50,9 @@ module ActiveSupport
def self.reload!
executor.wrap do
new.tap do |instance|
- begin
- instance.run!
- ensure
- instance.complete!
- end
+ instance.run!
+ ensure
+ instance.complete!
end
end
prepare!
diff --git a/activesupport/lib/active_support/security_utils.rb b/activesupport/lib/active_support/security_utils.rb
index 20b6b9cd3f..5e455fca57 100644
--- a/activesupport/lib/active_support/security_utils.rb
+++ b/activesupport/lib/active_support/security_utils.rb
@@ -24,7 +24,7 @@ module ActiveSupport
# The values are first processed by SHA256, so that we don't leak length info
# via timing attacks.
def secure_compare(a, b)
- fixed_length_secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b)) && a == b
+ fixed_length_secure_compare(::Digest::SHA256.digest(a), ::Digest::SHA256.digest(b)) && a == b
end
module_function :secure_compare
end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index ef12c6b9b0..7be4108ed7 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -12,6 +12,7 @@ require "active_support/testing/constant_lookup"
require "active_support/testing/time_helpers"
require "active_support/testing/file_fixtures"
require "active_support/testing/parallelization"
+require "concurrent/utility/processor_counter"
module ActiveSupport
class TestCase < ::Minitest::Test
@@ -58,16 +59,20 @@ module ActiveSupport
# If the number of workers is set to +1+ or fewer, the tests will not be
# parallelized.
#
+ # If +workers+ is set to +:number_of_processors+, the number of workers will be
+ # set to the actual core count on the machine you are on.
+ #
# The default parallelization method is to fork processes. If you'd like to
# use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+
# method. Note the threaded parallelization does not create multiple
# database and will not work with system tests at this time.
#
- # parallelize(workers: 2, with: :threads)
+ # parallelize(workers: :number_of_processors, with: :threads)
#
# The threaded parallelization uses minitest's parallel executor directly.
# The processes parallelization uses a Ruby DRb server.
- def parallelize(workers: 2, with: :processes)
+ def parallelize(workers: :number_of_processors, with: :processes)
+ workers = Concurrent.physical_processor_count if workers == :number_of_processors
workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
return if workers <= 1
diff --git a/activesupport/lib/active_support/testing/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb
index eba41aa907..03c38be481 100644
--- a/activesupport/lib/active_support/testing/method_call_assertions.rb
+++ b/activesupport/lib/active_support/testing/method_call_assertions.rb
@@ -35,18 +35,16 @@ module ActiveSupport
assert_called(object, method_name, message, times: 0, &block)
end
- # TODO: No need to resort to #send once support for Ruby 2.4 is
- # dropped.
def assert_called_on_instance_of(klass, method_name, message = nil, times: 1, returns: nil)
times_called = 0
- klass.send(:define_method, "stubbed_#{method_name}") do |*|
+ klass.define_method("stubbed_#{method_name}") do |*|
times_called += 1
returns
end
- klass.send(:alias_method, "original_#{method_name}", method_name)
- klass.send(:alias_method, method_name, "stubbed_#{method_name}")
+ klass.alias_method "original_#{method_name}", method_name
+ klass.alias_method method_name, "stubbed_#{method_name}"
yield
@@ -55,9 +53,9 @@ module ActiveSupport
assert_equal times, times_called, error
ensure
- klass.send(:alias_method, method_name, "original_#{method_name}")
- klass.send(:undef_method, "original_#{method_name}")
- klass.send(:undef_method, "stubbed_#{method_name}")
+ klass.alias_method method_name, "original_#{method_name}"
+ klass.undef_method "original_#{method_name}"
+ klass.undef_method "stubbed_#{method_name}"
end
def assert_not_called_on_instance_of(klass, method_name, message = nil, &block)
diff --git a/activesupport/lib/active_support/testing/parallelization.rb b/activesupport/lib/active_support/testing/parallelization.rb
index 9c8dffa9d8..63440069b1 100644
--- a/activesupport/lib/active_support/testing/parallelization.rb
+++ b/activesupport/lib/active_support/testing/parallelization.rb
@@ -1,7 +1,7 @@
# 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
@@ -15,12 +15,15 @@ module ActiveSupport
end
def record(reporter, result)
+ raise DRb::DRbConnError if result.is_a?(DRb::DRbUnknown)
+
reporter.synchronize do
reporter.record(result)
end
end
def <<(o)
+ o[2] = DRbObject.new(o[2]) if o
@queue << o
end
@@ -66,31 +69,29 @@ module ActiveSupport
def start
@pool = @queue_size.times.map do |worker|
fork do
- 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)
+ 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
- ensure
- run_cleanup(worker)
end
+ ensure
+ run_cleanup(worker)
end
end
end
diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb
index f160e66971..5a3fa9346c 100644
--- a/activesupport/lib/active_support/testing/time_helpers.rb
+++ b/activesupport/lib/active_support/testing/time_helpers.rb
@@ -22,7 +22,7 @@ module ActiveSupport
@stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
- object.singleton_class.send :alias_method, new_name, method_name
+ object.singleton_class.alias_method new_name, method_name
object.define_singleton_method(method_name, &block)
end
@@ -43,9 +43,9 @@ module ActiveSupport
def unstub_object(stub)
singleton_class = stub.object.singleton_class
- singleton_class.send :silence_redefinition_of_method, stub.method_name
- singleton_class.send :alias_method, stub.method_name, stub.original_method
- singleton_class.send :undef_method, stub.original_method
+ singleton_class.silence_redefinition_of_method stub.method_name
+ singleton_class.alias_method stub.method_name, stub.original_method
+ singleton_class.undef_method stub.original_method
end
end
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index e42eee07a3..075cd4ed8b 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -3,6 +3,7 @@
require "time"
require "base64"
require "bigdecimal"
+require "bigdecimal/util"
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/date_time/calculations"
@@ -68,11 +69,7 @@ module ActiveSupport
"float" => Proc.new { |float| float.to_f },
"decimal" => Proc.new do |number|
if String === number
- begin
- BigDecimal(number)
- rescue ArgumentError
- BigDecimal("0")
- end
+ number.to_d
else
BigDecimal(number)
end
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 168d3655d3..62356e4d46 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -39,8 +39,4 @@ class ActiveSupport::TestCase
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/raises_load_error.rb b/activesupport/test/autoloading_fixtures/raises_load_error.rb
new file mode 100644
index 0000000000..f97be29b71
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/raises_load_error.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+# raises a load error typical of the dynamic code that manually raises load errors
+raise LoadError, "required gem not present kind of error"
diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb
index 9f54b1e7de..a696760bb2 100644
--- a/activesupport/test/cache/behaviors/cache_store_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb
@@ -130,7 +130,7 @@ module CacheStoreBehavior
assert_equal("fufu", @cache.read("fu"))
end
- def test_multi_with_objects
+ def test_fetch_multi_with_objects
cache_struct = Struct.new(:cache_key, :title)
foo = cache_struct.new("foo", "FOO!")
bar = cache_struct.new("bar")
@@ -142,6 +142,14 @@ module CacheStoreBehavior
assert_equal({ foo => "FOO!", bar => "BAM!" }, values)
end
+ def test_fetch_multi_returns_ordered_names
+ @cache.write("bam", "BAM")
+
+ values = @cache.fetch_multi("foo", "bar", "bam") { |key| key.upcase }
+
+ assert_equal(%w(foo bar bam), values.keys)
+ end
+
def test_fetch_multi_without_block
assert_raises(ArgumentError) do
@cache.fetch_multi("foo")
diff --git a/activesupport/test/cache/behaviors/connection_pool_behavior.rb b/activesupport/test/cache/behaviors/connection_pool_behavior.rb
index 4d1901a173..aed04d07d4 100644
--- a/activesupport/test/cache/behaviors/connection_pool_behavior.rb
+++ b/activesupport/test/cache/behaviors/connection_pool_behavior.rb
@@ -7,24 +7,22 @@ module ConnectionPoolBehavior
threads = []
emulating_latency do
- begin
- cache = ActiveSupport::Cache.lookup_store(store, { pool_size: 2, pool_timeout: 1 }.merge(store_options))
- cache.clear
-
- assert_raises Timeout::Error do
- # One of the three threads will fail in 1 second because our pool size
- # is only two.
- 3.times do
- threads << Thread.new do
- cache.read("latency")
- end
+ cache = ActiveSupport::Cache.lookup_store(*store, { pool_size: 2, pool_timeout: 1 }.merge(store_options))
+ cache.clear
+
+ assert_raises Timeout::Error do
+ # One of the three threads will fail in 1 second because our pool size
+ # is only two.
+ 3.times do
+ threads << Thread.new do
+ cache.read("latency")
end
-
- threads.each(&:join)
end
- ensure
- threads.each(&:kill)
+
+ threads.each(&:join)
end
+ ensure
+ threads.each(&:kill)
end
ensure
Thread.report_on_exception = original_report_on_exception
@@ -34,24 +32,22 @@ module ConnectionPoolBehavior
threads = []
emulating_latency do
- begin
- cache = ActiveSupport::Cache.lookup_store(store, store_options)
- cache.clear
-
- assert_nothing_raised do
- # Default connection pool size is 5, assuming 10 will make sure that
- # the connection pool isn't used at all.
- 10.times do
- threads << Thread.new do
- cache.read("latency")
- end
+ cache = ActiveSupport::Cache.lookup_store(*store, store_options)
+ cache.clear
+
+ assert_nothing_raised do
+ # Default connection pool size is 5, assuming 10 will make sure that
+ # the connection pool isn't used at all.
+ 10.times do
+ threads << Thread.new do
+ cache.read("latency")
end
-
- threads.each(&:join)
end
- ensure
- threads.each(&:kill)
+
+ threads.each(&:join)
end
+ ensure
+ threads.each(&:kill)
end
end
diff --git a/activesupport/test/cache/stores/file_store_test.rb b/activesupport/test/cache/stores/file_store_test.rb
index f6855bb308..0364d9ab64 100644
--- a/activesupport/test/cache/stores/file_store_test.rb
+++ b/activesupport/test/cache/stores/file_store_test.rb
@@ -101,7 +101,7 @@ class FileStoreTest < ActiveSupport::TestCase
end
assert File.exist?(cache_dir), "Parent of top level cache dir was deleted!"
assert File.exist?(sub_cache_dir), "Top level cache dir was deleted!"
- assert_empty Dir.entries(sub_cache_dir).reject { |f| ActiveSupport::Cache::FileStore::EXCLUDED_DIRS.include?(f) }
+ assert_empty Dir.children(sub_cache_dir)
end
def test_log_exception_when_cache_read_fails
diff --git a/activesupport/test/cache/stores/mem_cache_store_test.rb b/activesupport/test/cache/stores/mem_cache_store_test.rb
index f426a37c66..0e472f5a1a 100644
--- a/activesupport/test/cache/stores/mem_cache_store_test.rb
+++ b/activesupport/test/cache/stores/mem_cache_store_test.rb
@@ -25,8 +25,9 @@ end
class MemCacheStoreTest < ActiveSupport::TestCase
begin
- ss = Dalli::Client.new("localhost:11211").stats
- raise Dalli::DalliError unless ss["localhost:11211"]
+ servers = ENV["MEMCACHE_SERVERS"] || "localhost:11211"
+ ss = Dalli::Client.new(servers).stats
+ raise Dalli::DalliError unless ss[servers]
MEMCACHE_UP = true
rescue Dalli::DalliError
@@ -37,8 +38,8 @@ class MemCacheStoreTest < ActiveSupport::TestCase
def setup
skip "memcache server is not up" unless MEMCACHE_UP
- @cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, expires_in: 60)
- @peek = ActiveSupport::Cache.lookup_store(:mem_cache_store)
+ @cache = ActiveSupport::Cache.lookup_store(*store, expires_in: 60)
+ @peek = ActiveSupport::Cache.lookup_store(*store)
@data = @cache.instance_variable_get(:@data)
@cache.clear
@cache.silence!
@@ -56,21 +57,21 @@ class MemCacheStoreTest < ActiveSupport::TestCase
include FailureSafetyBehavior
def test_raw_values
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true)
+ cache = ActiveSupport::Cache.lookup_store(*store, raw: true)
cache.clear
cache.write("foo", 2)
assert_equal "2", cache.read("foo")
end
def test_raw_values_with_marshal
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true)
+ cache = ActiveSupport::Cache.lookup_store(*store, raw: true)
cache.clear
cache.write("foo", Marshal.dump([]))
assert_equal [], cache.read("foo")
end
def test_local_cache_raw_values
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true)
+ cache = ActiveSupport::Cache.lookup_store(*store, raw: true)
cache.clear
cache.with_local_cache do
cache.write("foo", 2)
@@ -79,7 +80,7 @@ class MemCacheStoreTest < ActiveSupport::TestCase
end
def test_increment_expires_in
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true)
+ cache = ActiveSupport::Cache.lookup_store(*store, raw: true)
cache.clear
assert_called_with cache.instance_variable_get(:@data), :incr, [ "foo", 1, 60 ] do
cache.increment("foo", 1, expires_in: 60)
@@ -87,7 +88,7 @@ class MemCacheStoreTest < ActiveSupport::TestCase
end
def test_decrement_expires_in
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true)
+ cache = ActiveSupport::Cache.lookup_store(*store, raw: true)
cache.clear
assert_called_with cache.instance_variable_get(:@data), :decr, [ "foo", 1, 60 ] do
cache.decrement("foo", 1, expires_in: 60)
@@ -95,7 +96,7 @@ class MemCacheStoreTest < ActiveSupport::TestCase
end
def test_local_cache_raw_values_with_marshal
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true)
+ cache = ActiveSupport::Cache.lookup_store(*store, raw: true)
cache.clear
cache.with_local_cache do
cache.write("foo", Marshal.dump([]))
@@ -114,7 +115,7 @@ class MemCacheStoreTest < ActiveSupport::TestCase
private
def store
- :mem_cache_store
+ [:mem_cache_store, ENV["MEMCACHE_SERVERS"] || "localhost:11211"]
end
def emulating_latency
diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb
index 305a2c184d..1d87f74347 100644
--- a/activesupport/test/cache/stores/redis_cache_store_test.rb
+++ b/activesupport/test/cache/stores/redis_cache_store_test.rb
@@ -187,7 +187,7 @@ module ActiveSupport::Cache::RedisCacheStoreTests
private
def store
- :redis_cache_store
+ [:redis_cache_store]
end
def emulating_latency
@@ -204,7 +204,7 @@ module ActiveSupport::Cache::RedisCacheStoreTests
class RedisDistributedConnectionPoolBehaviourTest < ConnectionPoolBehaviourTest
private
def store_options
- { url: %w[ redis://localhost:6379/0 redis://localhost:6379/0 ] }
+ { url: [ENV["REDIS_URL"] || "redis://localhost:6379/0"] * 2 }
end
end
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 466b364e9d..79098b2a7d 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -31,7 +31,7 @@ module CallbacksTest
def callback_object(callback_method)
klass = Class.new
- klass.send(:define_method, callback_method) do |model|
+ klass.define_method(callback_method) do |model|
model.history << [:"#{callback_method}_save", :object]
end
klass.new
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/constantize_test_cases.rb b/activesupport/test/constantize_test_cases.rb
index 2c6145940b..cdb8441b81 100644
--- a/activesupport/test/constantize_test_cases.rb
+++ b/activesupport/test/constantize_test_cases.rb
@@ -112,6 +112,16 @@ module ConstantizeTestCases
assert_nil yield("A::Object::B")
assert_nil yield("A::Object::Object::Object::B")
+ with_autoloading_fixtures do
+ assert_nil yield("Em")
+ end
+
+ assert_raises(LoadError) do
+ with_autoloading_fixtures do
+ yield("RaisesLoadError")
+ end
+ end
+
assert_raises(NameError) do
with_autoloading_fixtures do
yield("RaisesNameError")
diff --git a/activesupport/test/core_ext/array/access_test.rb b/activesupport/test/core_ext/array/access_test.rb
index 8c217023cf..427b058925 100644
--- a/activesupport/test/core_ext/array/access_test.rb
+++ b/activesupport/test/core_ext/array/access_test.rb
@@ -32,6 +32,18 @@ class AccessTest < ActiveSupport::TestCase
assert_equal array[-2], array.second_to_last
end
+ def test_including
+ assert_equal [1, 2, 3, 4, 5], [1, 2, 4].including(3, 5).sort
+ assert_equal [1, 2, 3, 4, 5], [1, 2, 4].including([3, 5]).sort
+ assert_equal [[0, 1], [1, 0]], [[0, 1]].including([[1, 0]])
+ end
+
+ def test_excluding
+ assert_equal [1, 2, 4], [1, 2, 3, 4, 5].excluding(3, 5)
+ assert_equal [1, 2, 4], [1, 2, 3, 4, 5].excluding([3, 5])
+ assert_equal [[0, 1]], [[0, 1], [1, 0]].excluding([[1, 0]])
+ end
+
def test_without
assert_equal [1, 2, 4], [1, 2, 3, 4, 5].without(3, 5)
end
diff --git a/activesupport/test/core_ext/array/prepend_append_test.rb b/activesupport/test/core_ext/array/prepend_append_test.rb
index c34acd66ad..8573dbd5a6 100644
--- a/activesupport/test/core_ext/array/prepend_append_test.rb
+++ b/activesupport/test/core_ext/array/prepend_append_test.rb
@@ -1,14 +1,11 @@
# frozen_string_literal: true
require "abstract_unit"
-require "active_support/core_ext/array"
class PrependAppendTest < ActiveSupport::TestCase
- def test_append
- assert_equal [1, 2], [1].append(2)
- end
-
- def test_prepend
- assert_equal [2, 1], [1].prepend(2)
+ def test_requiring_prepend_and_append_is_deprecated
+ assert_deprecated do
+ require "active_support/core_ext/array/prepend_and_append"
+ end
end
end
diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index b63464a36a..381b5a1f32 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -217,11 +217,18 @@ class EnumerableTests < ActiveSupport::TestCase
assert_equal false, GenericEnumerable.new([ 1 ]).exclude?(1)
end
+ def test_excluding
+ assert_equal [1, 2, 4], GenericEnumerable.new((1..5).to_a).excluding(3, 5)
+ assert_equal [3, 4, 5], GenericEnumerable.new((1..5).to_a).excluding([1, 2])
+ assert_equal [[0, 1]], GenericEnumerable.new([[0, 1], [1, 0]]).excluding([[1, 0]])
+ assert_equal [1, 2, 4], (1..5).to_a.excluding(3, 5)
+ assert_equal [1, 2, 4], (1..5).to_set.excluding(3, 5)
+ assert_equal({ foo: 1, baz: 3 }, { foo: 1, bar: 2, baz: 3 }.excluding(:bar))
+ end
+
def test_without
assert_equal [1, 2, 4], GenericEnumerable.new((1..5).to_a).without(3, 5)
- assert_equal [1, 2, 4], (1..5).to_a.without(3, 5)
- assert_equal [1, 2, 4], (1..5).to_set.without(3, 5)
- assert_equal({ foo: 1, baz: 3 }, { foo: 1, bar: 2, baz: 3 }.without(:bar))
+ assert_equal [3, 4, 5], GenericEnumerable.new((1..5).to_a).without([1, 2])
end
def test_pluck
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/hash/transform_keys_test.rb b/activesupport/test/core_ext/hash/transform_keys_test.rb
deleted file mode 100644
index b9e41f7b25..0000000000
--- a/activesupport/test/core_ext/hash/transform_keys_test.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: true
-
-require "abstract_unit"
-require "active_support/core_ext/hash/keys"
-
-class TransformKeysTest < ActiveSupport::TestCase
- test "transform_keys returns a new hash with the keys computed from the block" do
- original = { a: "a", b: "b" }
- mapped = original.transform_keys { |k| "#{k}!".to_sym }
-
- assert_equal({ a: "a", b: "b" }, original)
- assert_equal({ a!: "a", b!: "b" }, mapped)
- end
-
- test "transform_keys! modifies the keys of the original" do
- original = { a: "a", b: "b" }
- mapped = original.transform_keys! { |k| "#{k}!".to_sym }
-
- assert_equal({ a!: "a", b!: "b" }, original)
- assert_same original, mapped
- end
-
- test "transform_keys returns a sized Enumerator if no block is given" do
- original = { a: "a", b: "b" }
- enumerator = original.transform_keys
- assert_equal original.size, enumerator.size
- assert_equal Enumerator, enumerator.class
- end
-
- test "transform_keys! returns a sized Enumerator if no block is given" do
- original = { a: "a", b: "b" }
- enumerator = original.transform_keys!
- assert_equal original.size, enumerator.size
- assert_equal Enumerator, enumerator.class
- end
-
- test "transform_keys is chainable with Enumerable methods" do
- original = { a: "a", b: "b" }
- mapped = original.transform_keys.with_index { |k, i| [k, i].join.to_sym }
- assert_equal({ a0: "a", b1: "b" }, mapped)
- end
-
- test "transform_keys! is chainable with Enumerable methods" do
- original = { a: "a", b: "b" }
- original.transform_keys!.with_index { |k, i| [k, i].join.to_sym }
- assert_equal({ a0: "a", b1: "b" }, original)
- end
-
- test "transform_keys returns a Hash instance when self is inherited from Hash" do
- class HashDescendant < ::Hash
- def initialize(elements = nil)
- super(elements)
- (elements || {}).each_pair { |key, value| self[key] = value }
- end
- end
-
- original = HashDescendant.new(a: "a", b: "b")
- mapped = original.transform_keys { |k| "#{k}!".to_sym }
-
- assert_equal({ a: "a", b: "b" }, original)
- assert_equal({ a!: "a", b!: "b" }, mapped)
- assert_equal(::Hash, mapped.class)
- end
-end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index f4f0dd6b31..8572d56722 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -31,10 +31,10 @@ class HashExtTest < ActiveSupport::TestCase
def test_methods
h = {}
- assert_respond_to h, :transform_keys
- assert_respond_to h, :transform_keys!
assert_respond_to h, :deep_transform_keys
assert_respond_to h, :deep_transform_keys!
+ assert_respond_to h, :deep_transform_values
+ assert_respond_to h, :deep_transform_values!
assert_respond_to h, :symbolize_keys
assert_respond_to h, :symbolize_keys!
assert_respond_to h, :deep_symbolize_keys
@@ -49,18 +49,6 @@ class HashExtTest < ActiveSupport::TestCase
assert_respond_to h, :except!
end
- def test_transform_keys
- assert_equal @upcase_strings, @strings.transform_keys { |key| key.to_s.upcase }
- assert_equal @upcase_strings, @symbols.transform_keys { |key| key.to_s.upcase }
- assert_equal @upcase_strings, @mixed.transform_keys { |key| key.to_s.upcase }
- end
-
- def test_transform_keys_not_mutates
- transformed_hash = @mixed.dup
- transformed_hash.transform_keys { |key| key.to_s.upcase }
- assert_equal @mixed, transformed_hash
- end
-
def test_deep_transform_keys
assert_equal @nested_upcase_strings, @nested_symbols.deep_transform_keys { |key| key.to_s.upcase }
assert_equal @nested_upcase_strings, @nested_strings.deep_transform_keys { |key| key.to_s.upcase }
@@ -76,19 +64,6 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal @nested_mixed, transformed_hash
end
- def test_transform_keys!
- assert_equal @upcase_strings, @symbols.dup.transform_keys! { |key| key.to_s.upcase }
- assert_equal @upcase_strings, @strings.dup.transform_keys! { |key| key.to_s.upcase }
- assert_equal @upcase_strings, @mixed.dup.transform_keys! { |key| key.to_s.upcase }
- end
-
- def test_transform_keys_with_bang_mutates
- transformed_hash = @mixed.dup
- transformed_hash.transform_keys! { |key| key.to_s.upcase }
- assert_equal @upcase_strings, transformed_hash
- assert_equal({ :a => 1, "b" => 2 }, @mixed)
- end
-
def test_deep_transform_keys!
assert_equal @nested_upcase_strings, @nested_symbols.deep_dup.deep_transform_keys! { |key| key.to_s.upcase }
assert_equal @nested_upcase_strings, @nested_strings.deep_dup.deep_transform_keys! { |key| key.to_s.upcase }
@@ -105,6 +80,31 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal({ "a" => { b: { "c" => 3 } } }, @nested_mixed)
end
+ def test_deep_transform_values
+ assert_equal({ "a" => "1", "b" => "2" }, @strings.deep_transform_values { |value| value.to_s })
+ assert_equal({ "a" => { "b" => { "c" => "3" } } }, @nested_strings.deep_transform_values { |value| value.to_s })
+ assert_equal({ "a" => [ { "b" => "2" }, { "c" => "3" }, "4" ] }, @string_array_of_hashes.deep_transform_values { |value| value.to_s })
+ end
+
+ def test_deep_transform_values_not_mutates
+ transformed_hash = @nested_mixed.deep_dup
+ transformed_hash.deep_transform_values { |value| value.to_s }
+ assert_equal @nested_mixed, transformed_hash
+ end
+
+ def test_deep_transform_values!
+ assert_equal({ "a" => "1", "b" => "2" }, @strings.deep_transform_values! { |value| value.to_s })
+ assert_equal({ "a" => { "b" => { "c" => "3" } } }, @nested_strings.deep_transform_values! { |value| value.to_s })
+ assert_equal({ "a" => [ { "b" => "2" }, { "c" => "3" }, "4" ] }, @string_array_of_hashes.deep_transform_values! { |value| value.to_s })
+ end
+
+ def test_deep_transform_values_with_bang_mutates
+ transformed_hash = @nested_mixed.deep_dup
+ transformed_hash.deep_transform_values! { |value| value.to_s }
+ assert_equal({ "a" => { b: { "c" => "3" } } }, transformed_hash)
+ assert_equal({ "a" => { b: { "c" => 3 } } }, @nested_mixed)
+ end
+
def test_symbolize_keys
assert_equal @symbols, @symbols.symbolize_keys
assert_equal @symbols, @strings.symbolize_keys
@@ -337,30 +337,16 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal expected, merged
end
- def test_slice
- original = { a: "x", b: "y", c: 10 }
- expected = { a: "x", b: "y" }
-
- # Should return a new hash with only the given keys.
- assert_equal expected, original.slice(:a, :b)
- assert_not_equal expected, original
- end
-
def test_slice_inplace
original = { a: "x", b: "y", c: 10 }
- expected = { c: 10 }
-
- # Should replace the hash with only the given keys.
- assert_equal expected, original.slice!(:a, :b)
- end
+ expected_return = { c: 10 }
+ expected_original = { a: "x", b: "y" }
- def test_slice_with_an_array_key
- original = { :a => "x", :b => "y", :c => 10, [:a, :b] => "an array key" }
- expected = { [:a, :b] => "an array key", :c => 10 }
+ # Should return a hash containing the removed key/value pairs.
+ assert_equal expected_return, original.slice!(:a, :b)
- # Should return a new hash with only the given keys when given an array key.
- assert_equal expected, original.slice([:a, :b], :c)
- assert_not_equal expected, original
+ # Should replace the hash with only the given keys.
+ assert_equal expected_original, original
end
def test_slice_inplace_with_an_array_key
@@ -371,14 +357,6 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal expected, original.slice!([:a, :b], :c)
end
- def test_slice_with_splatted_keys
- original = { :a => "x", :b => "y", :c => 10, [:a, :b] => "an array key" }
- expected = { a: "x", b: "y" }
-
- # Should grab each of the splatted keys.
- assert_equal expected, original.slice(*[:a, :b])
- end
-
def test_slice_bang_does_not_override_default
hash = Hash.new(0)
hash.update(a: 1, b: 2)
@@ -444,7 +422,7 @@ class HashExtTest < ActiveSupport::TestCase
original.freeze
assert_nothing_raised { original.except(:a) }
- assert_raise(frozen_error_class) { original.except!(:a) }
+ assert_raise(FrozenError) { original.except!(:a) }
end
def test_except_does_not_delete_values_in_original
diff --git a/activesupport/test/core_ext/load_error_test.rb b/activesupport/test/core_ext/load_error_test.rb
index 126aa51cb4..6d3726e407 100644
--- a/activesupport/test/core_ext/load_error_test.rb
+++ b/activesupport/test/core_ext/load_error_test.rb
@@ -13,10 +13,9 @@ class TestLoadError < ActiveSupport::TestCase
end
def test_path
- begin load "nor/this/one.rb"
- rescue LoadError => e
- assert_equal "nor/this/one.rb", e.path
- end
+ load "nor/this/one.rb"
+ rescue LoadError => e
+ assert_equal "nor/this/one.rb", e.path
end
def test_is_missing_with_nil_path
diff --git a/activesupport/test/core_ext/module/reachable_test.rb b/activesupport/test/core_ext/module/reachable_test.rb
deleted file mode 100644
index f356d46957..0000000000
--- a/activesupport/test/core_ext/module/reachable_test.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-# frozen_string_literal: true
-
-require "abstract_unit"
-require "active_support/core_ext/module/reachable"
-
-class AnonymousTest < ActiveSupport::TestCase
- test "an anonymous class or module is not reachable" do
- assert_deprecated do
- assert_not_predicate Module.new, :reachable?
- assert_not_predicate Class.new, :reachable?
- end
- end
-
- test "ordinary named classes or modules are reachable" do
- assert_deprecated do
- assert_predicate Kernel, :reachable?
- assert_predicate Object, :reachable?
- end
- end
-
- test "a named class or module whose constant has gone is not reachable" do
- c = eval "class C; end; C"
- m = eval "module M; end; M"
-
- self.class.send(:remove_const, :C)
- self.class.send(:remove_const, :M)
-
- assert_deprecated do
- assert_not_predicate c, :reachable?
- assert_not_predicate m, :reachable?
- end
- end
-
- test "a named class or module whose constants store different objects are not reachable" do
- c = eval "class C; end; C"
- m = eval "module M; end; M"
-
- self.class.send(:remove_const, :C)
- self.class.send(:remove_const, :M)
-
- eval "class C; end"
- eval "module M; end"
-
- assert_deprecated do
- assert_predicate C, :reachable?
- assert_predicate M, :reachable?
- assert_not_predicate c, :reachable?
- assert_not_predicate m, :reachable?
- end
- end
-end
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index 4b8efb8a93..16d6a4c2f2 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -57,7 +57,7 @@ class RangeTest < ActiveSupport::TestCase
end
def test_should_include_other_with_exclusive_end
- assert((1..10).include?(1...10))
+ assert((1..10).include?(1...11))
end
def test_should_compare_identical_inclusive
@@ -69,7 +69,7 @@ class RangeTest < ActiveSupport::TestCase
end
def test_should_compare_other_with_exclusive_end
- assert((1..10) === (1...10))
+ assert((1..10) === (1...11))
end
def test_exclusive_end_should_not_include_identical_with_inclusive_end
@@ -93,6 +93,10 @@ class RangeTest < ActiveSupport::TestCase
assert range.method(:include?) != range.method(:cover?)
end
+ def test_should_cover_other_with_exclusive_end
+ assert((1..10).cover?(1...11))
+ end
+
def test_overlaps_on_time
time_range_1 = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30)
time_range_2 = Time.utc(2005, 12, 10, 17, 00)..Time.utc(2005, 12, 10, 18, 00)
diff --git a/activesupport/test/core_ext/secure_random_test.rb b/activesupport/test/core_ext/secure_random_test.rb
index 7067fb524c..4b73233971 100644
--- a/activesupport/test/core_ext/secure_random_test.rb
+++ b/activesupport/test/core_ext/secure_random_test.rb
@@ -19,4 +19,24 @@ class SecureRandomTest < ActiveSupport::TestCase
assert_not_equal s1, s2
assert_equal 24, s1.length
end
+
+ def test_base36
+ s1 = SecureRandom.base36
+ s2 = SecureRandom.base36
+
+ assert_not_equal s1, s2
+ assert_equal 16, s1.length
+ assert_match(/^[a-z0-9]+$/, s1)
+ assert_match(/^[a-z0-9]+$/, s2)
+ end
+
+ def test_base36_with_length
+ s1 = SecureRandom.base36(24)
+ s2 = SecureRandom.base36(24)
+
+ assert_not_equal s1, s2
+ assert_equal 24, s1.length
+ assert_match(/^[a-z0-9]+$/, s1)
+ assert_match(/^[a-z0-9]+$/, s2)
+ end
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 2468fe3603..4ffa33aa61 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -206,6 +206,12 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
end
+ def test_parameterize_with_locale
+ word = "Fünf autos"
+ I18n.backend.store_translations(:de, i18n: { transliterate: { rule: { "ü" => "ue" } } })
+ assert_equal("fuenf-autos", word.parameterize(locale: :de))
+ end
+
def test_humanize
UnderscoreToHuman.each do |underscore, human|
assert_equal(human, underscore.humanize)
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 7078f3506d..590b81b770 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -514,6 +514,8 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(1582, 10, 15, 15, 15, 10), Time.local(1582, 10, 14, 15, 15, 10).advance(days: 1)
assert_equal Time.local(1582, 10, 5, 15, 15, 10), Time.local(1582, 10, 4, 15, 15, 10).advance(days: 1)
assert_equal Time.local(1582, 10, 4, 15, 15, 10), Time.local(1582, 10, 5, 15, 15, 10).advance(days: -1)
+ assert_equal Time.local(999, 10, 4, 15, 15, 10), Time.local(1000, 10, 4, 15, 15, 10).advance(years: -1)
+ assert_equal Time.local(1000, 10, 4, 15, 15, 10), Time.local(999, 10, 4, 15, 15, 10).advance(years: 1)
end
def test_last_week
diff --git a/activesupport/test/current_attributes_test.rb b/activesupport/test/current_attributes_test.rb
index 1669f08f68..adbdc646bc 100644
--- a/activesupport/test/current_attributes_test.rb
+++ b/activesupport/test/current_attributes_test.rb
@@ -3,13 +3,18 @@
require "abstract_unit"
class CurrentAttributesTest < ActiveSupport::TestCase
- Person = Struct.new(:name, :time_zone)
+ Person = Struct.new(:id, :name, :time_zone)
class Current < ActiveSupport::CurrentAttributes
attribute :world, :account, :person, :request
delegate :time_zone, to: :person
- resets { Time.zone = "UTC" }
+ before_reset { Session.previous = person.try(:id) }
+
+ resets do
+ Time.zone = "UTC"
+ Session.current = nil
+ end
def account=(account)
super
@@ -19,6 +24,7 @@ class CurrentAttributesTest < ActiveSupport::TestCase
def person=(person)
super
Time.zone = person.try(:time_zone)
+ Session.current = person.try(:id)
end
def request
@@ -30,9 +36,14 @@ class CurrentAttributesTest < ActiveSupport::TestCase
end
end
+ class Session < ActiveSupport::CurrentAttributes
+ attribute :current, :previous
+ end
+
setup do
@original_time_zone = Time.zone
Current.reset
+ Session.reset
end
teardown do
@@ -56,16 +67,28 @@ class CurrentAttributesTest < ActiveSupport::TestCase
end
test "set auxiliary class via overwritten method" do
- Current.person = Person.new("David", "Central Time (US & Canada)")
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "Central Time (US & Canada)", Time.zone.name
+ assert_equal 42, Session.current
end
- test "resets auxiliary class via callback" do
- Current.person = Person.new("David", "Central Time (US & Canada)")
+ test "resets auxiliary classes via callback" do
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "Central Time (US & Canada)", Time.zone.name
Current.reset
assert_equal "UTC", Time.zone.name
+ assert_nil Session.current
+ end
+
+ test "set auxiliary class based on current attributes via before callback" do
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
+ assert_nil Session.previous
+ assert_equal 42, Session.current
+
+ Current.reset
+ assert_equal 42, Session.previous
+ assert_nil Session.current
end
test "set attribute only via scope" do
@@ -92,13 +115,13 @@ class CurrentAttributesTest < ActiveSupport::TestCase
end
test "delegation" do
- Current.person = Person.new("David", "Central Time (US & Canada)")
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "Central Time (US & Canada)", Current.time_zone
assert_equal "Central Time (US & Canada)", Current.instance.time_zone
end
test "all methods forward to the instance" do
- Current.person = Person.new("David", "Central Time (US & Canada)")
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "David, in Central Time (US & Canada)", Current.intro
assert_equal "David, in Central Time (US & Canada)", Current.instance.intro
end
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 9f2755a25c..d4e709137e 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -284,7 +284,7 @@ class DependenciesTest < ActiveSupport::TestCase
def test_module_with_nested_class_requiring_lib_class
with_autoloading_fixtures do
- ModuleFolder::NestedWithRequire
+ _ = 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")
@@ -297,7 +297,7 @@ class DependenciesTest < ActiveSupport::TestCase
def test_module_with_nested_class_and_parent_requiring_lib_class
with_autoloading_fixtures do
- NestedWithRequireParent
+ _ = 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")
@@ -841,7 +841,7 @@ class DependenciesTest < ActiveSupport::TestCase
remove_constants(:C)
end
- def test_new_contants_in_without_constants
+ def test_new_constants_in_without_constants
assert_equal [], (ActiveSupport::Dependencies.new_constants_in(Object) { })
assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k, v| v.empty? }
end
diff --git a/activesupport/test/deprecation/method_wrappers_test.rb b/activesupport/test/deprecation/method_wrappers_test.rb
index b04bce7a11..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)
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index 105153584d..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
@@ -425,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/descendants_tracker_test_cases.rb b/activesupport/test/descendants_tracker_test_cases.rb
index 2c94c3c56c..f8752688d2 100644
--- a/activesupport/test/descendants_tracker_test_cases.rb
+++ b/activesupport/test/descendants_tracker_test_cases.rb
@@ -27,6 +27,15 @@ module DescendantsTrackerTestCases
assert_equal_sets [], Child2.descendants
end
+ def test_descendants_with_garbage_collected_classes
+ 1.times do
+ child_klass = Class.new(Parent)
+ assert_equal_sets [Child1, Grandchild1, Grandchild2, Child2, child_klass], Parent.descendants
+ end
+ GC.start
+ assert_equal_sets [Child1, Grandchild1, Grandchild2, Child2], Parent.descendants
+ end
+
def test_direct_descendants
assert_equal_sets [Child1, Child2], Parent.direct_descendants
assert_equal_sets [Grandchild1, Grandchild2], Child1.direct_descendants
diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb
index a557608986..b2d5eb94c2 100644
--- a/activesupport/test/evented_file_update_checker_test.rb
+++ b/activesupport/test/evented_file_update_checker_test.rb
@@ -76,6 +76,34 @@ class EventedFileUpdateCheckerTest < ActiveSupport::TestCase
Process.wait(pid)
end
+
+ test "updated should become true when nonexistent directory is added later" do
+ Dir.mktmpdir do |dir|
+ watched_dir = File.join(dir, "app")
+ unwatched_dir = File.join(dir, "node_modules")
+ not_exist_watched_dir = File.join(dir, "test")
+
+ Dir.mkdir(watched_dir)
+ Dir.mkdir(unwatched_dir)
+
+ checker = new_checker([], watched_dir => ".rb", not_exist_watched_dir => ".rb") { }
+
+ FileUtils.touch(File.join(watched_dir, "a.rb"))
+ wait
+ assert_predicate checker, :updated?
+ assert checker.execute_if_updated
+
+ Dir.mkdir(not_exist_watched_dir)
+ wait
+ assert_predicate checker, :updated?
+ assert checker.execute_if_updated
+
+ FileUtils.touch(File.join(unwatched_dir, "a.rb"))
+ wait
+ assert_not_predicate checker, :updated?
+ assert_not checker.execute_if_updated
+ end
+ end
end
class EventedFileUpdateCheckerPathHelperTest < ActiveSupport::TestCase
diff --git a/activesupport/test/file_update_checker_shared_tests.rb b/activesupport/test/file_update_checker_shared_tests.rb
index 72683816b3..84d89fa0a7 100644
--- a/activesupport/test/file_update_checker_shared_tests.rb
+++ b/activesupport/test/file_update_checker_shared_tests.rb
@@ -186,6 +186,18 @@ module FileUpdateCheckerSharedTests
assert_equal 1, i
end
+ test "should execute the block if files change in a watched directory any extensions" do
+ i = 0
+
+ checker = new_checker([], tmpdir => []) { i += 1 }
+
+ touch(tmpfile("foo.rb"))
+ wait
+
+ assert checker.execute_if_updated
+ assert_equal 1, i
+ end
+
test "should execute the block if files change in a watched directory several extensions" do
i = 0
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/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
index f81e0dc70f..1d6a07ec5f 100644
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ b/activesupport/test/hash_with_indifferent_access_test.rb
@@ -447,6 +447,14 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
end
+ def test_indifferent_assoc
+ indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings)
+ key, value = indifferent_strings.assoc(:a)
+
+ assert_equal("a", key)
+ assert_equal(1, value)
+ end
+
def test_indifferent_compact
hash_contain_nil_value = @strings.merge("z" => nil)
hash = ActiveSupport::HashWithIndifferentAccess.new(hash_contain_nil_value)
@@ -478,7 +486,7 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_equal @strings, roundtrip
assert_equal "1234", roundtrip.default
- # Ensure nested hashes are not HashWithIndiffereneAccess
+ # Ensure nested hashes are not HashWithIndifferentAccess
new_to_hash = @nested_mixed.with_indifferent_access.to_hash
assert_not new_to_hash.instance_of?(HashWithIndifferentAccess)
assert_not new_to_hash["a"].instance_of?(HashWithIndifferentAccess)
diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb
index 5e50acf5db..ddf765a220 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -224,12 +224,6 @@ class InflectorTest < ActiveSupport::TestCase
assert_equal("json_html_api", ActiveSupport::Inflector.underscore("JSONHTMLAPI"))
end
- def test_acronym_regexp_is_deprecated
- assert_deprecated do
- ActiveSupport::Inflector.inflections.acronym_regex
- end
- end
-
def test_underscore
CamelToUnderscore.each do |camel, underscore|
assert_equal(underscore, ActiveSupport::Inflector.underscore(camel))
@@ -310,6 +304,12 @@ class InflectorTest < ActiveSupport::TestCase
end
end
+ def test_parameterize_with_locale
+ word = "Fünf autos"
+ I18n.backend.store_translations(:de, i18n: { transliterate: { rule: { "ü" => "ue" } } })
+ assert_equal("fuenf-autos", ActiveSupport::Inflector.parameterize(word, locale: :de))
+ end
+
def test_classify
ClassNameToTableName.each do |class_name, table_name|
assert_equal(class_name, ActiveSupport::Inflector.classify(table_name))
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 8062873386..7589ffd0ea 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -21,20 +21,18 @@ class TestJSONEncoding < ActiveSupport::TestCase
JSONTest::EncodingTestCases.constants.each do |class_tests|
define_method("test_#{class_tests[0..-6].underscore}") do
- begin
- prev = ActiveSupport.use_standard_json_time_format
-
- standard_class_tests = /Standard/.match?(class_tests)
-
- ActiveSupport.escape_html_entities_in_json = !standard_class_tests
- ActiveSupport.use_standard_json_time_format = standard_class_tests
- JSONTest::EncodingTestCases.const_get(class_tests).each do |pair|
- assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first))
- end
- ensure
- ActiveSupport.escape_html_entities_in_json = false
- ActiveSupport.use_standard_json_time_format = prev
+ prev = ActiveSupport.use_standard_json_time_format
+
+ standard_class_tests = /Standard/.match?(class_tests)
+
+ ActiveSupport.escape_html_entities_in_json = !standard_class_tests
+ ActiveSupport.use_standard_json_time_format = standard_class_tests
+ JSONTest::EncodingTestCases.const_get(class_tests).each do |pair|
+ assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first))
end
+ ensure
+ ActiveSupport.escape_html_entities_in_json = false
+ ActiveSupport.use_standard_json_time_format = prev
end
end
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index f87099566b..5f4e3f3fd3 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -772,6 +772,16 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
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)
diff --git a/activesupport/test/notifications/evented_notification_test.rb b/activesupport/test/notifications/evented_notification_test.rb
index 4beb8194b9..ab2a9b8659 100644
--- a/activesupport/test/notifications/evented_notification_test.rb
+++ b/activesupport/test/notifications/evented_notification_test.rb
@@ -84,6 +84,39 @@ module ActiveSupport
[:finish, "hi", 1, {}]
], listener.events
end
+
+ def test_listen_to_regexp
+ notifier = Fanout.new
+ listener = Listener.new
+ notifier.subscribe(/[a-z]*.world/, listener)
+ notifier.start("hi.world", 1, {})
+ notifier.finish("hi.world", 2, {})
+ notifier.start("hello.world", 1, {})
+ notifier.finish("hello.world", 2, {})
+
+ assert_equal [
+ [:start, "hi.world", 1, {}],
+ [:finish, "hi.world", 2, {}],
+ [:start, "hello.world", 1, {}],
+ [:finish, "hello.world", 2, {}]
+ ], listener.events
+ end
+
+ def test_listen_to_regexp_with_exclusions
+ notifier = Fanout.new
+ listener = Listener.new
+ notifier.subscribe(/[a-z]*.world/, listener)
+ notifier.unsubscribe("hi.world")
+ notifier.start("hi.world", 1, {})
+ notifier.finish("hi.world", 2, {})
+ notifier.start("hello.world", 1, {})
+ notifier.finish("hello.world", 2, {})
+
+ assert_equal [
+ [:start, "hello.world", 1, {}],
+ [:finish, "hello.world", 2, {}]
+ ], listener.events
+ end
end
end
end
diff --git a/activesupport/test/notifications/instrumenter_test.rb b/activesupport/test/notifications/instrumenter_test.rb
index d5c9e82e9f..9729ad5c89 100644
--- a/activesupport/test/notifications/instrumenter_test.rb
+++ b/activesupport/test/notifications/instrumenter_test.rb
@@ -44,6 +44,12 @@ module ActiveSupport
assert_equal Hash[result: 2], payload
end
+ def test_instrument_works_without_a_block
+ instrumenter.instrument("no.block", payload)
+ assert_equal 1, notifier.finishes.size
+ assert_equal "no.block", notifier.finishes.first.first
+ end
+
def test_start
instrumenter.start("foo", payload)
assert_equal [["foo", instrumenter.id, payload]], notifier.starts
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 54fd4345fb..bb20d26a25 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -26,7 +26,7 @@ module Notifications
end
end
- class SubscribeEventObjects < TestCase
+ class SubscribeEventObjectsTest < TestCase
def test_subscribe_events
events = []
@notifier.subscribe do |event|
@@ -81,7 +81,7 @@ module Notifications
assert_equal expected, events
end
- def test_subsribing_to_instrumentation_while_inside_it
+ def test_subscribing_to_instrumentation_while_inside_it
# the repro requires that there are no evented subscribers for the "foo" event,
# so we have to duplicate some of the setup code
old_notifier = ActiveSupport::Notifications.notifier
@@ -128,6 +128,25 @@ module Notifications
assert_equal [["named.subscription", :foo], ["named.subscription", :foo]], @events
end
+ def test_unsubscribing_by_name_leaves_regexp_matched_subscriptions
+ @matched_events = []
+ @notifier.subscribe(/subscription/) { |*args| @matched_events << event(*args) }
+ @notifier.publish("named.subscription", :before)
+ @notifier.wait
+ [@events, @named_events, @matched_events].each do |collector|
+ assert_includes(collector, ["named.subscription", :before])
+ end
+ @notifier.unsubscribe("named.subscription")
+ @notifier.publish("named.subscription", :after)
+ @notifier.publish("other.subscription", :after)
+ @notifier.wait
+ assert_includes(@events, ["named.subscription", :after])
+ assert_includes(@events, ["other.subscription", :after])
+ assert_includes(@matched_events, ["other.subscription", :after])
+ assert_not_includes(@matched_events, ["named.subscription", :after])
+ assert_not_includes(@named_events, ["named.subscription", :after])
+ end
+
private
def event(*args)
args
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index 9449adb6de..b1a1c2d390 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -112,6 +112,38 @@ class SafeBufferTest < ActiveSupport::TestCase
end
end
+ test "can assign value into zero-index" do
+ buffer = ActiveSupport::SafeBuffer.new("012345")
+
+ buffer[0] = "<"
+
+ assert_equal "&lt;12345", buffer
+ end
+
+ test "can assign value into non zero-index" do
+ buffer = ActiveSupport::SafeBuffer.new("012345")
+
+ buffer[2] = "<"
+
+ assert_equal "01&lt;345", buffer
+ end
+
+ test "can assign value into slice" do
+ buffer = ActiveSupport::SafeBuffer.new("012345")
+
+ buffer[0, 3] = "<"
+
+ assert_equal "&lt;345", buffer
+ end
+
+ test "can assign value into offset slice" do
+ buffer = ActiveSupport::SafeBuffer.new("012345")
+
+ buffer[1, 3] = "<"
+
+ assert_equal "0&lt;45", buffer
+ end
+
test "Should escape dirty buffers on add" do
clean = "hello".html_safe
@buffer.gsub!("", "<>")
@@ -151,7 +183,7 @@ class SafeBufferTest < ActiveSupport::TestCase
assert_equal "", ActiveSupport::SafeBuffer.new("foo").clone_empty
end
- test "clone_empty keeps the original dirtyness" do
+ test "clone_empty keeps the original dirtiness" do
assert_predicate @buffer.clone_empty, :html_safe?
assert_not_predicate @buffer.gsub!("", "").clone_empty, :html_safe?
end
diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb
index 34479020e1..a40c813fe3 100644
--- a/activesupport/test/share_lock_test.rb
+++ b/activesupport/test/share_lock_test.rb
@@ -38,7 +38,7 @@ class ShareLockTest < ActiveSupport::TestCase
end
end
- def test_multiple_exlusives_are_able_to_progress
+ def test_multiple_exclusives_are_able_to_progress
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
exclusive_threads = (1..2).map do
Thread.new do
@@ -115,68 +115,66 @@ class ShareLockTest < ActiveSupport::TestCase
def test_exclusive_conflicting_purpose
[true, false].each do |use_upgrading|
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
- begin
- together = Concurrent::CyclicBarrier.new(2)
- conflicting_exclusive_threads = [
- Thread.new do
- @lock.send(use_upgrading ? :sharing : :tap) do
- together.wait
- @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]) { }
- end
+ together = Concurrent::CyclicBarrier.new(2)
+ conflicting_exclusive_threads = [
+ Thread.new do
+ @lock.send(use_upgrading ? :sharing : :tap) do
+ together.wait
+ @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]) { }
end
- ]
-
- assert_threads_stuck conflicting_exclusive_threads # wait for threads to get into their respective `exclusive {}` blocks
-
- # This thread will be stuck as long as any other thread is in
- # 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 { }
end
- assert_threads_stuck no_purpose_thread
+ ]
- # This thread is compatible with both of the "primary"
- # attempts above. It's initially stuck on the outer share
- # 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: []) { }
- end
- assert_threads_stuck compatible_thread
+ assert_threads_stuck conflicting_exclusive_threads # wait for threads to get into their respective `exclusive {}` blocks
- assert_threads_stuck conflicting_exclusive_threads
+ # This thread will be stuck as long as any other thread is in
+ # 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 { }
+ end
+ assert_threads_stuck no_purpose_thread
+
+ # This thread is compatible with both of the "primary"
+ # attempts above. It's initially stuck on the outer share
+ # 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: []) { }
+ end
+ assert_threads_stuck compatible_thread
- sharing_thread_release_latch.count_down
+ assert_threads_stuck conflicting_exclusive_threads
- assert_threads_not_stuck compatible_thread # compatible thread is now able to squeak through
+ sharing_thread_release_latch.count_down
- if use_upgrading
- # The "primary" threads both each hold a share lock, and are
- # mutually incompatible; they're still stuck.
- assert_threads_stuck conflicting_exclusive_threads
+ assert_threads_not_stuck compatible_thread # compatible thread is now able to squeak through
- # The thread without a specified purpose is also stuck; it's
- # not compatible with anything.
- assert_threads_stuck no_purpose_thread
- else
- # As the primaries didn't hold a share lock, as soon as the
- # outer one was released, all the exclusive locks are free
- # to be acquired in turn.
+ if use_upgrading
+ # The "primary" threads both each hold a share lock, and are
+ # mutually incompatible; they're still stuck.
+ assert_threads_stuck conflicting_exclusive_threads
- assert_threads_not_stuck conflicting_exclusive_threads
- assert_threads_not_stuck no_purpose_thread
- end
- ensure
- conflicting_exclusive_threads.each(&:kill)
- no_purpose_thread.kill
+ # The thread without a specified purpose is also stuck; it's
+ # not compatible with anything.
+ assert_threads_stuck no_purpose_thread
+ else
+ # As the primaries didn't hold a share lock, as soon as the
+ # outer one was released, all the exclusive locks are free
+ # to be acquired in turn.
+
+ assert_threads_not_stuck conflicting_exclusive_threads
+ assert_threads_not_stuck no_purpose_thread
end
+ ensure
+ conflicting_exclusive_threads.each(&:kill)
+ no_purpose_thread.kill
end
end
end
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index 8698c66e6d..56cd2665e0 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -104,7 +104,7 @@ class AssertionsTest < ActiveSupport::TestCase
def test_expression_is_evaluated_in_the_appropriate_scope
silence_warnings do
local_scope = "foo"
- local_scope = local_scope # to suppress unused variable warning
+ _ = local_scope # to suppress unused variable warning
assert_difference("local_scope; @object.num") { @object.increment }
end
end
diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb
index 8c47f2cdc7..a1f84bf69e 100644
--- a/activesupport/test/time_travel_test.rb
+++ b/activesupport/test/time_travel_test.rb
@@ -3,24 +3,25 @@
require "abstract_unit"
require "active_support/core_ext/date_time"
require "active_support/core_ext/numeric/time"
+require "time_zone_test_helpers"
class TimeTravelTest < ActiveSupport::TestCase
+ include TimeZoneTestHelpers
+
class TimeSubclass < ::Time; end
class DateSubclass < ::Date; end
class DateTimeSubclass < ::DateTime; end
def test_time_helper_travel
Time.stub(:now, Time.now) do
- begin
- expected_time = Time.now + 1.day
- travel 1.day
+ expected_time = Time.now + 1.day
+ travel 1.day
- assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
- assert_equal expected_time.to_date, Date.today
- assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
- ensure
- travel_back
- end
+ assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
+ assert_equal expected_time.to_date, Date.today
+ assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
+ ensure
+ travel_back
end
end
@@ -42,16 +43,14 @@ class TimeTravelTest < ActiveSupport::TestCase
def test_time_helper_travel_to
Time.stub(:now, Time.now) do
- begin
- expected_time = Time.new(2004, 11, 24, 01, 04, 44)
- travel_to expected_time
+ expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ travel_to expected_time
- assert_equal expected_time, Time.now
- assert_equal Date.new(2004, 11, 24), Date.today
- assert_equal expected_time.to_datetime, DateTime.now
- ensure
- travel_back
- end
+ assert_equal expected_time, Time.now
+ assert_equal Date.new(2004, 11, 24), Date.today
+ assert_equal expected_time.to_datetime, DateTime.now
+ ensure
+ travel_back
end
end
@@ -71,23 +70,35 @@ class TimeTravelTest < ActiveSupport::TestCase
end
end
+ def test_time_helper_travel_to_with_time_zone
+ with_env_tz "US/Eastern" do
+ with_tz_default ActiveSupport::TimeZone["UTC"] do
+ Time.stub(:now, Time.now) do
+ expected_time = 5.minutes.ago
+
+ travel_to 5.minutes.ago do
+ assert_equal expected_time.to_s(:db), Time.zone.now.to_s(:db)
+ end
+ end
+ end
+ end
+ end
+
def test_time_helper_travel_back
Time.stub(:now, Time.now) do
- begin
- expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ expected_time = Time.new(2004, 11, 24, 01, 04, 44)
- travel_to expected_time
- assert_equal expected_time, Time.now
- assert_equal Date.new(2004, 11, 24), Date.today
- assert_equal expected_time.to_datetime, DateTime.now
- travel_back
+ travel_to expected_time
+ assert_equal expected_time, Time.now
+ assert_equal Date.new(2004, 11, 24), Date.today
+ assert_equal expected_time.to_datetime, DateTime.now
+ travel_back
- assert_not_equal expected_time, Time.now
- assert_not_equal Date.new(2004, 11, 24), Date.today
- assert_not_equal expected_time.to_datetime, DateTime.now
- ensure
- travel_back
- end
+ assert_not_equal expected_time, Time.now
+ assert_not_equal Date.new(2004, 11, 24), Date.today
+ assert_not_equal expected_time.to_datetime, DateTime.now
+ ensure
+ travel_back
end
end
@@ -122,24 +133,22 @@ class TimeTravelTest < ActiveSupport::TestCase
def test_time_helper_travel_to_with_subsequent_calls
Time.stub(:now, Time.now) do
- begin
- initial_expected_time = Time.new(2004, 11, 24, 01, 04, 44)
- subsequent_expected_time = Time.new(2004, 10, 24, 01, 04, 44)
- assert_nothing_raised do
- travel_to initial_expected_time
- travel_to subsequent_expected_time
+ initial_expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ subsequent_expected_time = Time.new(2004, 10, 24, 01, 04, 44)
+ assert_nothing_raised do
+ travel_to initial_expected_time
+ travel_to subsequent_expected_time
- assert_equal subsequent_expected_time, Time.now
+ assert_equal subsequent_expected_time, Time.now
- travel_back
- end
- ensure
travel_back
end
+ ensure
+ travel_back
end
end
- def test_travel_to_will_reset_the_usec_to_avoid_mysql_rouding
+ def test_travel_to_will_reset_the_usec_to_avoid_mysql_rounding
Time.stub(:now, Time.now) do
travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do
assert_equal 50, Time.now.sec
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 6d45a6726d..f948c363df 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -32,7 +32,7 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
- def test_period_for_local_with_ambigiuous_time
+ def test_period_for_local_with_ambiguous_time
zone = ActiveSupport::TimeZone["Moscow"]
period = zone.period_for_local(Time.utc(2015, 1, 1))
assert_equal period, zone.period_for_local(Time.utc(2014, 10, 26, 1, 0, 0))
diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb
index 7d19447598..9e29a93ea0 100644
--- a/activesupport/test/transliterate_test.rb
+++ b/activesupport/test/transliterate_test.rb
@@ -30,6 +30,12 @@ class TransliterateTest < ActiveSupport::TestCase
I18n.locale = default_locale
end
+ def test_transliterate_respects_the_locale_argument
+ char = [117, 776].pack("U*") # "ü" as ASCII "u" plus COMBINING DIAERESIS
+ I18n.backend.store_translations(:de, i18n: { transliterate: { rule: { "ü" => "ue" } } })
+ assert_equal "ue", ActiveSupport::Inflector.transliterate(char, locale: :de)
+ end
+
def test_transliterate_should_allow_a_custom_replacement_char
assert_equal "a*b", ActiveSupport::Inflector.transliterate("a索b", "*")
end