diff options
author | Nicholas Seckar <nseckar@gmail.com> | 2006-08-08 21:21:04 +0000 |
---|---|---|
committer | Nicholas Seckar <nseckar@gmail.com> | 2006-08-08 21:21:04 +0000 |
commit | 74165eb6acaca98d4da13409c4e5b5ecc9d260f7 (patch) | |
tree | 024158d20563f34cca52a00b42aaa6c922777559 /activesupport | |
parent | 94a1309194fa5962e33d395ede14e94b237c54f5 (diff) | |
download | rails-74165eb6acaca98d4da13409c4e5b5ecc9d260f7.tar.gz rails-74165eb6acaca98d4da13409c4e5b5ecc9d260f7.tar.bz2 rails-74165eb6acaca98d4da13409c4e5b5ecc9d260f7.zip |
New dependencies implementation
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4728 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activesupport')
-rw-r--r-- | activesupport/CHANGELOG | 5 | ||||
-rw-r--r-- | activesupport/lib/active_support/dependencies.rb | 194 | ||||
-rw-r--r-- | activesupport/lib/active_support/deprecation.rb | 24 | ||||
-rw-r--r-- | activesupport/lib/active_support/reloadable.rb | 41 | ||||
-rw-r--r-- | activesupport/test/autoloading_fixtures/conflict.rb | 1 | ||||
-rw-r--r-- | activesupport/test/caching_tools_test.rb | 1 | ||||
-rw-r--r-- | activesupport/test/dependencies/conflict.rb | 1 | ||||
-rw-r--r-- | activesupport/test/dependencies_test.rb | 134 | ||||
-rw-r--r-- | activesupport/test/deprecation_test.rb | 11 | ||||
-rw-r--r-- | activesupport/test/reloadable_test.rb | 32 |
10 files changed, 381 insertions, 63 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 3ddfc19643..d8cc98cd97 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,9 @@ *SVN* +<<<<<<< .mine +* Replace Reloadable with improvements to the Dependencies mechanism. [Nicholas Seckar] + +======= * DateTime#to_time gives hour/minute/second resolution. #5747 [jon.evans@pobox.com] * attr_internal to support namespacing and deprecation. Like attr_* except backed by internally-named instance variable. Set attr_internal_naming_format to change the format from the default '@_%s'. [Jeremy Kemper] @@ -8,6 +12,7 @@ self.attr_internal_naming_format = '@%s__rofl' attr_internal :foo +>>>>>>> .r4727 * Raise fully qualified names upon name errors. #5533 [lars@pinds.com, Nicholas Seckar] * Add extention to obtain the missing constant from NameError instances. [Nicholas Seckar] diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index a58989173e..e845408137 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -22,12 +22,20 @@ module Dependencies #:nodoc: mattr_accessor :mechanism self.mechanism = :load + # The set of directories from which we may autoload files + mattr_accessor :autoload_paths + self.autoload_paths = [] + + mattr_accessor :autoloaded_constants + self.autoloaded_constants = [] + def load? mechanism == :load end def depend_on(file_name, swallow_load_errors = false) - require_or_load(file_name) + path = search_for_autoload_file(file_name) + require_or_load(path || file_name) rescue LoadError raise unless swallow_load_errors end @@ -38,9 +46,10 @@ module Dependencies #:nodoc: def clear loaded.clear + remove_autoloaded_constants! end - def require_or_load(file_name) + def require_or_load(file_name, const_path = nil) file_name = $1 if file_name =~ /^(.*)\.rb$/ return if loaded.include?(file_name) @@ -52,10 +61,13 @@ module Dependencies #:nodoc: begin # Enable warnings iff this file has not been loaded before and # warnings_on_first_load is set. + load_args = ["#{file_name}.rb"] + load_args << const_path unless const_path.nil? + if !warnings_on_first_load or history.include?(file_name) - load "#{file_name}.rb" + load_file(*load_args) else - enable_warnings { load "#{file_name}.rb" } + enable_warnings { load_file(*load_args) } end rescue loaded.delete file_name @@ -69,9 +81,127 @@ module Dependencies #:nodoc: history << file_name end - # Return the a constant path for the provided parent and constant name - def constant_path_for(mod, name) - ([Object, Kernel].include? mod) ? name.to_s : "#{mod}::#{name}" + # Is the provided constant path defined? + def qualified_const_defined?(path) + raise NameError, "#{path.inspect} is not a valid constant name!" unless + /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ path + Object.module_eval("defined?(#{path})", __FILE__, __LINE__) + end + + # Given +path+ return an array of constant paths which would cause Dependencies + # to attempt to load +path+. + def autoloadable_constants_for_path(path) + path = $1 if path =~ /\A(.*)\.rb\Z/ + expanded_path = File.expand_path(path) + autoload_paths.collect do |root| + expanded_root = File.expand_path root + next unless expanded_path.starts_with? expanded_root + + nesting = expanded_path[(expanded_root.size)..-1] + nesting = nesting[1..-1] if nesting && nesting[0] == ?/ + next if nesting.blank? + + nesting.camelize + end.compact.uniq + end + + # Search for a file in the autoload_paths matching the provided suffix. + def search_for_autoload_file(path_suffix) + path_suffix = path_suffix + '.rb' unless path_suffix.ends_with? '.rb' + autoload_paths.each do |root| + path = File.join(root, path_suffix) + return path if File.file? path + end + nil # Gee, I sure wish we had first_match ;-) + end + + # Does the provided path_suffix correspond to an autoloadable module? + def autoloadable_module?(path_suffix) + autoload_paths.any? do |autoload_path| + File.directory? File.join(autoload_path, path_suffix) + end + end + + # Load the file at the provided path. +const_paths+ is a set of qualified + # constant names. When loading the file, Dependencies will watch for the + # addition of these constants. Each that is defined will be marked as + # autoloaded, and will be removed when Dependencies.clear is next called. + # + # If the second parameter is left off, then Dependencies will construct a set + # of names that the file at +path+ may define. See + # +autoloadable_constants_for_path+ for more details. + def load_file(path, const_paths = autoloadable_constants_for_path(path)) + const_paths = [const_paths].compact unless const_paths.is_a? Array + undefined_before = const_paths.reject(&method(:qualified_const_defined?)) + + load path + + autoloaded_constants.concat const_paths.select(&method(:qualified_const_defined?)) + autoloaded_constants.uniq! + end + + # Return the constant path for the provided parent and constant name. + def qualified_name_for(mod, name) + mod_name = to_constant_name mod + (%w(Object Kernel).include? mod_name) ? name.to_s : "#{mod_name}::#{name}" + end + + # Load the constant named +const_name+ which is missing from +from_mod+. If + # it is not possible to laod the constant into from_mod, try its parent module + # using const_missing. + def load_missing_constant(from_mod, const_name) + qualified_name = qualified_name_for from_mod, const_name + path_suffix = qualified_name.underscore + name_error = NameError.new("uninitialized constant #{qualified_name}") + + file_path = search_for_autoload_file(path_suffix) + if file_path # We found a matching file to load + require_or_load file_path, qualified_name + raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless from_mod.const_defined?(const_name) + return from_mod.const_get(const_name) + elsif autoloadable_module? path_suffix # Create modules for directories + mod = Module.new + from_mod.const_set const_name, mod + autoloaded_constants << qualified_name + return mod + elsif (parent = from_mod.parent) && parent != from_mod && + ! from_mod.parents.any? { |p| p.const_defined?(const_name) } + # If our parents do not have a constant named +const_name+ then we are free + # to attempt to load upwards. If they do have such a constant, then this + # const_missing must be due to from_mod::const_name, which should not + # return constants from from_mod's parents. + begin + return parent.const_missing(const_name) + rescue NameError => e + raise unless e.missing_name? qualified_name_for(parent, const_name) + raise name_error + end + else + raise name_error + end + end + + # Remove the constants that have been autoloaded. + def remove_autoloaded_constants! + until autoloaded_constants.empty? + const = autoloaded_constants.shift + next unless qualified_const_defined? const + names = const.split('::') + if names.size == 1 || names.first.empty? # It's under Object + parent = Object + else + parent = (names[0..-2] * '::').constantize + end + parent.send :remove_const, names.last + true + end + end + + # Determine if the given constant has been automatically loaded. + def autoloaded?(desc) + name = to_constant_name desc + return false unless qualified_const_defined? name + return autoloaded_constants.include?(name) end class LoadingModule @@ -84,6 +214,20 @@ module Dependencies #:nodoc: end end end + +protected + + # Convert the provided const desc to a qualified constant name (as a string). + # A module, class, symbol, or string may be provided. + def to_constant_name(desc) + name = case desc + when String then desc.starts_with?('::') ? desc[2..-1] : desc + when Symbol then desc.to_s + when Module then desc.name + else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}" + end + end + end Object.send(:define_method, :require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load) @@ -97,37 +241,7 @@ class Module #:nodoc: # Use const_missing to autoload associations so we don't have to # require_association when using single-table inheritance. def const_missing(class_id) - file_name = class_id.to_s.demodulize.underscore - file_path = as_load_path.empty? ? file_name : "#{as_load_path}/#{file_name}" - begin - require_dependency(file_path) - brief_name = self == Object ? '' : "#{name}::" - raise NameError.new("uninitialized constant #{brief_name}#{class_id}") unless const_defined?(class_id) - return const_get(class_id) - rescue MissingSourceFile => e - # Re-raise the error if it does not concern the file we were trying to load. - raise unless e.is_missing? file_path - - # Look for a directory in the load path that we ought to load. - if $LOAD_PATH.any? { |base| File.directory? "#{base}/#{file_path}" } - mod = Module.new - const_set class_id, mod # Create the new module - return mod - end - - # Attempt to access the name from the parent, unless we don't have a valid - # parent, or the constant is already defined in the parent. If the latter - # is the case, then we are being queried via self::class_id, and we should - # avoid returning the constant from the parent if possible. - if parent && parent != self && ! parents.any? { |p| p.const_defined?(class_id) } - suppress(NameError) do - return parent.send(:const_missing, class_id) - end - end - - qualified_name = Dependencies.constant_path_for self, class_id - raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e) - end + Dependencies.load_missing_constant self, class_id end end @@ -140,9 +254,9 @@ class Class parent.send :const_missing, class_id rescue NameError => e # Make sure that the name we are missing is the one that caused the error - parent_qualified_name = Dependencies.constant_path_for parent, class_id + parent_qualified_name = Dependencies.qualified_name_for parent, class_id raise unless e.missing_name? parent_qualified_name - qualified_name = Dependencies.constant_path_for self, class_id + qualified_name = Dependencies.qualified_name_for self, class_id raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e) end end diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 5aff753616..d6c9317f06 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -49,24 +49,30 @@ module ActiveSupport module Assertions def assert_deprecated(match = nil, &block) - last = with_last_message_tracking_deprecation_behavior(&block) + last = collect_deprecations(&block).last assert last, "Expected a deprecation warning within the block but received none" - match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp) - assert_match match, last, "Deprecation warning didn't match #{match}: #{last}" + if match + match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp) + assert_match match, last, "Deprecation warning didn't match #{match}: #{last}" + end end def assert_not_deprecated(&block) - last = with_last_message_tracking_deprecation_behavior(&block) - assert_nil last, "Expected no deprecation warning within the block but received one: #{last}" + deprecations = collect_deprecations(&block) + assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}" end private - def with_last_message_tracking_deprecation_behavior + + def collect_deprecations old_behavior = ActiveSupport::Deprecation.behavior - last_message = nil - ActiveSupport::Deprecation.behavior = Proc.new { |message| last_message = message; old_behavior.call(message) if old_behavior } + deprecations = [] + ActiveSupport::Deprecation.behavior = Proc.new do |message| + deprecations << message + old_behavior.call(message) if old_behavior + end yield - last_message + return deprecations ensure ActiveSupport::Deprecation.behavior = old_behavior end diff --git a/activesupport/lib/active_support/reloadable.rb b/activesupport/lib/active_support/reloadable.rb index 3fd13e3d0f..3f57723e15 100644 --- a/activesupport/lib/active_support/reloadable.rb +++ b/activesupport/lib/active_support/reloadable.rb @@ -1,13 +1,25 @@ -# Classes that include this module will automatically be reloaded -# by the Rails dispatcher when Dependencies.mechanism = :load. +require 'active_support/deprecation' + +# A deprecated mechanism to mark a class reloadable. +# +# Deprecated as of Rails 1.2. +# All autoloaded objects are now unloaded. module Reloadable class << self + def included(base) #nodoc: + unless base.ancestors.include?(Reloadable::Subclasses) # Avoid double warning + ActiveSupport::Deprecation.warn "Reloadable has been deprecated and has no effect.", caller + end + raise TypeError, "Only Classes can be Reloadable!" unless base.is_a? Class unless base.respond_to?(:reloadable?) class << base - define_method(:reloadable?) { true } + define_method(:reloadable?) do + ActiveSupport::Deprecation.warn "Reloadable has been deprecated and reloadable? has no effect", caller + true + end end end end @@ -15,16 +27,37 @@ module Reloadable def reloadable_classes included_in_classes.select { |klass| klass.reloadable? } end + # Commented out so dispatcher doesn't warn. Should we just disable Reloadable? + # deprecate :reloadable_classes end # Captures the common pattern where a base class should not be reloaded, # but its subclasses should be. + # + # Deprecated as of Rails 1.2. + # All autoloaded objects are now unloaded. module Subclasses def self.included(base) #nodoc: base.send :include, Reloadable + ActiveSupport::Deprecation.warn "Reloadable::Subclasses has been deprecated and has no effect.", caller (class << base; self; end).send(:define_method, :reloadable?) do - base != self + ActiveSupport::Deprecation.warn "Reloadable has been deprecated and reloadable? has no effect", caller + base != self end end end + + module Deprecated + + def self.included(base) + class << base + define_method(:reloadable?) do + ActiveSupport::Deprecation.warn "Reloadable has been deprecated and reloadable? has no effect", caller + true # This might not have the desired effect, as AR::B.reloadable? => true. + end + end + end + + end + end
\ No newline at end of file diff --git a/activesupport/test/autoloading_fixtures/conflict.rb b/activesupport/test/autoloading_fixtures/conflict.rb new file mode 100644 index 0000000000..4ac6201902 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/conflict.rb @@ -0,0 +1 @@ +Conflict = 2
\ No newline at end of file diff --git a/activesupport/test/caching_tools_test.rb b/activesupport/test/caching_tools_test.rb index c9890e4d13..e1cd4e433c 100644 --- a/activesupport/test/caching_tools_test.rb +++ b/activesupport/test/caching_tools_test.rb @@ -1,4 +1,5 @@ require File.dirname(__FILE__) + '/abstract_unit' +require File.join(File.dirname(File.dirname(__FILE__)), 'lib/active_support/caching_tools.rb') class HashCachingTests < Test::Unit::TestCase def cached(&proc) diff --git a/activesupport/test/dependencies/conflict.rb b/activesupport/test/dependencies/conflict.rb new file mode 100644 index 0000000000..e888b7b54c --- /dev/null +++ b/activesupport/test/dependencies/conflict.rb @@ -0,0 +1 @@ +Conflict = 1
\ No newline at end of file diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index f19542bdb0..bf9aaac8c1 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -1,19 +1,19 @@ require File.dirname(__FILE__) + '/abstract_unit' -#require 'dependencies' class DependenciesTest < Test::Unit::TestCase + def teardown Dependencies.clear end - def with_loading(from_dir = nil) - prior_path = $LOAD_PATH.clone - $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/#{from_dir}" if from_dir + def with_loading(*from) old_mechanism, Dependencies.mechanism = Dependencies.mechanism, :load + dir = File.dirname(__FILE__) + prior_autoload_paths = Dependencies.autoload_paths + Dependencies.autoload_paths = from.collect { |f| "#{dir}/#{f}" } yield ensure - $LOAD_PATH.clear - $LOAD_PATH.concat prior_path + Dependencies.autoload_paths = prior_autoload_paths Dependencies.mechanism = old_mechanism end @@ -159,13 +159,13 @@ class DependenciesTest < Test::Unit::TestCase def test_non_existing_const_raises_name_error_with_fully_qualified_name with_loading 'autoloading_fixtures' do begin - A::DoesNotExist + A::DoesNotExist.nil? flunk "No raise!!" rescue NameError => e assert_equal "uninitialized constant A::DoesNotExist", e.message end begin - A::B::DoesNotExist + A::B::DoesNotExist.nil? flunk "No raise!!" rescue NameError => e assert_equal "uninitialized constant A::B::DoesNotExist", e.message @@ -182,4 +182,122 @@ class DependenciesTest < Test::Unit::TestCase end end + def test_autoloadable_constants_for_path_should_handle_empty_autoloads + assert_equal [], Dependencies.autoloadable_constants_for_path('hello') + end + + def test_autoloadable_constants_for_path_should_handle_relative_paths + fake_root = 'dependencies' + relative_root = File.dirname(__FILE__) + '/dependencies' + ['', '/'].each do |suffix| + with_loading fake_root + suffix do + assert_equal ["A::B"], Dependencies.autoloadable_constants_for_path(relative_root + '/a/b') + end + end + end + + def test_autoloadable_constants_for_path_should_provide_all_results + fake_root = '/usr/apps/backpack' + with_loading fake_root, fake_root + '/lib' do + root = Dependencies.autoload_paths.first + assert_equal ["Lib::A::B", "A::B"], Dependencies.autoloadable_constants_for_path(root + '/lib/a/b') + end + end + + def test_autoloadable_constants_for_path_should_uniq_results + fake_root = '/usr/apps/backpack/lib' + with_loading fake_root, fake_root + '/' do + root = Dependencies.autoload_paths.first + assert_equal ["A::B"], Dependencies.autoloadable_constants_for_path(root + '/a/b') + end + end + + def test_qualified_const_defined + assert Dependencies.qualified_const_defined?("Object") + assert Dependencies.qualified_const_defined?("::Object") + assert Dependencies.qualified_const_defined?("::Object::Kernel") + assert Dependencies.qualified_const_defined?("::Object::Dependencies") + assert Dependencies.qualified_const_defined?("::Test::Unit::TestCase") + end + + def test_autoloaded? + with_loading 'autoloading_fixtures' do + assert ! Dependencies.autoloaded?("ModuleFolder") + assert ! Dependencies.autoloaded?("ModuleFolder::NestedClass") + + assert Dependencies.autoloaded?(ModuleFolder) + + assert Dependencies.autoloaded?("ModuleFolder") + assert ! Dependencies.autoloaded?("ModuleFolder::NestedClass") + + assert Dependencies.autoloaded?(ModuleFolder::NestedClass) + + assert Dependencies.autoloaded?("ModuleFolder") + assert Dependencies.autoloaded?("ModuleFolder::NestedClass") + + assert Dependencies.autoloaded?("::ModuleFolder") + assert Dependencies.autoloaded?(:ModuleFolder) + + Object.send :remove_const, :ModuleFolder + end + end + + def test_qualified_name_for + assert_equal "A", Dependencies.qualified_name_for(Object, :A) + assert_equal "A", Dependencies.qualified_name_for(:Object, :A) + assert_equal "A", Dependencies.qualified_name_for("Object", :A) + assert_equal "A", Dependencies.qualified_name_for("::Object", :A) + assert_equal "A", Dependencies.qualified_name_for("::Kernel", :A) + + assert_equal "Dependencies::A", Dependencies.qualified_name_for(:Dependencies, :A) + assert_equal "Dependencies::A", Dependencies.qualified_name_for(Dependencies, :A) + end + + def test_file_search + with_loading 'dependencies' do + root = Dependencies.autoload_paths.first + assert_equal nil, Dependencies.search_for_autoload_file('service_three') + assert_equal nil, Dependencies.search_for_autoload_file('service_three.rb') + assert_equal root + '/service_one.rb', Dependencies.search_for_autoload_file('service_one') + assert_equal root + '/service_one.rb', Dependencies.search_for_autoload_file('service_one.rb') + end + end + + def test_file_search_uses_first_in_autoload_path + with_loading 'dependencies', 'autoloading_fixtures' do + deps, autoload = Dependencies.autoload_paths + assert_match %r/dependencies/, deps + assert_match %r/autoloading_fixtures/, autoload + + assert_equal deps + '/conflict.rb', Dependencies.search_for_autoload_file('conflict') + end + with_loading 'autoloading_fixtures', 'dependencies' do + autoload, deps = Dependencies.autoload_paths + assert_match %r/dependencies/, deps + assert_match %r/autoloading_fixtures/, autoload + + assert_equal autoload + '/conflict.rb', Dependencies.search_for_autoload_file('conflict') + end + + end + + def test_custom_const_missing_should_work + Object.module_eval <<-end_eval + module ModuleWithCustomConstMissing + def self.const_missing(name) + const_set name, name.to_s.hash + end + + module A + end + end + end_eval + + with_loading 'autoloading_fixtures' do + assert_kind_of Integer, ::ModuleWithCustomConstMissing::B + assert_kind_of Module, ::ModuleWithCustomConstMissing::A + assert_kind_of String, ::ModuleWithCustomConstMissing::A::B + end + end + end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index 472fdecaaf..1a054335d7 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -39,7 +39,7 @@ class DeprecationTest < Test::Unit::TestCase @dtc.partially end end - + def test_undeprecated assert_not_deprecated do assert_equal 2, @dtc.not @@ -67,8 +67,15 @@ class DeprecationTest < Test::Unit::TestCase def test_deprecated_instance_variable_proxy assert_not_deprecated { @dtc.request.size } - + assert_deprecated('@request.size') { assert_equal @dtc.request.size, @dtc.old_request.size } assert_deprecated('@request.to_s') { assert_equal @dtc.request.to_s, @dtc.old_request.to_s } end + + def test_assert_deprecation_without_match + assert_deprecated do + @dtc.partially + end + end + end diff --git a/activesupport/test/reloadable_test.rb b/activesupport/test/reloadable_test.rb index c330394b29..2ab19db77e 100644 --- a/activesupport/test/reloadable_test.rb +++ b/activesupport/test/reloadable_test.rb @@ -78,4 +78,36 @@ class ReloadableTest < Test::Unit::TestCase assert ! results.include?(ReloadableTestSandbox.const_get(name)), "Expected #{name} NOT to be reloadable" end end + + def test_including_reloadable_should_warn + c = Class.new + assert_deprecated %r{Reloadable} do + c.send :include, Reloadable + end + + assert_deprecated { c.reloadable? } + end + + def test_include_subclasses_should_warn + c = Class.new + deps = collect_deprecations do + c.send :include, Reloadable::Subclasses + end + assert_equal 1, deps.size + assert_match %r{Reloadable::Subclasses}, deps.first + + assert_deprecated { c.reloadable? } + end + + def test_include_deprecated_should_not_warn + c = Class.new + deps = collect_deprecations do + c.send :include, Reloadable::Deprecated + end + assert_equal 0, deps.size + + assert c.respond_to?(:reloadable?) + assert_deprecated { c.reloadable? } + end + end |