gelauto 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +9 -0
- data/README.md +139 -0
- data/Rakefile +14 -0
- data/bin/gelauto +54 -0
- data/gelauto.gemspec +21 -0
- data/lib/gelauto/arg_list.rb +32 -0
- data/lib/gelauto/array_type.rb +25 -0
- data/lib/gelauto/boolean_type.rb +7 -0
- data/lib/gelauto/cli_utils.rb +18 -0
- data/lib/gelauto/generic_type.rb +24 -0
- data/lib/gelauto/hash_type.rb +29 -0
- data/lib/gelauto/logger.rb +31 -0
- data/lib/gelauto/method_def.rb +34 -0
- data/lib/gelauto/method_index.rb +97 -0
- data/lib/gelauto/namespace.rb +10 -0
- data/lib/gelauto/rbi.rb +47 -0
- data/lib/gelauto/rspec.rb +13 -0
- data/lib/gelauto/type.rb +18 -0
- data/lib/gelauto/type_set.rb +61 -0
- data/lib/gelauto/utils.rb +13 -0
- data/lib/gelauto/var.rb +17 -0
- data/lib/gelauto/version.rb +3 -0
- data/lib/gelauto.rb +124 -0
- data/spec/gelauto_spec.rb +104 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/client.rb +29 -0
- data/spec/support/config.yml +8 -0
- data/spec/support/image.rb +17 -0
- data/spec/support/matchers.rb +103 -0
- data/spec/support/system.rb +10 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: edff254e0369ac872f616b28e6885f92efb5127f0ddd159d477d545015a19f48
|
4
|
+
data.tar.gz: 7645cfbff7c9c2806b8c160ce27aa2f8119f6c56f62362655481e44a26f720d4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 363f769d6dec4cec5a5390bdd099418b53785af49a61cc85a410bc125a66e5ad047fa403ca8220fa7e56ec060c9216f3ee63eacd83dfc8801a90591afee41b43
|
7
|
+
data.tar.gz: abee9406ff027155948e07b0be22cdfbcbd2b9744f492508cd90380ede41f792df120719a5ef4f143c783b52b9ea59269d412e7b4fa629ca083c7e1d08cd1d1c
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
## gelauto [![Build Status](https://secure.travis-ci.org/camertron/gelauto.png?branch=master)](http://travis-ci.org/camertron/gelauto)
|
2
|
+
|
3
|
+
Automatically annotate your code with Sorbet type definitions.
|
4
|
+
|
5
|
+
## What is This Thing?
|
6
|
+
|
7
|
+
The wonderful folks at Stripe recently released a static type checker for Ruby called [Sorbet](https://github.com/sorbet/sorbet). It works by examining type signatures placed at the beginning of each method. For example:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# typed: true
|
11
|
+
|
12
|
+
class Car
|
13
|
+
extend T::Sig
|
14
|
+
|
15
|
+
sig { params(num_wheels: Integer) }
|
16
|
+
def initialize(num_wheels)
|
17
|
+
@num_wheels = num_wheels
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { params(speed: Float).returns(T::Boolean) }
|
21
|
+
def drive(speed)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
Adding these definitions means you get cool stuff like auto-complete and type checking in your editor. Pretty freaking rad.
|
28
|
+
|
29
|
+
### Ok, so what is Gelauto?
|
30
|
+
|
31
|
+
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.
|
32
|
+
|
33
|
+
## Installation
|
34
|
+
|
35
|
+
`gem install gelauto`
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
You can run Gelauto either via the command line or by adding it to your bundle.
|
40
|
+
|
41
|
+
### Command Line Usage
|
42
|
+
|
43
|
+
First, install the gem by running `gem install gelauto`. That will make the `gelauto` executable available on your system.
|
44
|
+
|
45
|
+
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](https://github.com/rspec/rspec) test suite.
|
46
|
+
|
47
|
+
Like most RSpec test suites, let's assume ours is stored in the `spec/` directory (that's the RSpec default too). Let's furthermore assume our code is stored in the `lib` directory. To run the test suite in `spec/` and add type definitions to our files in `lib/`, we might run the following command:
|
48
|
+
|
49
|
+
```bash
|
50
|
+
gelauto run --annotate $(find . -name '*.rb') -- bundle exec rspec spec/
|
51
|
+
```
|
52
|
+
|
53
|
+
If you choose to run Gelauto without the `--annotate` flag, Gelauto will print results to standard output in [RBI format](https://sorbet.org/docs/rbi) and not modify any of your Ruby files.
|
54
|
+
|
55
|
+
### Gelauto in your Bundle
|
56
|
+
|
57
|
+
If you would rather run Gelauto as part of your bundle, add it to your Gemfile like so:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
gem 'gelauto'
|
61
|
+
```
|
62
|
+
|
63
|
+
Gelauto can be invoked from within your code in one of several ways.
|
64
|
+
|
65
|
+
#### Gelauto.discover
|
66
|
+
|
67
|
+
Wrap code you'd like to run with Gelauto in `Gelauto.discover`:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
require 'gelauto'
|
71
|
+
|
72
|
+
Gelauto.paths << 'path/to/file/i/want/to/annotate.rb'
|
73
|
+
|
74
|
+
Gelauto.discover do
|
75
|
+
call_some_method(with, some, params)
|
76
|
+
end
|
77
|
+
|
78
|
+
# loop over files and annotate them
|
79
|
+
Gelauto.each_absolute_path do |path|
|
80
|
+
Gelauto.annotate_file(path)
|
81
|
+
end
|
82
|
+
|
83
|
+
# you can also grab a reference to the method cache Gelauto
|
84
|
+
# has populated with all the type information it's been able
|
85
|
+
# to gather:
|
86
|
+
Gelauto.method_index
|
87
|
+
```
|
88
|
+
|
89
|
+
#### Setup and Teardown
|
90
|
+
|
91
|
+
`Gelauto.discover` is just syntactic sugar around two methods that start and stop Gelauto's method tracing functionality:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
Gelauto.setup
|
95
|
+
|
96
|
+
begin
|
97
|
+
call_some_method(with, some, params)
|
98
|
+
ensure
|
99
|
+
Gelauto.teardown
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
#### RSpec Helper
|
104
|
+
|
105
|
+
Gelauto comes with a handy RSpec helper that can do all this for you. Simply add
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
require 'gelauto/rspec'
|
109
|
+
```
|
110
|
+
|
111
|
+
to your spec_helper.rb, Rakefile, or wherever RSpec is configured.
|
112
|
+
|
113
|
+
## How does it Work?
|
114
|
+
|
115
|
+
Gelauto makes use of Ruby's [TracePoint API](https://ruby-doc.org/core-2.6/TracePoint.html). 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.
|
116
|
+
|
117
|
+
"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.
|
118
|
+
|
119
|
+
## Known Limitations
|
120
|
+
|
121
|
+
* Half-baked support for singleton (i.e. static) methods.
|
122
|
+
* Gelauto does not annotate Ruby files with `# typed: true` comments or `extend T::Sig`.
|
123
|
+
* Gelauto ignores existing type signatures and will simply add another one right above the method.
|
124
|
+
|
125
|
+
## Running Tests
|
126
|
+
|
127
|
+
`bundle exec rspec` should do the trick :)
|
128
|
+
|
129
|
+
## Contributing
|
130
|
+
|
131
|
+
Please fork this repo and submit a pull request.
|
132
|
+
|
133
|
+
## License
|
134
|
+
|
135
|
+
Licensed under the MIT license. See LICENSE for details.
|
136
|
+
|
137
|
+
## Authors
|
138
|
+
|
139
|
+
* Cameron C. Dutro: http://github.com/camertron
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
|
5
|
+
require 'gelauto'
|
6
|
+
|
7
|
+
Bundler::GemHelper.install_tasks
|
8
|
+
|
9
|
+
task default: :spec
|
10
|
+
|
11
|
+
desc 'Run specs'
|
12
|
+
RSpec::Core::RakeTask.new do |t|
|
13
|
+
t.pattern = './spec/**/*_spec.rb'
|
14
|
+
end
|
data/bin/gelauto
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'gelauto'
|
4
|
+
require 'gli'
|
5
|
+
|
6
|
+
module Gelauto
|
7
|
+
module CLI
|
8
|
+
extend GLI::App
|
9
|
+
|
10
|
+
program_desc 'Automatically annotate methods with Sorbet type signatures.'
|
11
|
+
|
12
|
+
version Gelauto::VERSION
|
13
|
+
|
14
|
+
subcommand_option_handling :normal
|
15
|
+
default_command :run
|
16
|
+
|
17
|
+
desc 'Run the given command with Gelauto and optionally annotate files.'
|
18
|
+
command :run do |c|
|
19
|
+
c.desc 'Write discovered type signatures into Ruby files.'
|
20
|
+
c.default_value false
|
21
|
+
c.switch [:a, :annotate]
|
22
|
+
|
23
|
+
c.action do |global_options, options, args|
|
24
|
+
paths, _, cmd = args.chunk_while { |arg1, arg2| arg1 != '--' && arg2 != '--' }.to_a
|
25
|
+
Gelauto.paths += paths
|
26
|
+
|
27
|
+
exe = Gelauto::CLIUtils.which(cmd[0]) || cmd[0]
|
28
|
+
cmd.shift
|
29
|
+
|
30
|
+
old_argv = ARGV.dup
|
31
|
+
|
32
|
+
begin
|
33
|
+
Gelauto.setup
|
34
|
+
ARGV.replace(cmd)
|
35
|
+
load exe
|
36
|
+
ensure
|
37
|
+
Gelauto.teardown
|
38
|
+
ARGV.replace(old_argv)
|
39
|
+
|
40
|
+
if options[:annotate]
|
41
|
+
Gelauto.each_absolute_path do |path|
|
42
|
+
Gelauto.annotate_file(path)
|
43
|
+
Gelauto::Logger.info("Annotated #{path}")
|
44
|
+
end
|
45
|
+
else
|
46
|
+
puts Gelauto::Rbi.new(Gelauto.method_index).to_s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
exit Gelauto::CLI.run(ARGV)
|
data/gelauto.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), 'lib')
|
2
|
+
require 'gelauto/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'gelauto'
|
6
|
+
s.version = ::Gelauto::VERSION
|
7
|
+
s.authors = ['Cameron Dutro']
|
8
|
+
s.email = ['camertron@gmail.com']
|
9
|
+
s.homepage = 'http://github.com/camertron/gelauto'
|
10
|
+
|
11
|
+
s.description = s.summary = 'Automatically annotate your code with Sorbet type definitions.'
|
12
|
+
s.platform = Gem::Platform::RUBY
|
13
|
+
|
14
|
+
s.add_dependency 'parser', '~> 2.6'
|
15
|
+
s.add_dependency 'gli', '~> 2.0'
|
16
|
+
|
17
|
+
s.executables << 'gelauto'
|
18
|
+
|
19
|
+
s.require_path = 'lib'
|
20
|
+
s.files = Dir['{lib,spec}/**/*', 'Gemfile', 'README.md', 'Rakefile', 'gelauto.gemspec']
|
21
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Gelauto
|
2
|
+
class ArgList
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :args
|
6
|
+
|
7
|
+
def initialize(args = [])
|
8
|
+
@args = args
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(arg)
|
12
|
+
args << arg
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](idx)
|
16
|
+
args[idx]
|
17
|
+
end
|
18
|
+
|
19
|
+
def empty?
|
20
|
+
args.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def each(&block)
|
24
|
+
args.each(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_sig
|
28
|
+
return '' if args.empty?
|
29
|
+
args.map(&:to_sig).join(', ')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Gelauto
|
2
|
+
class ArrayType < GenericType
|
3
|
+
def self.introspect(obj)
|
4
|
+
new.tap do |var|
|
5
|
+
obj.each { |elem| var[:elem] << Gelauto.introspect(elem) }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
super(::Array, [:elem])
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_sig
|
14
|
+
if self[:elem].empty?
|
15
|
+
'T::Array'
|
16
|
+
else
|
17
|
+
"T::Array[#{self[:elem].to_sig}]"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def merge!(other)
|
22
|
+
self[:elem].merge!(other[:elem])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Gelauto
|
2
|
+
module CLIUtils
|
3
|
+
EXTS = (ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']).freeze
|
4
|
+
|
5
|
+
def which(cmd)
|
6
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
7
|
+
EXTS.each do |ext|
|
8
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
9
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
extend self
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Gelauto
|
4
|
+
class GenericType
|
5
|
+
attr_reader :ruby_type, :generic_args
|
6
|
+
|
7
|
+
def initialize(ruby_type, generic_arg_names = [])
|
8
|
+
@ruby_type = ruby_type
|
9
|
+
@generic_args = {}
|
10
|
+
|
11
|
+
generic_arg_names.each_with_object({}) do |generic_arg_name, ret|
|
12
|
+
generic_args[generic_arg_name] = TypeSet.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](generic_arg_name)
|
17
|
+
generic_args[generic_arg_name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_sig
|
21
|
+
raise NotImplementedError, "please define #{__method__} in derived classes"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Gelauto
|
2
|
+
class HashType < GenericType
|
3
|
+
def self.introspect(obj)
|
4
|
+
new.tap do |var|
|
5
|
+
obj.each_pair do |key, value|
|
6
|
+
var[:key] << Gelauto.introspect(key)
|
7
|
+
var[:value] << Gelauto.introspect(value)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super(::Hash, [:key, :value])
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_sig
|
17
|
+
if self[:key].empty? && self[:value].empty?
|
18
|
+
'T::Hash'
|
19
|
+
else
|
20
|
+
"T::Hash[#{self[:key].to_sig}, #{self[:value].to_sig}]"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge!(other)
|
25
|
+
self[:key].merge!(other[:key])
|
26
|
+
self[:value].merge!(other[:value])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Gelauto
|
2
|
+
module Logger
|
3
|
+
class << self
|
4
|
+
def debug(str)
|
5
|
+
Gelauto.logger.debug(fmt(str))
|
6
|
+
end
|
7
|
+
|
8
|
+
def info(str)
|
9
|
+
Gelauto.logger.info(fmt(str))
|
10
|
+
end
|
11
|
+
|
12
|
+
def warn(str)
|
13
|
+
Gelauto.logger.warn(fmt(str))
|
14
|
+
end
|
15
|
+
|
16
|
+
def error(str)
|
17
|
+
Gelauto.logger.error(fmt(str))
|
18
|
+
end
|
19
|
+
|
20
|
+
def fatal(str)
|
21
|
+
Gelauto.logger.fatal(fmt(str))
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def fmt(str)
|
27
|
+
"[Gelauto] #{str}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Gelauto
|
4
|
+
class MethodDef
|
5
|
+
attr_reader :name, :args, :nesting, :return_types
|
6
|
+
|
7
|
+
def initialize(name, args, nesting, return_types = TypeSet.new)
|
8
|
+
@name = name
|
9
|
+
@args = ArgList.new(args.map { |arg| Var.new(arg) })
|
10
|
+
@nesting = nesting
|
11
|
+
@return_types = return_types
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_sig
|
15
|
+
components = []
|
16
|
+
|
17
|
+
unless args.empty?
|
18
|
+
components << "params(#{args.to_sig})"
|
19
|
+
end
|
20
|
+
|
21
|
+
if name == :initialize
|
22
|
+
components << 'void'
|
23
|
+
else
|
24
|
+
components << "returns(#{return_types.to_sig})"
|
25
|
+
end
|
26
|
+
|
27
|
+
"sig { #{components.join('.')} }"
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_rbi
|
31
|
+
"#{to_sig}\ndef #{name}; end"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Gelauto
|
2
|
+
class MethodIndex
|
3
|
+
attr_reader :index
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@index = Hash.new { |h, k| h[k] = {} }
|
7
|
+
end
|
8
|
+
|
9
|
+
def index_methods_in(path, ast, nesting = [])
|
10
|
+
return unless ast
|
11
|
+
|
12
|
+
case ast.type
|
13
|
+
when :def, :defs
|
14
|
+
name = nil
|
15
|
+
args = nil
|
16
|
+
|
17
|
+
if ast.type == :def
|
18
|
+
name = ast.children[0]
|
19
|
+
args = ast.children[1].children.map { |c| c.children.first }
|
20
|
+
else
|
21
|
+
name = ast.children[1]
|
22
|
+
args = ast.children[2].children.map { |c| c.children.first }
|
23
|
+
end
|
24
|
+
|
25
|
+
md = MethodDef.new(name, args, nesting)
|
26
|
+
index[path][ast.location.name.line] = md
|
27
|
+
|
28
|
+
# point to start of method
|
29
|
+
index[path][ast.location.end.line] = ast.location.name.line
|
30
|
+
|
31
|
+
when :class, :module
|
32
|
+
const_name = ast.children.first.children.last
|
33
|
+
return visit_children(path, ast, nesting + [Namespace.new(const_name, ast.type)])
|
34
|
+
end
|
35
|
+
|
36
|
+
visit_children(path, ast, nesting)
|
37
|
+
end
|
38
|
+
|
39
|
+
def find(path, lineno)
|
40
|
+
if md = index[path][lineno]
|
41
|
+
return md if md.is_a?(MethodDef)
|
42
|
+
|
43
|
+
# md is actually an index pointing to another line
|
44
|
+
index[path][md]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def each
|
49
|
+
return to_enum(__method__) unless block_given?
|
50
|
+
|
51
|
+
index.each_pair do |path, line_index|
|
52
|
+
line_index.each_pair do |lineno, md|
|
53
|
+
yield(path, lineno, md) if md.is_a?(MethodDef)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def each_method_in(path)
|
59
|
+
return to_enum(__method__, path) unless block_given?
|
60
|
+
|
61
|
+
index[path].each_pair do |lineno, md|
|
62
|
+
yield lineno, md if md.is_a?(MethodDef)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def annotate(path, code)
|
67
|
+
lines = code.split(/\r?\n/)
|
68
|
+
mds = index[path]
|
69
|
+
|
70
|
+
[].tap do |annotated|
|
71
|
+
lines.each_with_index do |line, idx|
|
72
|
+
lineno = idx + 1
|
73
|
+
md = mds[lineno]
|
74
|
+
|
75
|
+
if md.is_a?(MethodDef)
|
76
|
+
indent = line[0...line.index(/[^\s]/)]
|
77
|
+
annotated << "#{indent}#{md.to_sig}"
|
78
|
+
end
|
79
|
+
|
80
|
+
annotated << line
|
81
|
+
end
|
82
|
+
end.join("\n")
|
83
|
+
end
|
84
|
+
|
85
|
+
def reset
|
86
|
+
index.clear
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def visit_children(path, ast, nesting)
|
92
|
+
ast.children.each do |child|
|
93
|
+
index_methods_in(path, child, nesting) if child.is_a?(Parser::AST::Node)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/gelauto/rbi.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Gelauto
|
2
|
+
class Rbi
|
3
|
+
attr_reader :method_index, :paths
|
4
|
+
|
5
|
+
def initialize(method_index, paths = Gelauto.paths)
|
6
|
+
@method_index = method_index
|
7
|
+
@paths = paths
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
StringIO.new.tap { |io| compose(method_groups, io) }.string
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def method_groups
|
17
|
+
@method_groups ||= { methods: [], children: {} }.tap do |groups|
|
18
|
+
Utils.each_absolute_path(paths) do |path|
|
19
|
+
method_index.each_method_in(path) do |_lineno, md|
|
20
|
+
cur_group = md.nesting.inject(groups) do |group, namespace|
|
21
|
+
group[:children][[namespace.name, namespace.type]] ||= { methods: [], children: {} }
|
22
|
+
end
|
23
|
+
|
24
|
+
cur_group[:methods] << md
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def compose(h, io, indent_level = 0)
|
31
|
+
io.write(indent(h[:methods].map { |md| md.to_rbi }.join("\n\n"), indent_level))
|
32
|
+
|
33
|
+
h[:children].each_with_index do |((namespace, type), next_level), idx|
|
34
|
+
io.write("\n\n") if idx > 0
|
35
|
+
io.write(indent("#{type} #{namespace}", indent_level))
|
36
|
+
io.write("\n")
|
37
|
+
compose(next_level, io, indent_level + 1)
|
38
|
+
io.write("\n")
|
39
|
+
io.write(indent('end', indent_level))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def indent(str, indent_level)
|
44
|
+
str.split("\n").map { |s| "#{' ' * indent_level}#{s}" }.join("\n")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'gelauto'
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.before(:suite) { Gelauto.setup }
|
5
|
+
config.after(:suite) do
|
6
|
+
Gelauto.teardown
|
7
|
+
|
8
|
+
Gelauto.each_absolute_path do |path|
|
9
|
+
Gelauto.annotate_file(path)
|
10
|
+
Gelauto::Logger.info("Annotated #{path}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/gelauto/type.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Gelauto
|
4
|
+
class TypeSet
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :set
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@set = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def size
|
14
|
+
set.size
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
set.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def <<(type)
|
22
|
+
set[type.ruby_type] = type
|
23
|
+
end
|
24
|
+
|
25
|
+
def merge!(other)
|
26
|
+
other.set.each do |other_ruby_type, other_type|
|
27
|
+
if set[other_ruby_type]
|
28
|
+
set[other_ruby_type].merge!(other_type)
|
29
|
+
else
|
30
|
+
set[other_ruby_type] = other_type
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def each(&block)
|
36
|
+
set.each_value(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_sig
|
40
|
+
nilable = false
|
41
|
+
|
42
|
+
sigs = set.each_with_object([]) do |(rt, t), ret|
|
43
|
+
if rt == ::NilClass
|
44
|
+
nilable = true
|
45
|
+
else
|
46
|
+
ret << t.to_sig
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
sigs.uniq!
|
51
|
+
|
52
|
+
if sigs.size == 0
|
53
|
+
'T.untyped'
|
54
|
+
elsif sigs.size == 1
|
55
|
+
sigs.first
|
56
|
+
else
|
57
|
+
"T.#{nilable ? 'nilable' : 'any'}(#{sigs.join(', ')})"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/gelauto/var.rb
ADDED
data/lib/gelauto.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'parser/current'
|
3
|
+
|
4
|
+
module Gelauto
|
5
|
+
autoload :ArgList, 'gelauto/arg_list'
|
6
|
+
autoload :ArrayType, 'gelauto/array_type'
|
7
|
+
autoload :BooleanType, 'gelauto/boolean_type'
|
8
|
+
autoload :CLIUtils, 'gelauto/cli_utils'
|
9
|
+
autoload :GenericType, 'gelauto/generic_type'
|
10
|
+
autoload :HashType, 'gelauto/hash_type'
|
11
|
+
autoload :Logger, 'gelauto/logger'
|
12
|
+
autoload :MethodDef, 'gelauto/method_def'
|
13
|
+
autoload :MethodIndex, 'gelauto/method_index'
|
14
|
+
autoload :Namespace, 'gelauto/namespace'
|
15
|
+
autoload :Rbi, 'gelauto/rbi'
|
16
|
+
autoload :Type, 'gelauto/type'
|
17
|
+
autoload :TypeSet, 'gelauto/type_set'
|
18
|
+
autoload :Utils, 'gelauto/utils'
|
19
|
+
autoload :Var, 'gelauto/var'
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_accessor :paths
|
23
|
+
attr_writer :logger
|
24
|
+
|
25
|
+
def setup
|
26
|
+
enable_traces
|
27
|
+
index_methods
|
28
|
+
end
|
29
|
+
|
30
|
+
def teardown
|
31
|
+
disable_traces
|
32
|
+
end
|
33
|
+
|
34
|
+
def discover
|
35
|
+
setup
|
36
|
+
yield
|
37
|
+
ensure
|
38
|
+
teardown
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_index
|
42
|
+
@method_index ||= MethodIndex.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def paths
|
46
|
+
@paths ||= []
|
47
|
+
end
|
48
|
+
|
49
|
+
def each_absolute_path(&block)
|
50
|
+
Utils.each_absolute_path(paths, &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def register_type(type, handler)
|
54
|
+
types[type] = handler
|
55
|
+
end
|
56
|
+
|
57
|
+
def types
|
58
|
+
@types ||= Hash.new(Gelauto::Type)
|
59
|
+
end
|
60
|
+
|
61
|
+
def introspect(obj)
|
62
|
+
Gelauto.types[obj.class].introspect(obj)
|
63
|
+
end
|
64
|
+
|
65
|
+
def annotate_file(path)
|
66
|
+
annotated_code = Gelauto.method_index.annotate(path, File.read(path))
|
67
|
+
File.write(path, annotated_code)
|
68
|
+
end
|
69
|
+
|
70
|
+
def logger
|
71
|
+
@logger ||= ::Logger.new(STDOUT)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def enable_traces
|
77
|
+
call_trace.enable
|
78
|
+
return_trace.enable
|
79
|
+
end
|
80
|
+
|
81
|
+
def disable_traces
|
82
|
+
call_trace.disable
|
83
|
+
return_trace.disable
|
84
|
+
end
|
85
|
+
|
86
|
+
def index_methods
|
87
|
+
each_absolute_path.with_index do |path, idx|
|
88
|
+
begin
|
89
|
+
method_index.index_methods_in(
|
90
|
+
path, Parser::CurrentRuby.parse(File.read(path))
|
91
|
+
)
|
92
|
+
|
93
|
+
Gelauto::Logger.info("Indexed #{idx + 1}/#{paths.size} paths")
|
94
|
+
rescue Parser::SyntaxError => e
|
95
|
+
Gelauto::Logger.error("Syntax error in #{path}, skipping")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def call_trace
|
101
|
+
@call_trace ||= TracePoint.new(:call) do |tp|
|
102
|
+
if md = method_index.find(tp.path, tp.lineno)
|
103
|
+
md.args.each do |arg|
|
104
|
+
var = tp.binding.local_variable_get(arg.name)
|
105
|
+
arg.types << Gelauto.introspect(var)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def return_trace
|
112
|
+
@return_trace ||= TracePoint.new(:return) do |tp|
|
113
|
+
if md = method_index.find(tp.path, tp.lineno)
|
114
|
+
md.return_types << Gelauto.introspect(tp.return_value)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
Gelauto.register_type(::Hash, Gelauto::HashType)
|
122
|
+
Gelauto.register_type(::Array, Gelauto::ArrayType)
|
123
|
+
Gelauto.register_type(::TrueClass, Gelauto::BooleanType)
|
124
|
+
Gelauto.register_type(::FalseClass, Gelauto::BooleanType)
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gelauto do
|
4
|
+
before { index.reset }
|
5
|
+
|
6
|
+
let(:index) { Gelauto.method_index }
|
7
|
+
|
8
|
+
context 'with simple types' do
|
9
|
+
it 'identifies method signatures correctly' do
|
10
|
+
img = nil
|
11
|
+
|
12
|
+
Gelauto.discover do
|
13
|
+
img = GelautoSpecs::Image.new('foo.jpg', 800, 400)
|
14
|
+
expect(img.aspect_ratio).to eq(2.0)
|
15
|
+
end
|
16
|
+
|
17
|
+
init = get_indexed_method(img, :initialize)
|
18
|
+
expect(init).to accept(path: String, width: Integer, height: Integer)
|
19
|
+
expect(init).to hand_back_void
|
20
|
+
|
21
|
+
aspect_ratio = get_indexed_method(img, :aspect_ratio)
|
22
|
+
expect(aspect_ratio).to hand_back(Float)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with generic types' do
|
27
|
+
before do
|
28
|
+
Gelauto.discover do
|
29
|
+
@client = GelautoSpecs::Client.new(url: 'http://foo.com', username: 'bar')
|
30
|
+
@response = @client.request('body', param1: 'abc', param2: 'def')
|
31
|
+
expect(@response.to_a).to eq([200, 'it worked!'])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'identifies signature for Client#initialize' do
|
36
|
+
init = get_indexed_method(@client, :initialize)
|
37
|
+
expect(init).to accept(options: { Hash => { key: Symbol, value: String } })
|
38
|
+
expect(init).to hand_back_void
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'identifies signature for Client#request' do
|
42
|
+
request = get_indexed_method(@client, :request)
|
43
|
+
expect(request).to accept(body: String, headers: { Hash => { key: Symbol, value: String } })
|
44
|
+
expect(request).to hand_back(GelautoSpecs::Response)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'identifies signature for Response#initialize' do
|
48
|
+
init = get_indexed_method(@response, :initialize)
|
49
|
+
expect(init).to accept(status: Integer, body: String)
|
50
|
+
expect(init).to hand_back_void
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'identifies signature for Response#to_a' do
|
54
|
+
to_a = get_indexed_method(@response, :to_a)
|
55
|
+
expect(to_a).to hand_back(Array => { elem: [Integer, String] })
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with nested generic types' do
|
60
|
+
before do
|
61
|
+
Gelauto.discover do
|
62
|
+
GelautoSpecs::System.configure(YAML.load_file('spec/support/config.yml'))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'identifies signatures for System.configure' do
|
67
|
+
configure = get_indexed_method(GelautoSpecs::System, :configure)
|
68
|
+
expect(configure).to accept(
|
69
|
+
config: {
|
70
|
+
Hash => {
|
71
|
+
key: String,
|
72
|
+
value: [
|
73
|
+
{
|
74
|
+
Array => {
|
75
|
+
elem: [
|
76
|
+
String,
|
77
|
+
{
|
78
|
+
Hash => {
|
79
|
+
key: String,
|
80
|
+
value: {
|
81
|
+
Hash => {
|
82
|
+
key: String,
|
83
|
+
value: [
|
84
|
+
String,
|
85
|
+
{
|
86
|
+
Array => {
|
87
|
+
elem: String
|
88
|
+
}
|
89
|
+
}
|
90
|
+
]
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|
95
|
+
]
|
96
|
+
}
|
97
|
+
}
|
98
|
+
]
|
99
|
+
}
|
100
|
+
}
|
101
|
+
)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$:.push(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'gelauto'
|
5
|
+
require 'pry-byebug'
|
6
|
+
|
7
|
+
Dir.chdir('spec') do
|
8
|
+
Dir.glob('support/*.rb').each do |f|
|
9
|
+
require f
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.include(GelautoSpecs::AcceptMatcher)
|
15
|
+
config.include(GelautoSpecs::HandBackMatcher)
|
16
|
+
|
17
|
+
config.include(
|
18
|
+
Module.new do
|
19
|
+
def get_indexed_method(obj, method_name)
|
20
|
+
path, lineno = obj.method(method_name).source_location
|
21
|
+
Gelauto.method_index.find(path, lineno)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
# quiet logs for test runs
|
28
|
+
Gelauto.logger.level = :fatal
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module GelautoSpecs
|
2
|
+
class Response
|
3
|
+
attr_reader :status, :body
|
4
|
+
|
5
|
+
def initialize(status, body)
|
6
|
+
@status = status
|
7
|
+
@body = body
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_a
|
11
|
+
[status, body]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Client
|
16
|
+
attr_reader :url, :username
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
@url = options[:url]
|
20
|
+
@username = options[:username]
|
21
|
+
end
|
22
|
+
|
23
|
+
def request(body, headers = {})
|
24
|
+
Response.new(200, 'it worked!')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Gelauto.paths << __FILE__
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module GelautoSpecs
|
2
|
+
class Image
|
3
|
+
attr_reader :path, :width, :height
|
4
|
+
|
5
|
+
def initialize(path, width, height)
|
6
|
+
@path = path
|
7
|
+
@width = width
|
8
|
+
@height = height
|
9
|
+
end
|
10
|
+
|
11
|
+
def aspect_ratio
|
12
|
+
width.to_f / height
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Gelauto.paths << __FILE__
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module GelautoSpecs
|
2
|
+
def self.arg_hash_to_arglist(params)
|
3
|
+
Gelauto::ArgList.new.tap do |arg_list|
|
4
|
+
params.each_pair do |name, type_info|
|
5
|
+
arg_list << case type_info
|
6
|
+
when Hash
|
7
|
+
Gelauto::Var.new(name).tap do |v|
|
8
|
+
v.types.merge!(type_hash_to_typeset(type_info))
|
9
|
+
end
|
10
|
+
else
|
11
|
+
Gelauto::Var.new(name).tap do |v|
|
12
|
+
Array(type_info).each { |t| v.types << Gelauto::Type.new(t) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.type_hash_to_typeset(type_info)
|
20
|
+
Gelauto::TypeSet.new.tap do |typeset|
|
21
|
+
type_info.each_pair do |type, generics|
|
22
|
+
type = Gelauto.types[type].new
|
23
|
+
|
24
|
+
generics.each_pair do |generic_name, generic_type|
|
25
|
+
type.generic_args[generic_name].merge!(
|
26
|
+
case generic_type
|
27
|
+
when Hash
|
28
|
+
type_hash_to_typeset(generic_type)
|
29
|
+
else
|
30
|
+
type_array_to_typeset(Array(generic_type))
|
31
|
+
end
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
typeset << type
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.type_array_to_typeset(params)
|
41
|
+
Gelauto::TypeSet.new.tap do |typeset|
|
42
|
+
params.each do |type_info|
|
43
|
+
case type_info
|
44
|
+
when Hash
|
45
|
+
typeset.merge!(type_hash_to_typeset(type_info))
|
46
|
+
when Array
|
47
|
+
typeset.merge!(type_array_to_typeset(type_info))
|
48
|
+
else
|
49
|
+
typeset << Gelauto::Type.new(type_info)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module AcceptMatcher
|
56
|
+
extend RSpec::Matchers::DSL
|
57
|
+
|
58
|
+
matcher :accept do |expected_params = {}|
|
59
|
+
match do |actual_md|
|
60
|
+
@expected_arg_list = GelautoSpecs.arg_hash_to_arglist(expected_params)
|
61
|
+
@expected_arg_list.to_sig == actual_md.args.to_sig
|
62
|
+
end
|
63
|
+
|
64
|
+
failure_message do |actual_md|
|
65
|
+
<<~END
|
66
|
+
Expected: #{@expected_arg_list.to_sig}
|
67
|
+
Got: #{actual_md.args.to_sig}
|
68
|
+
END
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module HandBackMatcher
|
74
|
+
extend RSpec::Matchers::DSL
|
75
|
+
|
76
|
+
matcher :hand_back do |*expected_types|
|
77
|
+
match do |actual_md|
|
78
|
+
@expected_typeset = GelautoSpecs.type_array_to_typeset(expected_types)
|
79
|
+
@expected_typeset.to_sig == actual_md.return_types.to_sig
|
80
|
+
end
|
81
|
+
|
82
|
+
failure_message do |actual_md|
|
83
|
+
<<~END
|
84
|
+
Expected: #{@expected_typeset.to_sig}
|
85
|
+
Got: #{actual_md.return_types.to_sig}
|
86
|
+
END
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
matcher :hand_back_void do
|
91
|
+
match do |actual_md|
|
92
|
+
actual_md.to_sig.end_with?('.void }')
|
93
|
+
end
|
94
|
+
|
95
|
+
failure_message do |actual_md|
|
96
|
+
<<~END
|
97
|
+
Expected: void
|
98
|
+
Got: #{actual_md.return_types.to_sig}
|
99
|
+
END
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gelauto
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cameron Dutro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-07-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: parser
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: gli
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
description: Automatically annotate your code with Sorbet type definitions.
|
42
|
+
email:
|
43
|
+
- camertron@gmail.com
|
44
|
+
executables:
|
45
|
+
- gelauto
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- Gemfile
|
50
|
+
- README.md
|
51
|
+
- Rakefile
|
52
|
+
- bin/gelauto
|
53
|
+
- gelauto.gemspec
|
54
|
+
- lib/gelauto.rb
|
55
|
+
- lib/gelauto/arg_list.rb
|
56
|
+
- lib/gelauto/array_type.rb
|
57
|
+
- lib/gelauto/boolean_type.rb
|
58
|
+
- lib/gelauto/cli_utils.rb
|
59
|
+
- lib/gelauto/generic_type.rb
|
60
|
+
- lib/gelauto/hash_type.rb
|
61
|
+
- lib/gelauto/logger.rb
|
62
|
+
- lib/gelauto/method_def.rb
|
63
|
+
- lib/gelauto/method_index.rb
|
64
|
+
- lib/gelauto/namespace.rb
|
65
|
+
- lib/gelauto/rbi.rb
|
66
|
+
- lib/gelauto/rspec.rb
|
67
|
+
- lib/gelauto/type.rb
|
68
|
+
- lib/gelauto/type_set.rb
|
69
|
+
- lib/gelauto/utils.rb
|
70
|
+
- lib/gelauto/var.rb
|
71
|
+
- lib/gelauto/version.rb
|
72
|
+
- spec/gelauto_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- spec/support/client.rb
|
75
|
+
- spec/support/config.yml
|
76
|
+
- spec/support/image.rb
|
77
|
+
- spec/support/matchers.rb
|
78
|
+
- spec/support/system.rb
|
79
|
+
homepage: http://github.com/camertron/gelauto
|
80
|
+
licenses: []
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubygems_version: 3.0.4
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Automatically annotate your code with Sorbet type definitions.
|
101
|
+
test_files: []
|