aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG.md24
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/marshal.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/introspection.rb4
-rw-r--r--activesupport/lib/active_support/dependencies.rb61
-rw-r--r--activesupport/lib/active_support/dependencies/interlock.rb14
-rw-r--r--activesupport/lib/active_support/evented_file_update_checker.rb9
-rw-r--r--activesupport/lib/active_support/execution_wrapper.rb78
-rw-r--r--activesupport/lib/active_support/executor.rb6
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb3
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb4
-rw-r--r--activesupport/lib/active_support/reloader.rb123
-rw-r--r--activesupport/lib/active_support/rescuable.rb10
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb1
-rw-r--r--activesupport/test/abstract_unit.rb7
-rw-r--r--activesupport/test/autoloading_fixtures/raises_arbitrary_exception.rb4
-rw-r--r--activesupport/test/autoloading_fixtures/throws.rb4
-rw-r--r--activesupport/test/core_ext/marshal_test.rb11
-rw-r--r--activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb6
-rw-r--r--activesupport/test/core_ext/module_test.rb8
-rw-r--r--activesupport/test/dependencies_test.rb22
-rw-r--r--activesupport/test/executor_test.rb76
-rw-r--r--activesupport/test/reloader_test.rb85
27 files changed, 497 insertions, 83 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 9a44773497..dd1b347e73 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -2,6 +2,22 @@
*Glauco Custódio*
+* Prevent `Marshal.load` from looping infinitely when trying to autoload a constant
+ which resolves to a different name.
+
+ *Olek Janiszewski*
+
+* Deprecate `Module.local_constants`. Please use `Module.constants(false)` instead.
+
+ *Yuichiro Kaneko*
+
+* Publish `ActiveSupport::Executor` and `ActiveSupport::Reloader` APIs to allow
+ components and libraries to manage, and participate in, the execution of
+ application code, and the application reloading process.
+
+ *Matthew Draper*
+
+
## Rails 5.0.0.beta3 (February 24, 2016) ##
* Deprecate arguments on `assert_nothing_raised`.
@@ -13,7 +29,7 @@
*Tara Scherner de la Fuente*
-* Make `benchmark('something', silence: true)` actually work
+* Make `benchmark('something', silence: true)` actually work.
*DHH*
@@ -28,13 +44,13 @@
*Brian Christian*
-* Fix regression in `Hash#dig` for HashWithIndifferentAccess.
+* Fix regression in `Hash#dig` for HashWithIndifferentAccess.
- *Jon Moss*
+ *Jon Moss*
## Rails 5.0.0.beta2 (February 01, 2016) ##
-* Change number_to_currency behavior for checking negativity.
+* Change `number_to_currency` behavior for checking negativity.
Used `to_f.negative` instead of using `to_f.phase` for checking negativity
of a number in number_to_currency helper.
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index 68a80701ed..71fe4d7253 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
- s.homepage = 'http://www.rubyonrails.org'
+ s.homepage = 'http://rubyonrails.org'
s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*']
s.require_path = 'lib'
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 94fe893149..72777baecd 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -33,10 +33,13 @@ module ActiveSupport
autoload :Concern
autoload :Dependencies
autoload :DescendantsTracker
+ autoload :ExecutionWrapper
+ autoload :Executor
autoload :FileUpdateChecker
autoload :EventedFileUpdateChecker
autoload :LogSubscriber
autoload :Notifications
+ autoload :Reloader
eager_autoload do
autoload :BacktraceCleaner
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 2de0d19a7e..4da7fdd159 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
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/try'
+
module DateAndTime
module Calculations
DAYS_INTO_WEEK = {
diff --git a/activesupport/lib/active_support/core_ext/marshal.rb b/activesupport/lib/active_support/core_ext/marshal.rb
index e333b26133..ca278cb2fa 100644
--- a/activesupport/lib/active_support/core_ext/marshal.rb
+++ b/activesupport/lib/active_support/core_ext/marshal.rb
@@ -5,7 +5,10 @@ module ActiveSupport
rescue ArgumentError, NameError => exc
if exc.message.match(%r|undefined class/module (.+)|)
# try loading the class/module
- $1.constantize
+ loaded = $1.constantize
+
+ raise unless $1 == loaded.name
+
# if it is an IO we need to go back to read the object
source.rewind if source.respond_to?(:rewind)
retry
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 76825862d7..567ac825e9 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -27,7 +27,7 @@ class Module
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
#
# module HairColors
- # mattr_writer :hair_colors, instance_reader: false
+ # mattr_reader :hair_colors, instance_reader: false
# end
#
# class Person
@@ -40,7 +40,7 @@ class Module
# Also, you can pass a block to set up the attribute with a default value.
#
# module HairColors
- # cattr_reader :hair_colors do
+ # mattr_reader :hair_colors do
# [:brown, :black, :blonde, :red]
# end
# end
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 8a7e6776da..0b3d18301e 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
@@ -47,7 +47,7 @@ class Module
unless options[:instance_reader] == false || options[:instance_accessor] == false
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{sym}
- Thread.current[:"attr_#{self.class.name}_#{sym}"]
+ Thread.current[:"attr_#{name}_#{sym}"]
end
EOS
end
@@ -86,7 +86,7 @@ class Module
unless options[:instance_writer] == false || options[:instance_accessor] == false
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{sym}=(obj)
- Thread.current[:"attr_#{self.class.name}_#{sym}"] = obj
+ Thread.current[:"attr_#{name}_#{sym}"] = obj
end
EOS
end
diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb
index f1d26ef28f..fa692e1b0e 100644
--- a/activesupport/lib/active_support/core_ext/module/introspection.rb
+++ b/activesupport/lib/active_support/core_ext/module/introspection.rb
@@ -57,6 +57,10 @@ class Module
end
def local_constants #:nodoc:
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Module#local_constants is deprecated and will be removed in Rails 5.1.
+ Use Module#constants(false) instead.
+ MSG
constants(false)
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index fd9fbff96a..57f6286de3 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -88,15 +88,6 @@ module ActiveSupport #:nodoc:
mattr_accessor :explicitly_unloadable_constants
self.explicitly_unloadable_constants = []
- # The logger is used for generating information on the action run-time
- # (including benchmarking) if available. Can be set to nil for no logging.
- # Compatible with both Ruby's own Logger and Log4r loggers.
- mattr_accessor :logger
-
- # Set to +true+ to enable logging of const_missing and file loads.
- mattr_accessor :log_activity
- self.log_activity = false
-
# The WatchStack keeps a stack of the modules being watched as files are
# loaded. If a file in the process of being loaded (parent.rb) triggers the
# load of another file (child.rb) the stack will ensure that child.rb
@@ -143,7 +134,7 @@ module ActiveSupport #:nodoc:
next unless mod.is_a?(Module)
# Get a list of the constants that were added
- new_constants = mod.local_constants - original_constants
+ new_constants = mod.constants(false) - original_constants
# @stack[namespace] returns an Array of the constants that are being evaluated
# for that namespace. For instance, if parent.rb requires child.rb, the first
@@ -171,7 +162,7 @@ module ActiveSupport #:nodoc:
@watching << namespaces.map do |namespace|
module_name = Dependencies.to_constant_name(namespace)
original_constants = Dependencies.qualified_const_defined?(module_name) ?
- Inflector.constantize(module_name).local_constants : []
+ Inflector.constantize(module_name).constants(false) : []
@stack[module_name] << original_constants
module_name
@@ -352,7 +343,6 @@ module ActiveSupport #:nodoc:
end
def clear
- log_call
Dependencies.unload_interlock do
loaded.clear
loading.clear
@@ -361,7 +351,6 @@ module ActiveSupport #:nodoc:
end
def require_or_load(file_name, const_path = nil)
- log_call file_name, const_path
file_name = $` if file_name =~ /\.rb\z/
expanded = File.expand_path(file_name)
return if loaded.include?(expanded)
@@ -377,8 +366,6 @@ module ActiveSupport #:nodoc:
begin
if load?
- log "loading #{file_name}"
-
# Enable warnings if this file has not been loaded before and
# warnings_on_first_load is set.
load_args = ["#{file_name}.rb"]
@@ -390,7 +377,6 @@ module ActiveSupport #:nodoc:
enable_warnings { result = load_file(*load_args) }
end
else
- log "requiring #{file_name}"
result = require file_name
end
rescue Exception
@@ -483,7 +469,6 @@ module ActiveSupport #:nodoc:
# set of names that the file at +path+ may define. See
# +loadable_constants_for_path+ for more details.
def load_file(path, const_paths = loadable_constants_for_path(path))
- log_call path, const_paths
const_paths = [const_paths].compact unless const_paths.is_a? Array
parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object }
@@ -494,7 +479,6 @@ module ActiveSupport #:nodoc:
autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
autoloaded_constants.uniq!
- log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
result
end
@@ -508,8 +492,6 @@ module ActiveSupport #:nodoc:
# it is not possible to load the constant into from_mod, try its parent
# module using +const_missing+.
def load_missing_constant(from_mod, const_name)
- log_call from_mod, const_name
-
unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
end
@@ -673,25 +655,20 @@ module ActiveSupport #:nodoc:
# exception, any new constants are regarded as being only partially defined
# and will be removed immediately.
def new_constants_in(*descs)
- log_call(*descs)
-
constant_watch_stack.watch_namespaces(descs)
- aborting = true
+ success = false
begin
yield # Now yield to the code that is to define new constants.
- aborting = false
+ success = true
ensure
new_constants = constant_watch_stack.new_constants
- log "New constants: #{new_constants * ', '}"
- return new_constants unless aborting
+ return new_constants if success
- log "Error during loading, removing partially loaded constants "
- new_constants.each { |c| remove_constant(c) }.clear
+ # Remove partially loaded constants.
+ new_constants.each { |c| remove_constant(c) }
end
-
- []
end
# Convert the provided const desc to a qualified constant name (as a string).
@@ -738,8 +715,6 @@ module ActiveSupport #:nodoc:
parent = constantize(parent_name)
end
- log "removing constant #{const}"
-
# In an autoloaded user.rb like this
#
# autoload :Foo, 'foo'
@@ -760,7 +735,7 @@ module ActiveSupport #:nodoc:
begin
constantized = parent.const_get(to_remove, false)
rescue NameError
- log "the constant #{const} is not reachable anymore, skipping"
+ # The constant is no longer reachable, just skip it.
return
else
constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
@@ -770,27 +745,9 @@ module ActiveSupport #:nodoc:
begin
parent.instance_eval { remove_const to_remove }
rescue NameError
- log "the constant #{const} is not reachable anymore, skipping"
+ # The constant is no longer reachable, just skip it.
end
end
-
- protected
- def log_call(*args)
- if log_activity?
- arg_str = args.collect(&:inspect) * ', '
- /in `([a-z_\?\!]+)'/ =~ caller(1).first
- selector = $1 || '<unknown>'
- log "called #{selector}(#{arg_str})"
- end
- end
-
- def log(msg)
- logger.debug "Dependencies: #{msg}" if log_activity?
- end
-
- def log_activity?
- logger && log_activity
- end
end
end
diff --git a/activesupport/lib/active_support/dependencies/interlock.rb b/activesupport/lib/active_support/dependencies/interlock.rb
index 47bcecbb35..f1865ca2f8 100644
--- a/activesupport/lib/active_support/dependencies/interlock.rb
+++ b/activesupport/lib/active_support/dependencies/interlock.rb
@@ -19,14 +19,12 @@ module ActiveSupport #:nodoc:
end
end
- # Attempt to obtain an "unloading" (exclusive) lock. If possible,
- # execute the supplied block while holding the lock. If there is
- # concurrent activity, return immediately (without executing the
- # block) instead of waiting.
- def attempt_unloading
- @lock.exclusive(purpose: :unload, compatible: [:load, :unload], after_compatible: [:load, :unload], no_wait: true) do
- yield
- end
+ def start_unloading
+ @lock.start_exclusive(purpose: :unload, compatible: [:load, :unload])
+ end
+
+ def done_unloading
+ @lock.stop_exclusive(compatible: [:load, :unload])
end
def start_running
diff --git a/activesupport/lib/active_support/evented_file_update_checker.rb b/activesupport/lib/active_support/evented_file_update_checker.rb
index 315be85fb3..6a02a838b7 100644
--- a/activesupport/lib/active_support/evented_file_update_checker.rb
+++ b/activesupport/lib/active_support/evented_file_update_checker.rb
@@ -21,7 +21,13 @@ module ActiveSupport
# 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 { require 'listen' }
+ 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
+ end
Listen.to(*dtw, &method(:changed)).start
end
end
@@ -37,6 +43,7 @@ module ActiveSupport
def execute_if_updated
if updated?
+ yield if block_given?
execute
true
end
diff --git a/activesupport/lib/active_support/execution_wrapper.rb b/activesupport/lib/active_support/execution_wrapper.rb
new file mode 100644
index 0000000000..2bd1c01d35
--- /dev/null
+++ b/activesupport/lib/active_support/execution_wrapper.rb
@@ -0,0 +1,78 @@
+require 'active_support/callbacks'
+
+module ActiveSupport
+ class ExecutionWrapper
+ include ActiveSupport::Callbacks
+
+ Null = Object.new # :nodoc:
+ def Null.complete! # :nodoc:
+ end
+
+ define_callbacks :run
+ define_callbacks :complete
+
+ def self.to_run(*args, &block)
+ set_callback(:run, *args, &block)
+ end
+
+ def self.to_complete(*args, &block)
+ set_callback(:complete, *args, &block)
+ end
+
+ # Run this execution.
+ #
+ # Returns an instance, whose +complete!+ method *must* be invoked
+ # after the work has been performed.
+ #
+ # Where possible, prefer +wrap+.
+ def self.run!
+ if active?
+ Null
+ else
+ new.tap(&:run!)
+ end
+ end
+
+ # Perform the work in the supplied block as an execution.
+ def self.wrap
+ return yield if active?
+
+ state = run!
+ begin
+ yield
+ ensure
+ state.complete!
+ end
+ end
+
+ class << self # :nodoc:
+ attr_accessor :active
+ end
+
+ def self.inherited(other) # :nodoc:
+ super
+ other.active = Concurrent::Hash.new
+ end
+
+ self.active = Concurrent::Hash.new
+
+ def self.active? # :nodoc:
+ @active[Thread.current]
+ end
+
+ def run! # :nodoc:
+ self.class.active[Thread.current] = true
+ run_callbacks(:run)
+ end
+
+ # Complete this in-flight execution. This method *must* be called
+ # exactly once on the result of any call to +run!+.
+ #
+ # Where possible, prefer +wrap+.
+ def complete!
+ run_callbacks(:complete)
+ ensure
+ self.class.active.delete Thread.current
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/executor.rb b/activesupport/lib/active_support/executor.rb
new file mode 100644
index 0000000000..602fb11a44
--- /dev/null
+++ b/activesupport/lib/active_support/executor.rb
@@ -0,0 +1,6 @@
+require 'active_support/execution_wrapper'
+
+module ActiveSupport
+ class Executor < ExecutionWrapper
+ end
+end
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index 1fa9335080..8708a502e6 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -23,7 +23,7 @@ module ActiveSupport
# I18n.reload!
# end
#
- # ActionDispatch::Reloader.to_prepare do
+ # ActiveSupport::Reloader.to_prepare do
# i18n_reloader.execute_if_updated
# end
class FileUpdateChecker
@@ -81,6 +81,7 @@ module ActiveSupport
# Execute the block given if updated.
def execute_if_updated
if updated?
+ yield if block_given?
execute
true
else
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index 82aacf3b24..6cc7c90c12 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -64,8 +64,8 @@ module I18n
end
app.reloaders << reloader
- ActionDispatch::Reloader.to_prepare do
- reloader.execute_if_updated
+ app.reloader.to_run do
+ reloader.execute_if_updated { require_unload_lock! }
# TODO: remove the following line as soon as the return value of
# callbacks is ignored, that is, returning `false` does not
# display a deprecation warning or halts the callback chain.
diff --git a/activesupport/lib/active_support/reloader.rb b/activesupport/lib/active_support/reloader.rb
new file mode 100644
index 0000000000..5d1f0e1e66
--- /dev/null
+++ b/activesupport/lib/active_support/reloader.rb
@@ -0,0 +1,123 @@
+require 'active_support/execution_wrapper'
+
+module ActiveSupport
+ #--
+ # This class defines several callbacks:
+ #
+ # to_prepare -- Run once at application startup, and also from
+ # +to_run+.
+ #
+ # to_run -- Run before a work run that is reloading. If
+ # +reload_classes_only_on_change+ is true (the default), the class
+ # unload will have already occurred.
+ #
+ # to_complete -- Run after a work run that has reloaded. If
+ # +reload_classes_only_on_change+ is false, the class unload will
+ # have occurred after the work run, but before this callback.
+ #
+ # before_class_unload -- Run immediately before the classes are
+ # unloaded.
+ #
+ # after_class_unload -- Run immediately after the classes are
+ # unloaded.
+ #
+ class Reloader < ExecutionWrapper
+ define_callbacks :prepare
+
+ define_callbacks :class_unload
+
+ def self.to_prepare(*args, &block)
+ set_callback(:prepare, *args, &block)
+ end
+
+ def self.before_class_unload(*args, &block)
+ set_callback(:class_unload, *args, &block)
+ end
+
+ def self.after_class_unload(*args, &block)
+ set_callback(:class_unload, :after, *args, &block)
+ end
+
+ to_run(:after) { self.class.prepare! }
+
+ # Initiate a manual reload
+ def self.reload!
+ executor.wrap do
+ new.tap(&:run!).complete!
+ end
+ prepare!
+ end
+
+ def self.run! # :nodoc:
+ if check!
+ super
+ else
+ Null
+ end
+ end
+
+ # Run the supplied block as a work unit, reloading code as needed
+ def self.wrap
+ executor.wrap do
+ super
+ end
+ end
+
+ class_attribute :executor
+ class_attribute :check
+
+ self.executor = Executor
+ self.check = lambda { false }
+
+ def self.check! # :nodoc:
+ @should_reload ||= check.call
+ end
+
+ def self.reloaded! # :nodoc:
+ @should_reload = false
+ end
+
+ def self.prepare! # :nodoc:
+ new.run_callbacks(:prepare)
+ end
+
+ def initialize
+ super
+ @locked = false
+ end
+
+ # Acquire the ActiveSupport::Dependencies::Interlock unload lock,
+ # ensuring it will be released automatically
+ def require_unload_lock!
+ unless @locked
+ ActiveSupport::Dependencies.interlock.start_unloading
+ @locked = true
+ end
+ end
+
+ # Release the unload lock if it has been previously obtained
+ def release_unload_lock!
+ if @locked
+ @locked = false
+ ActiveSupport::Dependencies.interlock.done_unloading
+ end
+ end
+
+ def run! # :nodoc:
+ super
+ release_unload_lock!
+ end
+
+ def class_unload!(&block) # :nodoc:
+ require_unload_lock!
+ run_callbacks(:class_unload, &block)
+ end
+
+ def complete! # :nodoc:
+ super
+ self.class.reloaded!
+ ensure
+ release_unload_lock!
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
index fcf5553061..73bc52b56f 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -115,5 +115,15 @@ module ActiveSupport
end
end
end
+
+ def index_of_handler_for_rescue(exception)
+ handlers = self.class.rescue_handlers.reverse_each.with_index
+ _, index = handlers.detect do |(klass_name, _), _|
+ klass = self.class.const_get(klass_name) rescue nil
+ klass ||= (klass_name.constantize rescue nil)
+ klass === exception if klass
+ end
+ index
+ end
end
end
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 7ca3592520..118bf8eab0 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -1,7 +1,6 @@
require 'tzinfo'
require 'concurrent/map'
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/object/try'
module ActiveSupport
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index c0e23e89f7..7f0fcd6996 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -1,12 +1,5 @@
ORIG_ARGV = ARGV.dup
-begin
- old, $VERBOSE = $VERBOSE, nil
- require File.expand_path('../../../load_paths', __FILE__)
-ensure
- $VERBOSE = old
-end
-
require 'active_support/core_ext/kernel/reporting'
silence_warnings do
diff --git a/activesupport/test/autoloading_fixtures/raises_arbitrary_exception.rb b/activesupport/test/autoloading_fixtures/raises_arbitrary_exception.rb
new file mode 100644
index 0000000000..404ae289c6
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/raises_arbitrary_exception.rb
@@ -0,0 +1,4 @@
+RaisesArbitraryException = 1
+A::B # Autoloading recursion, also expected to be watched and discarded.
+
+raise Exception, 'arbitray exception message'
diff --git a/activesupport/test/autoloading_fixtures/throws.rb b/activesupport/test/autoloading_fixtures/throws.rb
new file mode 100644
index 0000000000..5e47b9699b
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/throws.rb
@@ -0,0 +1,4 @@
+Throws = 1
+A::B # Autoloading recursion, expected to be discarded.
+
+throw :t
diff --git a/activesupport/test/core_ext/marshal_test.rb b/activesupport/test/core_ext/marshal_test.rb
index 5427837d19..07c0c0d8cb 100644
--- a/activesupport/test/core_ext/marshal_test.rb
+++ b/activesupport/test/core_ext/marshal_test.rb
@@ -64,6 +64,17 @@ class MarshalTest < ActiveSupport::TestCase
end
end
+ test "when one constant resolves to another" do
+ class Parent; C = Class.new; end
+ class Child < Parent; C = Class.new; end
+
+ dump = Marshal.dump(Child::C.new)
+
+ Child.send(:remove_const, :C)
+
+ assert_raise(ArgumentError) { Marshal.load(dump) }
+ end
+
test "that a real missing class is causing an exception" do
dumped = nil
with_autoloading_fixtures do
diff --git a/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb b/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb
index 65fadc5c20..a9fd878b80 100644
--- a/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb
+++ b/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb
@@ -106,4 +106,10 @@ class ModuleAttributeAccessorPerThreadTest < ActiveSupport::TestCase
end
assert_equal "invalid attribute name: 2valid_part", exception.message
end
+
+ def test_should_return_same_value_by_class_or_instance_accessor
+ @class.foo = 'fries'
+
+ assert_equal @class.foo, @object.foo
+ end
end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 0ed66f8c37..ae4aed0554 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -328,7 +328,13 @@ class ModuleTest < ActiveSupport::TestCase
end
def test_local_constants
- assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s)
+ ActiveSupport::Deprecation.silence do
+ assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s)
+ end
+ end
+
+ def test_local_constants_is_deprecated
+ assert_deprecated { Ab.local_constants.sort.map(&:to_s) }
end
end
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index b7a5747f1b..04e7b24d30 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -269,6 +269,28 @@ class DependenciesTest < ActiveSupport::TestCase
remove_constants(:ModuleFolder)
end
+ def test_raising_discards_autoloaded_constants
+ with_autoloading_fixtures do
+ assert_raises(Exception, 'arbitray exception message') { RaisesArbitraryException }
+ assert_not defined?(A)
+ assert_not defined?(RaisesArbitraryException)
+ end
+ ensure
+ remove_constants(:A, :RaisesArbitraryException)
+ end
+
+ def test_throwing_discards_autoloaded_constants
+ with_autoloading_fixtures do
+ catch :t do
+ Throws
+ end
+ assert_not defined?(A)
+ assert_not defined?(Throws)
+ end
+ ensure
+ remove_constants(:A, :Throws)
+ end
+
def test_doesnt_break_normal_require
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
original_path = $:.dup
diff --git a/activesupport/test/executor_test.rb b/activesupport/test/executor_test.rb
new file mode 100644
index 0000000000..6db6db4fa8
--- /dev/null
+++ b/activesupport/test/executor_test.rb
@@ -0,0 +1,76 @@
+require 'abstract_unit'
+
+class ExecutorTest < ActiveSupport::TestCase
+ def test_wrap_invokes_callbacks
+ called = []
+ executor.to_run { called << :run }
+ executor.to_complete { called << :complete }
+
+ executor.wrap do
+ called << :body
+ end
+
+ assert_equal [:run, :body, :complete], called
+ end
+
+ def test_callbacks_share_state
+ result = false
+ executor.to_run { @foo = true }
+ executor.to_complete { result = @foo }
+
+ executor.wrap { }
+
+ assert result
+ end
+
+ def test_separated_calls_invoke_callbacks
+ called = []
+ executor.to_run { called << :run }
+ executor.to_complete { called << :complete }
+
+ state = executor.run!
+ called << :body
+ state.complete!
+
+ assert_equal [:run, :body, :complete], called
+ end
+
+ def test_avoids_double_wrapping
+ called = []
+ executor.to_run { called << :run }
+ executor.to_complete { called << :complete }
+
+ executor.wrap do
+ called << :early
+ executor.wrap do
+ called << :body
+ end
+ called << :late
+ end
+
+ assert_equal [:run, :early, :body, :late, :complete], called
+ end
+
+ def test_separate_classes_can_wrap
+ other_executor = Class.new(ActiveSupport::Executor)
+
+ called = []
+ executor.to_run { called << :run }
+ executor.to_complete { called << :complete }
+ other_executor.to_run { called << :other_run }
+ other_executor.to_complete { called << :other_complete }
+
+ executor.wrap do
+ other_executor.wrap do
+ called << :body
+ end
+ end
+
+ assert_equal [:run, :other_run, :body, :other_complete, :complete], called
+ end
+
+ private
+ def executor
+ @executor ||= Class.new(ActiveSupport::Executor)
+ end
+end
diff --git a/activesupport/test/reloader_test.rb b/activesupport/test/reloader_test.rb
new file mode 100644
index 0000000000..958cb49993
--- /dev/null
+++ b/activesupport/test/reloader_test.rb
@@ -0,0 +1,85 @@
+require 'abstract_unit'
+
+class ReloaderTest < ActiveSupport::TestCase
+ def test_prepare_callback
+ prepared = false
+ reloader.to_prepare { prepared = true }
+
+ assert !prepared
+ reloader.prepare!
+ assert prepared
+
+ prepared = false
+ reloader.wrap do
+ assert prepared
+ prepared = false
+ end
+ assert !prepared
+ end
+
+ def test_only_run_when_check_passes
+ r = new_reloader { true }
+ invoked = false
+ r.to_run { invoked = true }
+ r.wrap { }
+ assert invoked
+
+ r = new_reloader { false }
+ invoked = false
+ r.to_run { invoked = true }
+ r.wrap { }
+ assert !invoked
+ end
+
+ def test_full_reload_sequence
+ called = []
+ reloader.to_prepare { called << :prepare }
+ reloader.to_run { called << :reloader_run }
+ reloader.to_complete { called << :reloader_complete }
+ reloader.executor.to_run { called << :executor_run }
+ reloader.executor.to_complete { called << :executor_complete }
+
+ reloader.wrap { }
+ assert_equal [:executor_run, :reloader_run, :prepare, :reloader_complete, :executor_complete], called
+
+ called = []
+ reloader.reload!
+ assert_equal [:executor_run, :reloader_run, :prepare, :reloader_complete, :executor_complete, :prepare], called
+
+ reloader.check = lambda { false }
+
+ called = []
+ reloader.wrap { }
+ assert_equal [:executor_run, :executor_complete], called
+
+ called = []
+ reloader.reload!
+ assert_equal [:executor_run, :reloader_run, :prepare, :reloader_complete, :executor_complete, :prepare], called
+ end
+
+ def test_class_unload_block
+ called = []
+ reloader.before_class_unload { called << :before_unload }
+ reloader.after_class_unload { called << :after_unload }
+ reloader.to_run do
+ class_unload! do
+ called << :unload
+ end
+ end
+ reloader.wrap { called << :body }
+
+ assert_equal [:before_unload, :unload, :after_unload, :body], called
+ end
+
+ private
+ def new_reloader(&check)
+ Class.new(ActiveSupport::Reloader).tap do |r|
+ r.check = check
+ r.executor = Class.new(ActiveSupport::Executor)
+ end
+ end
+
+ def reloader
+ @reloader ||= new_reloader { true }
+ end
+end