Sidekiq::Sorbet
Add typed arguments to your Sidekiq Workers with automatic argument access.
Quick Example
# Worker Class
class AnalyzeAttachmentWorker
include Sidekiq::Sorbet
class Args < T::Struct
const :attachment_id, Integer
const :regenerate, T::Boolean, default: false
end
def run
# Direct access to typed arguments
attachment = Attachment.find(attachment_id)
return if attachment.analyzed? && !regenerate
attachment.analyze!
end
end
# Call Worker
AnalyzeAttachmentWorker.run_async(attachment_id: 1) # arguments are typed and validated
AnalyzeAttachmentWorker.run_sync(attachment_id: 1, regenerate: true) # execute synchronouslyFeatures
- ✅ Direct argument access - Access arguments directly as
attachment_idinstead ofargs.attachment_id - ✅ Type safety - Arguments are validated at enqueue time using Sorbet's T::Struct
- ✅ Optional Args - Workers can omit the Args class if they don't need arguments
- ✅ Backward compatible - The
argsaccessor still works:args.attachment_id - ✅ Clean API - Use
run_async/run_syncinstead ofperform_async/perform - ✅ Fail-fast validation - Errors caught before jobs are enqueued
- ✅ Comprehensive errors - Clear error messages for debugging
Installation
Install the gem and add to the application's Gemfile by executing:
bundle add sidekiq-sorbetIf bundler is not being used to manage dependencies, install the gem by executing:
gem install sidekiq-sorbetUsage
Basic Worker with Arguments
class ProcessUserWorker
include Sidekiq::Sorbet
class Args < T::Struct
const :user_id, Integer
const :send_email, T::Boolean, default: true
end
def run
user = User.find(user_id)
user.process!
UserMailer.processed(user).deliver_later if send_email
end
end
# Enqueue the job
ProcessUserWorker.run_async(user_id: 123)
ProcessUserWorker.run_async(user_id: 456, send_email: false)Worker Without Arguments
Args class is optional! Workers without arguments work perfectly:
class CleanupWorker
include Sidekiq::Sorbet
def run
# Perform cleanup tasks
clean_temp_files
vacuum_database
end
end
# No arguments needed
CleanupWorker.run_asyncComplex Types
class ReportWorker
include Sidekiq::Sorbet
class Args < T::Struct
const :user_ids, T::Array[Integer]
const :filters, T::Hash[String, T.untyped], default: {}
const :format, String
end
def run
users = User.where(id: user_ids)
report = ReportGenerator.new(users, filters)
report.export(format)
end
end
ReportWorker.run_async(
user_ids: [1, 2, 3],
filters: { "active" => true },
format: "pdf",
)Nested T::Structs
class NotificationWorker
include Sidekiq::Sorbet
class Recipient < T::Struct
const :name, String
const :email, String
end
class Args < T::Struct
const :recipient, Recipient
const :message, String
end
def run
# Access nested struct fields
email = NotificationMailer.compose(
to: recipient.email,
name: recipient.name,
body: message,
)
email.deliver
end
end
NotificationWorker.run_async(
recipient: NotificationWorker::Recipient.new(
name: "John Doe",
email: "john@example.com",
),
message: "Hello!",
)Backward Compatibility
The args accessor still works if you prefer the old style:
class LegacyWorker
include Sidekiq::Sorbet
class Args < T::Struct
const :value, Integer
end
def run
# Both styles work
direct = value # Direct access (recommended)
via_args = args.value # Via args accessor (still works)
end
endSynchronous Execution
Use run_sync for testing or immediate execution:
# In tests
result = ProcessUserWorker.run_sync(user_id: 123)
# In console for debugging
AnalyzeAttachmentWorker.run_sync(attachment_id: 456)Tapioca DSL Compiler
This gem includes a Tapioca DSL compiler that generates proper type signatures for your workers. This enables Sorbet to understand the dynamically generated methods like run_async, run_sync, and argument accessors.
Generating RBI Files
Run the Tapioca DSL compiler to generate type definitions:
bundle exec tapioca dsl SidekiqSorbetOr generate RBI files for all DSL compilers:
bundle exec tapioca dslGenerated Signatures
For a worker like this:
class MyWorker
include Sidekiq::Sorbet
class Args < T::Struct
const :user_id, Integer
const :notify, T::Boolean, default: false
end
def run
# ...
end
endThe compiler generates:
class MyWorker
sig { returns(Integer) }
def user_id; end
sig { returns(T::Boolean) }
def notify; end
sig { params(user_id: Integer, notify: T::Boolean).returns(String) }
def self.run_async(user_id:, notify: T.unsafe(nil)); end
sig { params(user_id: Integer, notify: T::Boolean).returns(T.untyped) }
def self.run_sync(user_id:, notify: T.unsafe(nil)); end
endWorkers without an Args class will still have run_async and run_sync generated with no parameters.
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sidekiq-sorbet. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the Sidekiq::Sorbet project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.