Remove need for nillable argument to cast macro

The `class` method cannot be stubbed as this causes the cast (via `as`) to fail at compile-time.
This commit is contained in:
Michael Miller 2022-04-16 16:33:37 -06:00
parent 23d2c014b4
commit 0758c19a86
No known key found for this signature in database
GPG key ID: 32B47AE8F388A1FF
3 changed files with 18 additions and 25 deletions

View file

@ -2,6 +2,6 @@ module Spectator
module DSL module DSL
# Keywords that cannot be used in specs using the DSL. # Keywords that cannot be used in specs using the DSL.
# These are either problematic or reserved for internal use. # These are either problematic or reserved for internal use.
RESERVED_KEYWORDS = %i[initialize finalize] RESERVED_KEYWORDS = %i[initialize finalize class]
end end
end end

View file

@ -54,7 +54,7 @@ module Spectator
# Cast the stub or return value to the expected type. # Cast the stub or return value to the expected type.
# This is necessary to match the expected return type of the original message. # This is necessary to match the expected return type of the original message.
\{% if Messages.keys.includes?({{call.name.symbolize}}) %} \{% if Messages.keys.includes?({{call.name.symbolize}}) %}
_spectator_cast_stub_value(%stub, %call, \{{Messages[{{call.name.symbolize}}.id]}}, \{{Messages[{{call.name.symbolize}}.id].resolve >= Nil}}) _spectator_cast_stub_value(%stub, %call, \{{Messages[{{call.name.symbolize}}.id]}})
\{% else %} \{% else %}
# A method that was not defined during initialization was stubbed. # A method that was not defined during initialization was stubbed.
# Even though all stubs will have a #call method, the compiler doesn't seem to agree. # Even though all stubs will have a #call method, the compiler doesn't seem to agree.

View file

@ -160,10 +160,10 @@ module Spectator
# This is necessary to match the expected return type of the original method. # This is necessary to match the expected return type of the original method.
{% if method.return_type %} {% if method.return_type %}
# Return type restriction takes priority since it can be a superset of the original implementation. # Return type restriction takes priority since it can be a superset of the original implementation.
_spectator_cast_stub_value(%stub, %call, {{method.return_type}}, {{method.return_type.resolve >= Nil}}) _spectator_cast_stub_value(%stub, %call, {{method.return_type}})
{% elsif !method.abstract? %} {% elsif !method.abstract? %}
# The method isn't abstract, infer the type it returns without calling it. # The method isn't abstract, infer the type it returns without calling it.
_spectator_cast_stub_value(%stub, %call, typeof({{original}}), {{method.return_type && method.return_type.resolve >= Nil || false}}) _spectator_cast_stub_value(%stub, %call, typeof({{original}}))
{% else %} {% else %}
# Stubbed method is abstract and there's no return type annotation. # Stubbed method is abstract and there's no return type annotation.
# The value of the stub could be returned as-is. # The value of the stub could be returned as-is.
@ -246,7 +246,7 @@ module Spectator
if %stub = _spectator_find_stub(%call) if %stub = _spectator_find_stub(%call)
{% if method.return_type %} {% if method.return_type %}
# Return type was provided with a restriction. # Return type was provided with a restriction.
_spectator_cast_stub_value(%stub, %call, {{method.return_type}}, {{method.return_type.resolve >= Nil}}) _spectator_cast_stub_value(%stub, %call, {{method.return_type}})
{% else %} {% else %}
# Stubbed method is abstract and there's no return type annotation. # Stubbed method is abstract and there's no return type annotation.
# The value of the stub could be returned as-is. # The value of the stub could be returned as-is.
@ -273,8 +273,7 @@ module Spectator
# *stub* is the variable holding the stub. # *stub* is the variable holding the stub.
# *call* is the variable holding the captured method call. # *call* is the variable holding the captured method call.
# *type* is the expected type to cast the value to. # *type* is the expected type to cast the value to.
# *nillable* is true or false depending on whether *type* can be nil. private macro _spectator_cast_stub_value(stub, call, type)
private macro _spectator_cast_stub_value(stub, call, type, nillable)
# Attempt to cast the stub to the method's return type. # Attempt to cast the stub to the method's return type.
# If successful, return the value of the stub. # If successful, return the value of the stub.
# This is a common usage where the return type is simple and matches the stub type exactly. # This is a common usage where the return type is simple and matches the stub type exactly.
@ -283,7 +282,7 @@ module Spectator
else else
# The stub couldn't be easily cast to match the return type. # The stub couldn't be easily cast to match the return type.
# Even though all stubs will have a #call method, the compiler doesn't seem to agree. # Even though all stubs will have a `#call` method, the compiler doesn't seem to agree.
# Assert that it will (this should never fail). # Assert that it will (this should never fail).
raise TypeCastError.new("Stub has no value") unless {{stub}}.responds_to?(:call) raise TypeCastError.new("Stub has no value") unless {{stub}}.responds_to?(:call)
@ -294,23 +293,17 @@ module Spectator
# Attempt to cast the value to the method's return type. # Attempt to cast the value to the method's return type.
# If successful, which it will be in most cases, return it. # If successful, which it will be in most cases, return it.
# The caller will receive a properly typed value without unions or other side-effects. # The caller will receive a properly typed value without unions or other side-effects.
if %cast = %value.as?({{type}}) begin
%cast %value.as({{type}})
else rescue TypeCastError
# Now we're down to edge cases. # The stubbed value was something else entirely and cannot be cast to the return type.
{% if nillable %} # There's something weird going on (compiler bug?) that sometimes causes this class lookup to fail.
# The return type might have "cast" correctly, but the value is nil and the return type is nillable. %type = begin
# Just return nil in this case. %value.class.to_s
nil rescue
{% else %} "<Unknown>"
# The stubbed value was something else entirely and cannot be cast to the return type. end
%type = begin raise TypeCastError.new("#{_spectator_stubbed_name} received message #{ {{call}} } and is attempting to return a `#{%type}`, but returned type must be `#{ {{type}} }`.")
%value.class.to_s
rescue
"<Unknown>"
end
raise TypeCastError.new("#{_spectator_stubbed_name} received message #{ {{call}} } and is attempting to return a `#{%type}`, but returned type must be `#{ {{type}} }`.")
{% end %}
end end
end end
end end