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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81caba98bdbde19ffbb78a1860f994a8b5c39a82dcd48832ce0daabcd9f6d77b
4
- data.tar.gz: 803fe5c9b0ac3838f984455adf53fd2a470e13a877ea218d80bb74b2c309afbe
3
+ metadata.gz: 6aedd5a0415cca7e554021e114401254561649803acceed6caaa2cb24b9b4c8c
4
+ data.tar.gz: 376290c5b3cad6f082e9fc186cbb2ed32a9bb239c5c37bc8f9f464c78a2d951e
5
5
  SHA512:
6
- metadata.gz: 75c2b49c410a6b9f558470a5ab6b59f464b41e4b03a88344fd8967d06e3b36b8b29669bd92c8103d106e234a91fb4ead51d617a365059342e03542cfe113c497
7
- data.tar.gz: 4261f1657b92a65ba119f95e1e9be3211af056fe2ba5a5b225b0d920a2adda1fd7619d9e21bb8e034c3946b4cb931d8ef45beebd3bb429c2c5d8446bce09efa8
6
+ metadata.gz: e65c8822f8312788075a84f24d5b9c95221bba2ced2dd877f150a377696fd70eff96bc936fdd3cf482fa6d4ac1b2d86a0201ed91c386c1ee1ce0bd19e8defd9c
7
+ data.tar.gz: bace8a6fca5ca72789c5fdfa121fb7b30bf415e9f04d6c715f43237f4d022c4507a6d6182161c4928a7258f9759c957a22c2559cc73ebc56d093f5864dcd872f
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/Shopify/tapioca.svg?branch=master)](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.
@@ -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"
@@ -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
- IO.popen(
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
@@ -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 ||= T.let(Pathname.new(outdir), T.nilable(Pathname))
18
- T.must(@outpath)
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
- DEFAULT_OUTDIR = "sorbet/rbi/gems"
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
@@ -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 = [
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.3.1"
5
+ VERSION = "0.4.0"
6
6
  end
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.3.1
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-04-29 00:00:00.000000000 Z
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