id | title |
---|---|
tconfiguration |
Runtime Configuration |
Recall from Enabling runtime checks that sorbet-runtime
raises
exceptions for type errors that happen at runtime. This is to ensure that even
code that is not covered by types statically can still benefit from type
information, and that authors don't have to defend against their typed methods
being called incorrectly.
The behavior of the sorbet-runtime
package can be configured by registering
callbacks before a program is run. While it's possible to use these callbacks to
silence all runtime exceptions raised by sorbet-runtime
, we strongly
recommend not doing so. Runtime exceptions have proven to be invaluable in
gaining confidence in the correctness of type signatures as we rolled out types
to Stripe's Ruby codebase.
Note: Think carefully before disabling runtime checks!
These docs are somewhat low-level. For a higher-level description of how to change Sorbet's runtime, see Enabling Runtime Checks.
There are four kinds of inline type assertions:
T.let(expr, Type)
T.cast(expr, Type)
T.must(expr)
T.assert_type!(expr, Type)
To customize the behavior when one of these assertions fails:
T::Configuration.inline_type_error_handler = lambda do |error|
puts error.message
end
The default error handler is to raise (a TypeError
).
Note that setting this callback will not handle type errors raised when a method with a signature is called incorrectly. For those, see the next section.
To customize the behavior when a method with a sig is called and the argument types or return types don't match the actual value present at runtime, use this:
T::Configuration.call_validation_error_handler = lambda do |signature, opts|
puts opts[:message]
end
The default error handler is to raise an error.
The example handler above does the same thing for every method. One thing which
can be useful is to pass custom metadata to the call_validation
handler, which
can be done with .on_failure
:
T::Configuration.call_validation_error_handler = lambda do |signature, opts|
if signature.on_failure
puts "Metadata: #{signature.on_failure}"
end
raise TypeError.new(opts[:pretty_message])
end
sig {params(x: Integer).void.on_failure(:hello, :world)}
def foo(x); end
When the call_validation_error_handler
is called this time, it will be passed
all the args that were given to on_failure
for the specific sig that failed.
We write sigs using valid Ruby syntax. The body of the proc passed to
a sig is executed (lazily, on first method call) to compute an in-memory data
structure representing that sig's types. The execution of this proc can be
invalid (for example, if returns
or void
is never called).
The default behavior when building a sig is invalid is to raise an
ArgumentError
. To customize this behavior, use this:
T::Configuration.sig_builder_error_handler = lambda do |error, location|
puts error.message
end
Method signatures that build correctly can still be invalid. For example, a sig
marked override
must actually override a method. Same for abstract
methods.
When overriding a parent sig, the variance must match on the input and output
types. If a sig that built correctly is invalid in anyway, this error handler
will be called:
T::Configuration.sig_validation_error_handler = lambda do |error, opts|
puts error.message
end