Automatically annotate your code with Sorbet type definitions.
What is This Thing?
The wonderful folks at Stripe recently released a static type checker for Ruby called Sorbet. It works by examining type signatures placed at the beginning of each method. For example:
# typed: true
class Car
extend T::Sig
sig { params(num_wheels: Integer) }
def initialize(num_wheels)
@num_wheels = num_wheels
end
sig { params(speed: Float).returns(T::Boolean) }
def drive(speed)
true
end
end
Adding these definitions means you get cool stuff like auto-complete and type checking in your editor. Pretty freaking rad.
Ok, so what is Gelauto?
Gelauto is an auto-matic way (get it?! lol) of adding Sorbet type signatures to your methods. It works by running your code (for example, your test suite). As your code runs, Gelauto keeps track of the actual types of objects that were passed to your methods as well as the types of objects they return. After gathering the info, Gelauto then (optionally) inserts type signatures into your Ruby files.
Installation
gem install gelauto
Usage
You can run Gelauto either via the command line or by adding it to your bundle.
Command Line Usage
First, install the gem by running gem install gelauto
. That will make the gelauto
executable available on your system.
Gelauto's only subcommand is run
, which accepts a list of Ruby files to scan for methods and a command to run that will exercise your code.
In this example, we're going to be running an RSpec test suite.
Like most RSpec test suites, let's assume ours is stored in the spec/
directory (that's the RSpec default too). To run the test suite in spec/
and add type definitions to our ruby files, we might run the following command:
gelauto run --annotate $(find . -name '*.rb') -- bundle exec rspec spec/
You can also choose to run Gelauto with the --rbi
flag, which will cause Gelauto to print results to standard output or to the given file in RBI format:
# print RBI output to STDOUT
gelauto run --annotate --rbi - $(find . -name '*.rb') -- bundle exec rspec spec/
# write RBI output to a file
gelauto run --annotate --rbi ./rbi/mylib.rbi $(find . -name '*.rb') -- bundle exec rspec spec/
In this second example, we're going to be running a minitest test suite. Like most minitest suites, let's assume ours is stored in the test/
directory (that's the Rails default too). To run the test suite in test/
, we might run the following command:
gelauto run --annotate $(find . -name '*.rb') -- bundle exec rake test/
Gelauto in your Bundle
If you would rather run Gelauto as part of your bundle, add it to your Gemfile like so:
gem 'gelauto'
Gelauto can be invoked from within your code in one of several ways.
Gelauto.discover
Wrap code you'd like to run with Gelauto in Gelauto.discover
:
require 'gelauto'
Gelauto.paths << 'path/to/file/i/want/to/annotate.rb'
Gelauto.discover do
call_some_method(with, some, params)
end
# loop over files and annotate them
Gelauto.each_absolute_path do |path|
Gelauto.annotate_file(path)
end
# you can also grab a reference to the method cache Gelauto
# has populated with all the type information it's been able
# to gather:
Gelauto.method_index
Setup and Teardown
Gelauto.discover
is just syntactic sugar around two methods that start and stop Gelauto's method tracing functionality:
Gelauto.setup
begin
call_some_method(with, some, params)
ensure
Gelauto.teardown
end
RSpec Helper
Gelauto comes with a handy RSpec helper that can do most of this for you. Simply add
require 'gelauto/rspec'
to your spec_helper.rb, Rakefile, or wherever RSpec is configured. You'll also need to set the GELAUTO_FILES
environment variable when running your test suite. For example:
GELAUTO_FILES=$(find . -name *.rb) bundle exec rspec
Files can be separated by spaces, newlines, or commas. If you want Gelauto to annotate them, set GELAUTO_ANNOTATE
to true
, eg:
GELAUTO_FILES=$(find . -name *.rb) GELAUTO_ANNOTATE=true bundle exec rspec
Finally, set GELAUTO_RBI=/path/to/output.rbi
to have Gelauto emit an RBI file when the test suite finishes.
Minitest Helper
Gelauto also comes with a handy Minitest helper. Simply add
require 'gelauto/minitest'
to your test_helper.rb (or wherever Minitest is require
d and configured). The same environment variables described above for RSpec, eg. GELAUTO_FILES
, GELAUTO_ANNOTATE
, and GELAUTO_RBI
, can be used in a Minitest setup.
How does it Work?
Gelauto makes use of Ruby's TracePoint API. TracePoint effectively allows Gelauto to receive a notification whenever a Ruby method is called and whenever a method returns. That info combined with method location information gathered from parsing your Ruby files ahead of time allows Gelauto to know a) where methods are located, 2) what arguments they take, 3) the types of those arguments, and 4) the type of the return value.
"Doesn't that potentially make my code run slower?" is a question you might ask. Yes. Gelauto adds overhead to literally every Ruby method call, so your code will probably run quite a bit slower. For that reason you probably won't want to enable Gelauto in, for example, a production web application.
Known Limitations
- Half-baked support for singleton (i.e. static) methods.
- Gelauto does not annotate Ruby files with
# typed: true
comments orextend T::Sig
.
Running Tests
bundle exec rspec
should do the trick :)
Contributing
Please fork this repo and submit a pull request.
License
Licensed under the MIT license. See LICENSE for details.
Authors
- Cameron C. Dutro: http://github.com/camertron