tapioca 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +9 -2
- data/lib/tapioca.rb +2 -0
- data/lib/tapioca/cli.rb +7 -0
- data/lib/tapioca/compilers/sorbet.rb +34 -0
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +3 -0
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +1 -20
- data/lib/tapioca/compilers/todos_compiler.rb +32 -0
- data/lib/tapioca/config.rb +6 -3
- data/lib/tapioca/config_builder.rb +3 -0
- data/lib/tapioca/generator.rb +32 -0
- data/lib/tapioca/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6aedd5a0415cca7e554021e114401254561649803acceed6caaa2cb24b9b4c8c
|
4
|
+
data.tar.gz: 376290c5b3cad6f082e9fc186cbb2ed32a9bb239c5c37bc8f9f464c78a2d951e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e65c8822f8312788075a84f24d5b9c95221bba2ced2dd877f150a377696fd70eff96bc936fdd3cf482fa6d4ac1b2d86a0201ed91c386c1ee1ce0bd19e8defd9c
|
7
|
+
data.tar.gz: bace8a6fca5ca72789c5fdfa121fb7b30bf415e9f04d6c715f43237f4d022c4507a6d6182161c4928a7258f9759c957a22c2559cc73ebc56d093f5864dcd872f
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/Shopify/tapioca)
|
4
4
|
|
5
|
-
Tapioca is a library used to generate RBI (Ruby interface) files for use with [Sorbet](https://sorbet.org). RBI files provide the structure (classes, modules, methods, parameters) of the gem/library to Sorbet to assist with typechecking.
|
5
|
+
Tapioca is a library used to generate RBI (Ruby interface) files for use with [Sorbet](https://sorbet.org). RBI files provide the structure (classes, modules, methods, parameters) of the gem/library to Sorbet to assist with typechecking.
|
6
6
|
|
7
7
|
As yet, no gem exports type information in a consumable format and it would be a huge effort to manually maintain such an interface file for all the gems that your codebase depends on. Thus, there is a need for an automated way to generate the appropriate RBI file for a given gem. The `tapioca` gem, developed at Shopify, is able to do exactly that to almost 99% accuracy. It can generate the definitions for all statically defined types and most of the runtime defined types exported from Ruby gems (non-Ruby gems are not handled yet).
|
8
8
|
|
@@ -27,7 +27,7 @@ from (pry):3:in `__pry__`
|
|
27
27
|
=> BetterHtml::Parser
|
28
28
|
```
|
29
29
|
|
30
|
-
In order to make sure that `tapioca` can reflect on that type, we need to add the line `require "better_html/parser"` to the `sorbet/tapioca/require.rb` file. This will make sure `BetterHtml::Parser` is loaded into memory and a type annotation is generated for it in the `better_html.rbi` file. If this extra `require` line is not added to `sorbet/tapioca/require.rb` file, then the definition for that type will be missing from the RBI file.
|
30
|
+
In order to make sure that `tapioca` can reflect on that type, we need to add the line `require "better_html/parser"` to the `sorbet/tapioca/require.rb` file. This will make sure `BetterHtml::Parser` is loaded into memory and a type annotation is generated for it in the `better_html.rbi` file. If this extra `require` line is not added to `sorbet/tapioca/require.rb` file, then the definition for that type will be missing from the RBI file.
|
31
31
|
|
32
32
|
If you ever run into a case, where you add a gem or update the version of a gem and run `tapioca sync` but don't have some types you expect in the generated gem RBI files, you will need to make sure you have added the necessary requires to the `sorbet/tapioca/require.rb` file.
|
33
33
|
|
@@ -50,6 +50,7 @@ Commands:
|
|
50
50
|
tapioca help [COMMAND] # Describe available commands or one specific command
|
51
51
|
tapioca init # initializes folder structure
|
52
52
|
tapioca sync # sync RBIs to Gemfile
|
53
|
+
tapioca todo # generate the list of unresolved constants
|
53
54
|
|
54
55
|
Options:
|
55
56
|
--pre, -b, [--prerequire=file] # A file to be required before Bundler.require is called
|
@@ -80,6 +81,12 @@ Command: `tapioca sync`
|
|
80
81
|
|
81
82
|
This will sync the RBIs with the gems in the Gemfile and will add, update, and remove RBIs as necessary.
|
82
83
|
|
84
|
+
### Generate the list of all unresolved constants
|
85
|
+
|
86
|
+
Command: `tapioca todo`
|
87
|
+
|
88
|
+
This will generate the file `sorbet/rbi/todo.rbi` defining all unresolved constants as empty modules.
|
89
|
+
|
83
90
|
### Flags
|
84
91
|
|
85
92
|
- `--prerequire [file]`: A file to be required before `Bundler.require` is called.
|
data/lib/tapioca.rb
CHANGED
@@ -35,7 +35,9 @@ require "tapioca/config_builder"
|
|
35
35
|
require "tapioca/generator"
|
36
36
|
require "tapioca/cli"
|
37
37
|
require "tapioca/gemfile"
|
38
|
+
require "tapioca/compilers/sorbet"
|
38
39
|
require "tapioca/compilers/symbol_table_compiler"
|
39
40
|
require "tapioca/compilers/symbol_table/symbol_generator"
|
40
41
|
require "tapioca/compilers/symbol_table/symbol_loader"
|
42
|
+
require "tapioca/compilers/todos_compiler"
|
41
43
|
require "tapioca/version"
|
data/lib/tapioca/cli.rb
CHANGED
@@ -52,6 +52,13 @@ module Tapioca
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
desc "todo", "generate the list of unresolved constants"
|
56
|
+
def todo
|
57
|
+
Tapioca.silence_warnings do
|
58
|
+
generator.build_todos
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
55
62
|
desc "generate [gem...]", "generate RBIs from gems"
|
56
63
|
def generate(*gems)
|
57
64
|
Tapioca.silence_warnings do
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'pathname'
|
5
|
+
require 'shellwords'
|
6
|
+
|
7
|
+
module Tapioca
|
8
|
+
module Compilers
|
9
|
+
module Sorbet
|
10
|
+
SORBET = Pathname.new(Gem::Specification.find_by_name("sorbet-static").full_gem_path) / "libexec" / "sorbet"
|
11
|
+
|
12
|
+
class << self
|
13
|
+
extend(T::Sig)
|
14
|
+
|
15
|
+
sig { params(args: String).returns(String) }
|
16
|
+
def run(*args)
|
17
|
+
IO.popen(
|
18
|
+
[
|
19
|
+
sorbet_path,
|
20
|
+
"--quiet",
|
21
|
+
*args,
|
22
|
+
].join(' '),
|
23
|
+
err: "/dev/null"
|
24
|
+
).read
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { returns(String) }
|
28
|
+
def sorbet_path
|
29
|
+
SORBET.to_s.shellescape
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -287,6 +287,7 @@ module Tapioca
|
|
287
287
|
end
|
288
288
|
|
289
289
|
prepends = prepend
|
290
|
+
.reverse
|
290
291
|
.select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
|
291
292
|
.select(&method(:public_module?))
|
292
293
|
.map do |mod|
|
@@ -297,6 +298,7 @@ module Tapioca
|
|
297
298
|
end
|
298
299
|
|
299
300
|
includes = include
|
301
|
+
.reverse
|
300
302
|
.select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
|
301
303
|
.select(&method(:public_module?))
|
302
304
|
.map do |mod|
|
@@ -304,6 +306,7 @@ module Tapioca
|
|
304
306
|
end
|
305
307
|
|
306
308
|
extends = extend
|
309
|
+
.reverse
|
307
310
|
.select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
|
308
311
|
.select(&method(:public_module?))
|
309
312
|
.map do |mod|
|
@@ -2,16 +2,12 @@
|
|
2
2
|
# typed: true
|
3
3
|
|
4
4
|
require 'json'
|
5
|
-
require 'pathname'
|
6
5
|
require 'tempfile'
|
7
|
-
require 'shellwords'
|
8
6
|
|
9
7
|
module Tapioca
|
10
8
|
module Compilers
|
11
9
|
module SymbolTable
|
12
10
|
module SymbolLoader
|
13
|
-
SORBET = Pathname.new(Gem::Specification.find_by_name("sorbet-static").full_gem_path) / "libexec" / "sorbet"
|
14
|
-
|
15
11
|
class << self
|
16
12
|
extend(T::Sig)
|
17
13
|
|
@@ -53,22 +49,7 @@ module Tapioca
|
|
53
49
|
end
|
54
50
|
|
55
51
|
def symbol_table_json_from(input, table_type: "symbol-table-json")
|
56
|
-
|
57
|
-
[
|
58
|
-
sorbet_path,
|
59
|
-
# We don't want to pick up any sorbet/config files in cwd
|
60
|
-
"--no-config",
|
61
|
-
"--print=#{table_type}",
|
62
|
-
"--quiet",
|
63
|
-
input,
|
64
|
-
].join(' '),
|
65
|
-
err: "/dev/null"
|
66
|
-
).read
|
67
|
-
end
|
68
|
-
|
69
|
-
sig { returns(String) }
|
70
|
-
def sorbet_path
|
71
|
-
SORBET.to_s.shellescape
|
52
|
+
Tapioca::Compilers::Sorbet.run("--no-config", "--print=#{table_type}", input)
|
72
53
|
end
|
73
54
|
end
|
74
55
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: strong
|
3
|
+
|
4
|
+
module Tapioca
|
5
|
+
module Compilers
|
6
|
+
# Taken from https://github.com/sorbet/sorbet/blob/master/gems/sorbet/lib/todo-rbi.rb
|
7
|
+
class TodosCompiler
|
8
|
+
extend(T::Sig)
|
9
|
+
|
10
|
+
sig do
|
11
|
+
returns(String)
|
12
|
+
end
|
13
|
+
def compile
|
14
|
+
list_todos.each_line.map do |line|
|
15
|
+
next if line.include?("<") || line.include?("class_of")
|
16
|
+
"module #{line.strip.gsub('T.untyped::', '')}; end"
|
17
|
+
end.compact.join("\n")
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
sig { returns(String) }
|
23
|
+
def list_todos
|
24
|
+
Tapioca::Compilers::Sorbet.run(
|
25
|
+
"--print=missing-constants",
|
26
|
+
"--stdout-hup-hack",
|
27
|
+
"--no-error-count"
|
28
|
+
).strip
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/tapioca/config.rb
CHANGED
@@ -11,11 +11,12 @@ module Tapioca
|
|
11
11
|
const(:generate_command, String)
|
12
12
|
const(:exclude, T::Array[String])
|
13
13
|
const(:typed_overrides, T::Hash[String, String])
|
14
|
+
const(:todos_path, String)
|
14
15
|
|
15
16
|
sig { returns(Pathname) }
|
16
17
|
def outpath
|
17
|
-
@outpath
|
18
|
-
|
18
|
+
@outpath = T.let(@outpath, T.nilable(Pathname))
|
19
|
+
@outpath ||= Pathname.new(outdir)
|
19
20
|
end
|
20
21
|
|
21
22
|
private_class_method :new
|
@@ -24,11 +25,13 @@ module Tapioca
|
|
24
25
|
SORBET_CONFIG = "sorbet/config"
|
25
26
|
|
26
27
|
DEFAULT_POSTREQUIRE = "sorbet/tapioca/require.rb"
|
27
|
-
|
28
|
+
DEFAULT_RBIDIR = "sorbet/rbi"
|
29
|
+
DEFAULT_OUTDIR = T.let("#{DEFAULT_RBIDIR}/gems", String)
|
28
30
|
DEFAULT_OVERRIDES = T.let({
|
29
31
|
# ActiveSupport overrides some core methods with different signatures
|
30
32
|
# so we generate a typed: false RBI for it to suppress errors
|
31
33
|
"activesupport" => "false",
|
32
34
|
}.freeze, T::Hash[String, String])
|
35
|
+
DEFAULT_TODOSPATH = T.let("#{DEFAULT_RBIDIR}/todo.rbi", String)
|
33
36
|
end
|
34
37
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'yaml'
|
5
|
+
|
4
6
|
module Tapioca
|
5
7
|
class ConfigBuilder
|
6
8
|
class << self
|
@@ -57,6 +59,7 @@ module Tapioca
|
|
57
59
|
"generate_command" => default_command,
|
58
60
|
"exclude" => [],
|
59
61
|
"typed_overrides" => Config::DEFAULT_OVERRIDES,
|
62
|
+
"todos_path" => Config::DEFAULT_TODOSPATH,
|
60
63
|
}.freeze, T::Hash[String, T.untyped])
|
61
64
|
end
|
62
65
|
end
|
data/lib/tapioca/generator.rb
CHANGED
@@ -44,6 +44,38 @@ module Tapioca
|
|
44
44
|
say("Please review changes and commit them.", [:green, :bold])
|
45
45
|
end
|
46
46
|
|
47
|
+
sig { void }
|
48
|
+
def build_todos
|
49
|
+
todos_path = config.todos_path
|
50
|
+
compiler = Compilers::TodosCompiler.new
|
51
|
+
name = set_color(todos_path, :yellow, :bold)
|
52
|
+
say("Compiling #{name}, this may take a few seconds... ")
|
53
|
+
|
54
|
+
# Clean all existing unresolved constants before regenerating the list
|
55
|
+
# so Sorbet won't grab them as already resolved.
|
56
|
+
File.delete(todos_path) if File.exist?(todos_path)
|
57
|
+
|
58
|
+
rbi_string = compiler.compile
|
59
|
+
if rbi_string.empty?
|
60
|
+
say("Nothing to do", :green)
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
content = String.new
|
65
|
+
content << rbi_header(config.generate_command, "false")
|
66
|
+
content << rbi_string
|
67
|
+
content << "\n"
|
68
|
+
|
69
|
+
outdir = File.dirname(todos_path)
|
70
|
+
FileUtils.mkdir_p(outdir)
|
71
|
+
File.write(todos_path, content)
|
72
|
+
|
73
|
+
say("Done", :green)
|
74
|
+
|
75
|
+
say("All unresolved constants have been written to #{name}.", [:green, :bold])
|
76
|
+
say("Please review changes and commit them.", [:green, :bold])
|
77
|
+
end
|
78
|
+
|
47
79
|
sig { void }
|
48
80
|
def sync_rbis_with_gemfile
|
49
81
|
anything_done = [
|
data/lib/tapioca/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tapioca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ufuk Kayserilioglu
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2020-
|
14
|
+
date: 2020-05-08 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: pry
|
@@ -139,9 +139,11 @@ files:
|
|
139
139
|
- exe/tapioca
|
140
140
|
- lib/tapioca.rb
|
141
141
|
- lib/tapioca/cli.rb
|
142
|
+
- lib/tapioca/compilers/sorbet.rb
|
142
143
|
- lib/tapioca/compilers/symbol_table/symbol_generator.rb
|
143
144
|
- lib/tapioca/compilers/symbol_table/symbol_loader.rb
|
144
145
|
- lib/tapioca/compilers/symbol_table_compiler.rb
|
146
|
+
- lib/tapioca/compilers/todos_compiler.rb
|
145
147
|
- lib/tapioca/config.rb
|
146
148
|
- lib/tapioca/config_builder.rb
|
147
149
|
- lib/tapioca/constant_locator.rb
|