module RSpec::Sorbet::Doubles
def allow_doubles!
def allow_doubles! reset! if configured self.existing_inline_type_error_handler = T::Configuration.instance_variable_get( :@inline_type_error_handler, ) T::Configuration.inline_type_error_handler = proc do |error| inline_type_error_handler(error) end self.existing_call_validation_error_handler = T::Configuration.instance_variable_get( :@call_validation_error_handler, ) T::Configuration.call_validation_error_handler = proc do |signature, opts| call_validation_error_handler(signature, opts) end self.configured = true end
def call_validation_error_handler(signature, opts)
def call_validation_error_handler(signature, opts) should_raise = true message = opts.fetch(:pretty_message, opts.fetch(:message, "")) if message.match?(VERIFYING_DOUBLE_OR_DOUBLE) typing = opts[:type] value = opts[:value].is_a?(Array) ? opts[:value].first : opts[:value] target = value.instance_variable_get(:@doubled_module)&.target return if target.nil? case typing when T::Types::TypedArray, T::Types::TypedEnumerable typing = typing.type end case typing when T::Types::ClassOf should_raise = !(target <= typing.type) when T::Types::Simple should_raise = !target.ancestors.include?(typing.raw_type) when T::Types::Union valid = typing.types.any? do |type| next false unless type.respond_to?(:raw_type) target.ancestors.include?(type.raw_type) end should_raise = !valid else should_raise = !target.ancestors.include?(typing) end end handle_call_validation_error(signature, opts) if should_raise end
def double_message_with_ellipsis?(message)
def double_message_with_ellipsis?(message) message.include?("...") && message.match?(VERIFYING_DOUBLE_OR_DOUBLE) end
def handle_call_validation_error(signature, opts)
def handle_call_validation_error(signature, opts) raise TypeError, opts[:pretty_message] unless @existing_call_validation_error_handler @existing_call_validation_error_handler.call(signature, opts) end
def inline_type_error_handler(error)
def inline_type_error_handler(error) case error when TypeError message = error.message return if unable_to_check_type_for_message?(message) raise error unless (match = message.match(INLINE_DOUBLE_REGEX)) t_method = match[:t_method] expected_types = T.must(match[:expected_types]).split(",").map do |expected_type| Object.const_get(expected_type.strip) end double_type = match[:double_type] return if double_type.nil? doubled_type = Object.const_get(T.must(match[:doubled_type])) if double_type == "Class" raise error if t_method != "class_of" valid = expected_types.any? do |expected_type| doubled_type <= expected_type end raise error unless valid end valid = expected_types.any? do |expected_type| doubled_type.ancestors.include?(expected_type) end raise error unless valid else raise error end existing_inline_type_error_handler&.call(error) end
def reset!(clear_existing: false)
def reset!(clear_existing: false) if clear_existing self.existing_inline_type_error_handler = nil self.existing_call_validation_error_handler = nil end T::Configuration.instance_variable_set( :@inline_type_error_handler, existing_inline_type_error_handler, ) T::Configuration.instance_variable_set( :@call_validation_error_handler, existing_call_validation_error_handler, ) self.configured = false end
def typed_array_message?(message)
def typed_array_message?(message) message.match?(TYPED_ARRAY_MESSAGE) end
def unable_to_check_type_for_message?(message)
def unable_to_check_type_for_message?(message) double_message_with_ellipsis?(message) || typed_array_message?(message) end