File Manager
Viewing File: support.rb
module RSpec
module Support
# @api private
#
# Defines a helper method that is optimized to require files from the
# named lib. The passed block MUST be `{ |f| require_relative f }`
# because for `require_relative` to work properly from within the named
# lib the line of code must be IN that lib.
#
# `require_relative` is preferred when available because it is always O(1),
# regardless of the number of dirs in $LOAD_PATH. `require`, on the other
# hand, does a linear O(N) search over the dirs in the $LOAD_PATH until
# it can resolve the file relative to one of the dirs.
def self.define_optimized_require_for_rspec(lib, &require_relative)
name = "require_rspec_#{lib}"
if Kernel.respond_to?(:require_relative)
(class << self; self; end).__send__(:define_method, name) do |f|
require_relative.call("#{lib}/#{f}")
end
else
(class << self; self; end).__send__(:define_method, name) do |f|
require "rspec/#{lib}/#{f}"
end
end
end
define_optimized_require_for_rspec(:support) { |f| require_relative(f) }
require_rspec_support "version"
require_rspec_support "ruby_features"
# @api private
KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method)
# @api private
#
# Used internally to get a method handle for a particular object
# and method name.
#
# Includes handling for a few special cases:
#
# - Objects that redefine #method (e.g. an HTTPRequest struct)
# - BasicObject subclasses that mixin a Kernel dup (e.g. SimpleDelegator)
# - Objects that undefine method and delegate everything to another
# object (e.g. Mongoid association objects)
if RubyFeatures.supports_rebinding_module_methods?
def self.method_handle_for(object, method_name)
KERNEL_METHOD_METHOD.bind(object).call(method_name)
rescue NameError => original
begin
handle = object.method(method_name)
raise original unless handle.is_a? Method
handle
rescue Support::AllExceptionsExceptOnesWeMustNotRescue
raise original
end
end
else
def self.method_handle_for(object, method_name)
if ::Kernel === object
KERNEL_METHOD_METHOD.bind(object).call(method_name)
else
object.method(method_name)
end
rescue NameError => original
begin
handle = object.method(method_name)
raise original unless handle.is_a? Method
handle
rescue Support::AllExceptionsExceptOnesWeMustNotRescue
raise original
end
end
end
# A single thread local variable so we don't excessively pollute that namespace.
def self.thread_local_data
Thread.current[:__rspec] ||= {}
end
# @api private
def self.failure_notifier=(callable)
thread_local_data[:failure_notifier] = callable
end
# @private
DEFAULT_FAILURE_NOTIFIER = lambda { |failure, _opts| raise failure }
# @api private
def self.failure_notifier
thread_local_data[:failure_notifier] || DEFAULT_FAILURE_NOTIFIER
end
# @api private
def self.notify_failure(failure, options={})
failure_notifier.call(failure, options)
end
# @api private
def self.with_failure_notifier(callable)
orig_notifier = failure_notifier
self.failure_notifier = callable
yield
ensure
self.failure_notifier = orig_notifier
end
class << self
# @api private
attr_writer :warning_notifier
end
# @private
DEFAULT_WARNING_NOTIFIER = lambda { |warning| ::Kernel.warn warning }
# @api private
def self.warning_notifier
@warning_notifier ||= DEFAULT_WARNING_NOTIFIER
end
# @private
module AllExceptionsExceptOnesWeMustNotRescue
# These exceptions are dangerous to rescue as rescuing them
# would interfere with things we should not interfere with.
AVOID_RESCUING = [NoMemoryError, SignalException, Interrupt, SystemExit]
def self.===(exception)
AVOID_RESCUING.none? { |ar| ar === exception }
end
end
# The Differ is only needed when a a spec fails with a diffable failure.
# In the more common case of all specs passing or the only failures being
# non-diffable, we can avoid the extra cost of loading the differ, diff-lcs,
# pp, etc by avoiding an unnecessary require. Instead, autoload will take
# care of loading the differ on first use.
autoload :Differ, "rspec/support/differ"
end
end