Install via RubyGems.
Steep requires Ruby 2.6 or later.
Steep does not infer types from Ruby programs, but requires declaring types and writing annotations. You have to go on the following three steps.
Run steep init
to generate a configuration file.
$ steep init # Generates Steepfile
Edit the Steepfile
:
target :app do check "lib" signature "sig" library "pathname" end
Declare types in .rbs
files in sig
directory.
class Person
@name: String
@contacts: Array[Email | Phone]
def initialize: (name: String) -> untyped
def name: -> String
def contacts: -> Array[Email | Phone]
def guess_country: -> (String | nil)
end
class Email
@address: String
def initialize: (address: String) -> untyped
def address: -> String
end
class Phone
@country: String
@number: String
def initialize: (country: String, number: String) -> untyped
def country: -> String
def number: -> String
def self.countries: -> Hash[String, String]
end
Hash[String, String]
.Email | Phone
.self.countries
.nil
type to represent nullable types.Write Ruby code with annotations.
class Person # `@dynamic` annotation is to tell steep that # the `name` and `contacts` methods are defined without def syntax. # (Steep can skip checking if the methods are implemented.) # @dynamic name, contacts attr_reader :name attr_reader :contacts def initialize(name:) @name = name @contacts = [] end def guess_country() contacts.map do |contact| # With case expression, simple type-case is implemented. # `contact` has type of `Phone | Email` but in the `when` clause, contact has type of `Phone`. case contact when Phone contact.country end end.compact.first end end class Email # @dynamic address attr_reader :address def initialize(address:) @address = address end def ==(other) # `other` has type of `untyped`, which means type checking is skipped. # No type errors can be detected in this method. other.is_a?(self.class) && other.address == address end def hash [self.class, address].hash end end class Phone # @dynamic country, number attr_reader :country, :number def initialize(country:, number:) @country = country @number = number end def ==(other) # You cannot use `case` for type case because `other` has type of `untyped`, not a union type. # You have to explicitly declare the type of `other` in `if` expression. if other.is_a?(Phone) # @type var other: Phone other.country == country && other.number == number end end def hash [self.class, country, number].hash end end
Run steep check
command to type check. 💡
$ steep check
lib/phone.rb:46:0: MethodDefinitionMissing: module=::Phone, method=self.countries (class Phone)
You now find Phone.countries
method is not implemented yet. 🙃
You can use rbs prototype
command to generate a signature declaration.
$ rbs prototype rb lib/person.rb lib/email.rb lib/phone.rb
class Person
@name: untyped
@contacts: Array[untyped]
def initialize: (name: untyped) -> Array[untyped]
def guess_country: () -> untyped
end
class Email
@address: untyped
def initialize: (address: untyped) -> untyped
def ==: (untyped) -> untyped
def hash: () -> untyped
end
class Phone
@country: untyped
@number: untyped
def initialize: (country: untyped, number: untyped) -> untyped
def ==: (untyped) -> void
def hash: () -> untyped
end
It prints all methods, classes, instance variables, and constants. It can be a good starting point to writing signatures.
Because it just prints all def
s, you may find some odd points:
initialize
in Person
looks strange.attr_reader
methods extracted.Generally, these are by our design.
rbs prototype
offers options: rbi
to generate prototype from Sorbet RBI and runtime
to generate from runtime API.
There are some documents in the manual
and guides
directories.
The doc
directory contains a few internal design docs.
You can find examples in smoke
directory.
Steep implements some of the Language Server Protocol features.
let g:ale_ruby_steep_executable = 'bundle'
to use your bundled steep
version.Other LSP supporting tools may work with Steep where it starts the server as steep langserver
.
Steep comes with a set of configurable Rake tasks.
# Rakefile require "steep/rake_task" Steep::RakeTask.new do |t| t.check.severity_level = :error t.watch.verbose end task default: [:steep]
Use bundle exec rake -T
to see all available tasks.
After checking out the repo, run bin/setup
to install dependencies. Then, run rake test
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 tags, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/soutaro/steep.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4