diff options
Diffstat (limited to 'activesupport/lib')
14 files changed, 129 insertions, 46 deletions
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 5589c71281..9e242ddeaa 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -34,6 +34,7 @@ module ActiveSupport extend ActiveSupport::Autoload autoload :Concern + autoload :ActionableError autoload :CurrentAttributes autoload :Dependencies autoload :DescendantsTracker diff --git a/activesupport/lib/active_support/actionable_error.rb b/activesupport/lib/active_support/actionable_error.rb new file mode 100644 index 0000000000..7db14cd178 --- /dev/null +++ b/activesupport/lib/active_support/actionable_error.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module ActiveSupport + # Actionable errors let's you define actions to resolve an error. + # + # To make an error actionable, include the <tt>ActiveSupport::ActionableError</tt> + # module and invoke the +action+ class macro to define the action. An action + # needs a name and a block to execute. + module ActionableError + extend Concern + + class NonActionable < StandardError; end + + included do + class_attribute :_actions, default: {} + end + + def self.actions(error) # :nodoc: + case error + when ActionableError, -> it { Class === it && it < ActionableError } + error._actions + else + {} + end + end + + def self.dispatch(error, name) # :nodoc: + actions(error).fetch(name).call + rescue KeyError + raise NonActionable, "Cannot find action \"#{name}\"" + end + + module ClassMethods + # Defines an action that can resolve the error. + # + # class PendingMigrationError < MigrationError + # include ActiveSupport::ActionableError + # + # action "Run pending migrations" do + # ActiveRecord::Tasks::DatabaseTasks.migrate + # end + # end + def action(name, &block) + _actions[name] = block + end + end + end +end diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb index 62973eca58..02cbfbaee6 100644 --- a/activesupport/lib/active_support/backtrace_cleaner.rb +++ b/activesupport/lib/active_support/backtrace_cleaner.rb @@ -122,7 +122,11 @@ module ActiveSupport end def noise(backtrace) - backtrace - silence(backtrace) + backtrace.select do |line| + @silencers.any? do |s| + s.call(line) + end + end end end end diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb index 9a55e49e27..bb092fcde9 100644 --- a/activesupport/lib/active_support/cache/redis_cache_store.rb +++ b/activesupport/lib/active_support/cache/redis_cache_store.rb @@ -152,12 +152,14 @@ module ActiveSupport # Creates a new Redis cache store. # - # Handles three options: block provided to instantiate, single URL - # provided, and multiple URLs provided. + # Handles four options: :redis block, :redis instance, single :url + # string, and multiple :url strings. # - # :redis Proc -> options[:redis].call - # :url String -> Redis.new(url: …) - # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …]) + # Option Class Result + # :redis Proc -> options[:redis].call + # :redis Object -> options[:redis] + # :url String -> Redis.new(url: …) + # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …]) # # No namespace is set by default. Provide one if the Redis cache # server is shared with other apps: <tt>namespace: 'myapp-cache'</tt>. @@ -361,6 +363,7 @@ module ActiveSupport def read_multi_mget(*names) options = names.extract_options! options = merged_options(options) + return {} if names == [] keys = names.map { |name| normalize_key(name, options) } diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index 5d356a0ab6..708c445031 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -110,7 +110,7 @@ module ActiveSupport base.instance_variable_set(:@_dependencies, []) end - def append_features(base) + def append_features(base) #:nodoc: if base.instance_variable_defined?(:@_dependencies) base.instance_variable_get(:@_dependencies) << self false @@ -123,6 +123,9 @@ module ActiveSupport end end + # Evaluate given block in context of base class, + # so that you can write class macros here. + # When you define more than one +included+ block, it raises an exception. def included(base = nil, &block) if base.nil? if instance_variable_defined?(:@_included_block) @@ -137,6 +140,26 @@ module ActiveSupport end end + # Define class methods from given block. + # You can define private class methods as well. + # + # module Example + # extend ActiveSupport::Concern + # + # class_methods do + # def foo; puts 'foo'; end + # + # private + # def bar; puts 'bar'; end + # end + # end + # + # class Buzz + # include Example + # end + # + # Buzz.foo # => "foo" + # Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError) def class_methods(&class_methods_module_definition) mod = const_defined?(:ClassMethods, false) ? const_get(:ClassMethods) : 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 638152626b..645b1fea17 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -213,6 +213,12 @@ module ActiveSupport #:nodoc: dup.concat(other) end + def *(*) + new_safe_buffer = super + new_safe_buffer.instance_variable_set(:@html_safe, @html_safe) + new_safe_buffer + end + def %(args) case args when Hash diff --git a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb index a43d03cf09..fd39ad6e55 100644 --- a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb +++ b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb @@ -10,6 +10,8 @@ module ActiveSupport def clear Dependencies.unload_interlock do Rails.autoloaders.main.reload + rescue Zeitwerk::ReloadingDisabledError + raise "reloading is disabled because config.cache_classes is true" end end @@ -22,16 +24,12 @@ module ActiveSupport end def autoloaded_constants - cpaths = [] - Rails.autoloaders.each do |autoloader| - cpaths.concat(autoloader.loaded_cpaths.to_a) - end - cpaths + Rails.autoloaders.main.unloadable_cpaths end def autoloaded?(object) cpath = object.is_a?(Module) ? object.name : object.to_s - Rails.autoloaders.any? { |autoloader| autoloader.loaded?(cpath) } + Rails.autoloaders.main.unloadable_cpath?(cpath) end def verbose=(verbose) @@ -51,15 +49,15 @@ module ActiveSupport end class << self - def take_over - setup_autoloaders + def take_over(enable_reloading:) + setup_autoloaders(enable_reloading) freeze_paths decorate_dependencies end private - def setup_autoloaders + def setup_autoloaders(enable_reloading) Dependencies.autoload_paths.each do |autoload_path| # Zeitwerk only accepts existing directories in `push_dir` to # prevent misconfigurations. @@ -72,6 +70,7 @@ module ActiveSupport autoloader.do_not_eager_load(autoload_path) unless eager_load?(autoload_path) end + Rails.autoloaders.main.enable_reloading if enable_reloading Rails.autoloaders.each(&:setup) end diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index d99571790f..7c0a54a1d0 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/module/redefine_method" module ActiveSupport class Deprecation @@ -52,29 +53,17 @@ module ActiveSupport options = method_names.extract_options! deprecator = options.delete(:deprecator) || self method_names += options.keys - mod = Module.new + mod = nil method_names.each do |method_name| if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name) - aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ""), $1 - with_method = "#{aliased_method}_with_deprecation#{punctuation}" - without_method = "#{aliased_method}_without_deprecation#{punctuation}" - - target_module.define_method(with_method) do |*args, &block| + method = target_module.instance_method(method_name) + target_module.redefine_method(method_name) do |*args, &block| deprecator.deprecation_warning(method_name, options[method_name]) - send(without_method, *args, &block) - end - - 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) + method.bind(self).call(*args, &block) end else + mod ||= Module.new mod.define_method(method_name) do |*args, &block| deprecator.deprecation_warning(method_name, options[method_name]) super(*args, &block) @@ -82,7 +71,7 @@ module ActiveSupport end end - target_module.prepend(mod) unless mod.instance_methods(false).empty? + target_module.prepend(mod) if mod end end end diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb index 2dca990712..21565138a7 100644 --- a/activesupport/lib/active_support/descendants_tracker.rb +++ b/activesupport/lib/active_support/descendants_tracker.rb @@ -23,10 +23,10 @@ module ActiveSupport def clear if defined? ActiveSupport::Dependencies @@direct_descendants.each do |klass, descendants| - if ActiveSupport::Dependencies.autoloaded?(klass) + if Dependencies.autoloaded?(klass) @@direct_descendants.delete(klass) else - descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) } + descendants.reject! { |v| Dependencies.autoloaded?(v) } end end else diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 97b4634d7b..a30bd11a87 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -4,7 +4,6 @@ require "active_support/core_ext/array/conversions" require "active_support/core_ext/module/delegation" require "active_support/core_ext/object/acts_like" require "active_support/core_ext/string/filters" -require "active_support/deprecation" module ActiveSupport # Provides accurate date and time measurements using Date#advance and diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb index 584930e413..8faa93a3e4 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -97,7 +97,8 @@ module I18n If you desire the default locale to be included in the defaults, please explicitly configure it with `config.i18n.fallbacks.defaults = [I18n.default_locale]` or `config.i18n.fallbacks = [I18n.default_locale, - {...}]` + {...}]`. If you want to opt-in to the new behavior, use + `config.i18n.fallbacks.defaults = [nil, {...}]`. MSG args.unshift I18n.default_locale end diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index c506b35b1e..8812b67f63 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -180,13 +180,13 @@ module ActiveSupport def start(name, id, payload) timestack = Thread.current[:_timestack] ||= [] - timestack.push Time.now + timestack.push Concurrent.monotonic_time end def finish(name, id, payload) timestack = Thread.current[:_timestack] started = timestack.pop - @delegate.call(name, started, Time.now, id, payload) + @delegate.call(name, started, Concurrent.monotonic_time, id, payload) end end diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index a03e7e483e..12546511a8 100644 --- a/activesupport/lib/active_support/notifications/instrumenter.rb +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -68,9 +68,8 @@ module ActiveSupport @transaction_id = transaction_id @end = ending @children = [] - @duration = nil - @cpu_time_start = nil - @cpu_time_finish = nil + @cpu_time_start = 0 + @cpu_time_finish = 0 @allocation_count_start = 0 @allocation_count_finish = 0 end @@ -125,7 +124,7 @@ module ActiveSupport # # @event.duration # => 1000.138 def duration - @duration ||= 1000.0 * (self.end - time) + 1000.0 * (self.end - time) end def <<(event) diff --git a/activesupport/lib/active_support/testing/parallelization.rb b/activesupport/lib/active_support/testing/parallelization.rb index 63440069b1..e760bf5ce3 100644 --- a/activesupport/lib/active_support/testing/parallelization.rb +++ b/activesupport/lib/active_support/testing/parallelization.rb @@ -71,7 +71,9 @@ module ActiveSupport fork do DRb.stop_service - after_fork(worker) + begin + after_fork(worker) + rescue => setup_exception; end queue = DRbObject.new_with_uri(@url) @@ -79,7 +81,11 @@ module ActiveSupport klass = job[0] method = job[1] reporter = job[2] - result = Minitest.run_one_method(klass, method) + result = klass.with_info_handler reporter do + Minitest.run_one_method(klass, method) + end + + add_setup_exception(result, setup_exception) if setup_exception begin queue.record(reporter, result) @@ -104,6 +110,11 @@ module ActiveSupport @queue_size.times { @queue << nil } @pool.each { |pid| Process.waitpid pid } end + + private + def add_setup_exception(result, setup_exception) + result.failures.prepend Minitest::UnexpectedError.new(setup_exception) + end end end end |