This sample shows a proof of concept on how to use advanced Sorbet generics for activities, workflows, signals, queries, and updates. The Temporal Ruby SDK does not have Sorbet signatures natively, so this is just a proof of concept on how Sorbet users could consume the library if they have advanced needs.
tapioca generation to
generate the RBI files for Temporal Ruby SDK. This sample is only here to demonstrate advanced generic use cases.
See the "Issues" section for details on issues encountered during development.
To run, first see README.md for prerequisites. Then, in this directory:
bundle install
To check types, run:
bundle exec srb tc
Note how you can change a parameter type when calling activity, workflow, signal, query, or update and type checking will fail if it doesn't match expectation.
To actually run the code, from a terminal, start the worker:
bundle exec ruby worker.rb
Then, from another terminal, run the workflow:
bundle exec ruby starter.rb
There are a few issues with Sorbet and advanced use that had to be worked around. See each section below.
The Temporal Ruby SDK does not use Sorbet itself and does not take a sorbet-runtime dependency or have inline
signatures. Therefore, Temporal must be treated like any other non-Sorbet Ruby library. To generate the signatures
herein, we:
- Ran
bundle exec tapioca init - Ran
bundle exec tapioca require - Added
require 'google/protobuf'totapioca/require.rb - Ran
bundle exec tapioca gem temporalio google-protobuf
Unfortunately, due to the fact that the SDK supports splatted positional parameters (e.g. for a workflow) where a user may want a well-typed limited set of parameters, we have to alter the generated code. As documented at https://srb.help/4010:
In cases like these, usually the solution is to remove the
foodefinition fromautogenerated/some_gem.rbi
This means we had to hand-mutate the tapioca-generated RBI files, specifically we had to comment out all methods we
wanted to refine parameter arity on. Look for "NOTE: Manually removed" comments in
sorbet/rbi/gems/temporalio@0.3.0.rbi.
For generic reasons we had to define the base classes for workflows and activities as having a single input (which
Temporal encourages anyways). However, it is also normal to have no input, but Sorbet disallows overrides to change
the parameter count, so a _ placeholder param with a default has to be used to ignore it.
When a signal, query, or update is defined on a method, the Ruby SDK also makes an associated class method for the "definition" of that handler for use by clients. So when this is present:
workflow_signal
sig { params(some_value: String).void }
def my_signal(some_value)
@some_value = some_value
endThat creates a my_signal class method on the workflow class dynamically. However, due to an
issue in Sorbet defining singleton methods in instance
on_method_added, this needs to change to:
workflow_signal
T::Sig::WithoutRuntime.sig { params(some_value: String).void }
def my_signal(some_value)
@some_value = some_value
endThis disables runtime behavior to prevent the bug.
In Sorbet a generic class is referenced using brackets. For example, to define a class method stub on a workflow to represent the above-section-mentioned class methods created, one might have:
sig { returns(Temporalio::Workflow::Definition::Signal[String]) }
def self.my_signal = T.unsafe(nil)But this will fail at runtime because the Temporal Ruby SDK doesn't define []. So you have to change this to:
T::Sig::WithoutRuntime.sig { returns(Temporalio::Workflow::Definition::Signal[String]) }
def self.my_signal = T.unsafe(nil)This will avoid processing the invalid body.