tapioca 0.4.15 → 0.4.20
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 +4 -2
- data/exe/tapioca +17 -2
- data/lib/tapioca.rb +1 -27
- data/lib/tapioca/cli.rb +1 -108
- data/lib/tapioca/cli/main.rb +146 -0
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +4 -4
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +1 -1
- data/lib/tapioca/compilers/dsl/base.rb +1 -1
- data/lib/tapioca/compilers/dsl/url_helpers.rb +3 -3
- data/lib/tapioca/compilers/sorbet.rb +4 -1
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +145 -39
- data/lib/tapioca/config.rb +1 -1
- data/lib/tapioca/config_builder.rb +7 -12
- data/lib/tapioca/generator.rb +183 -28
- data/lib/tapioca/generic_type_registry.rb +219 -0
- data/lib/tapioca/internal.rb +21 -0
- data/lib/tapioca/loader.rb +13 -2
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +66 -0
- data/lib/tapioca/sorbet_ext/name_patch.rb +16 -0
- data/lib/tapioca/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 567eff35f7ee24b52dafcd45ceb40ccaf042b6e0661c13cc5dcb87228c21c792
|
4
|
+
data.tar.gz: 0c5143d8407d5583ccd80edbb047fb0b7500b2de8bba1717723a92f97994c045
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d82321ab3b2516a4410825a6dfe4b253d7cccf49abc8c62db0b2a8cdbe13f064aa43df759108685ba06c36073c7a59cfda4ff361cdad1bfc736d7e557ae3ada6
|
7
|
+
data.tar.gz: 0b9bd1ff91ca97cbbb1922e9915a8d046421ff40f3cd2774b4518c979f44635507b48fcce93a904770b1a65e7214a9897ce5444ce26ee6d532523a595e24ec96
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
> :warning: **Note**: This software is currently under active development. The API and interface should be considered unstable until a v1.0.0 release.
|
2
|
+
|
1
3
|
# Tapioca
|
2
4
|
|
3
5
|

|
@@ -14,7 +16,7 @@ For gems that have a normal default `require` and load all of their constants th
|
|
14
16
|
|
15
17
|
For example, suppose you are using the class `BetterHtml::Parser` exported from the `better_html` gem. Just doing a `require "better_html"` (which is the default require) does not load that type:
|
16
18
|
|
17
|
-
```
|
19
|
+
```shell
|
18
20
|
$ bundle exec pry
|
19
21
|
[1] pry(main)> require 'better_html'
|
20
22
|
=> true
|
@@ -110,7 +112,7 @@ This will generate DSL RBIs for specified constants (or for all handled constant
|
|
110
112
|
- `--prerequire [file]`: A file to be required before `Bundler.require` is called.
|
111
113
|
- `--postrequire [file]`: A file to be required after `Bundler.require` is called.
|
112
114
|
- `--out [directory]`: The output directory for generated RBI files, default to `sorbet/rbi/gems`.
|
113
|
-
- `--generate-command [command]`: The command to run to regenerate RBI files (used in header comment of the RBI files), defaults to the current command.
|
115
|
+
- `--generate-command [command]`: **[DEPRECATED]** The command to run to regenerate RBI files (used in header comment of the RBI files), defaults to the current command.
|
114
116
|
- `--typed-overrides [gem:level]`: Overrides typed sigils for generated gem RBIs for gem `gem` to level `level` (`level` can be one of `ignore`, `false`, `true`, `strict`, or `strong`, see [the Sorbet docs](https://sorbet.org/docs/static#file-level-granularity-strictness-levels) for more details).
|
115
117
|
|
116
118
|
## Contributing
|
data/exe/tapioca
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
require 'sorbet-runtime'
|
5
5
|
|
6
|
-
|
6
|
+
begin
|
7
|
+
T::Configuration.default_checked_level = :never
|
8
|
+
# Suppresses errors caused by T.cast, T.let, T.must, etc.
|
9
|
+
T::Configuration.inline_type_error_handler = ->(*) {}
|
10
|
+
# Suppresses errors caused by incorrect parameter ordering
|
11
|
+
T::Configuration.sig_validation_error_handler = ->(*) {}
|
12
|
+
rescue
|
13
|
+
# Need this rescue so that if another gem has
|
14
|
+
# already set the checked level by the time we
|
15
|
+
# get to it, we don't fail outright.
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
require_relative "../lib/tapioca/internal"
|
20
|
+
|
21
|
+
Tapioca::Cli::Main.start(ARGV)
|
data/lib/tapioca.rb
CHANGED
@@ -17,31 +17,5 @@ module Tapioca
|
|
17
17
|
class Error < StandardError; end
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
T::Configuration.default_checked_level = :never
|
22
|
-
# Suppresses errors caused by T.cast, T.let, T.must, etc.
|
23
|
-
T::Configuration.inline_type_error_handler = ->(*) {}
|
24
|
-
# Suppresses errors caused by incorrect parameter ordering
|
25
|
-
T::Configuration.sig_validation_error_handler = ->(*) {}
|
26
|
-
rescue
|
27
|
-
# Need this rescue so that if another gem has
|
28
|
-
# already set the checked level by the time we
|
29
|
-
# get to it, we don't fail outright.
|
30
|
-
nil
|
31
|
-
end
|
32
|
-
|
33
|
-
require "tapioca/loader"
|
34
|
-
require "tapioca/constant_locator"
|
35
|
-
require "tapioca/config"
|
36
|
-
require "tapioca/config_builder"
|
37
|
-
require "tapioca/generator"
|
38
|
-
require "tapioca/cli"
|
39
|
-
require "tapioca/gemfile"
|
40
|
-
require "tapioca/compilers/sorbet"
|
41
|
-
require "tapioca/compilers/requires_compiler"
|
42
|
-
require "tapioca/compilers/symbol_table_compiler"
|
43
|
-
require "tapioca/compilers/symbol_table/symbol_generator"
|
44
|
-
require "tapioca/compilers/symbol_table/symbol_loader"
|
45
|
-
require "tapioca/compilers/todos_compiler"
|
46
|
-
require "tapioca/compilers/dsl_compiler"
|
20
|
+
require "tapioca/compilers/dsl/base"
|
47
21
|
require "tapioca/version"
|
data/lib/tapioca/cli.rb
CHANGED
@@ -4,112 +4,5 @@
|
|
4
4
|
require 'thor'
|
5
5
|
|
6
6
|
module Tapioca
|
7
|
-
|
8
|
-
include(Thor::Actions)
|
9
|
-
|
10
|
-
class_option :prerequire,
|
11
|
-
aliases: ["--pre", "-b"],
|
12
|
-
banner: "file",
|
13
|
-
desc: "A file to be required before Bundler.require is called"
|
14
|
-
class_option :postrequire,
|
15
|
-
aliases: ["--post", "-a"],
|
16
|
-
banner: "file",
|
17
|
-
desc: "A file to be required after Bundler.require is called"
|
18
|
-
class_option :outdir,
|
19
|
-
aliases: ["--out", "-o"],
|
20
|
-
banner: "directory",
|
21
|
-
desc: "The output directory for generated RBI files"
|
22
|
-
class_option :generate_command,
|
23
|
-
aliases: ["--cmd", "-c"],
|
24
|
-
banner: "command",
|
25
|
-
desc: "The command to run to regenerate RBI files"
|
26
|
-
class_option :exclude,
|
27
|
-
aliases: ["-x"],
|
28
|
-
type: :array,
|
29
|
-
banner: "gem [gem ...]",
|
30
|
-
desc: "Excludes the given gem(s) from RBI generation"
|
31
|
-
class_option :typed_overrides,
|
32
|
-
aliases: ["--typed", "-t"],
|
33
|
-
type: :hash,
|
34
|
-
banner: "gem:level [gem:level ...]",
|
35
|
-
desc: "Overrides for typed sigils for generated gem RBIs"
|
36
|
-
|
37
|
-
map T.unsafe(%w[--version -v] => :__print_version)
|
38
|
-
|
39
|
-
desc "init", "initializes folder structure"
|
40
|
-
def init
|
41
|
-
create_file(Config::SORBET_CONFIG, skip: true) do
|
42
|
-
<<~CONTENT
|
43
|
-
--dir
|
44
|
-
.
|
45
|
-
CONTENT
|
46
|
-
end
|
47
|
-
create_file(Config::DEFAULT_POSTREQUIRE, skip: true) do
|
48
|
-
<<~CONTENT
|
49
|
-
# typed: false
|
50
|
-
# frozen_string_literal: true
|
51
|
-
|
52
|
-
# Add your extra requires here
|
53
|
-
CONTENT
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
desc "require", "generate the list of files to be required by tapioca"
|
58
|
-
def require
|
59
|
-
Tapioca.silence_warnings do
|
60
|
-
generator.build_requires
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
desc "todo", "generate the list of unresolved constants"
|
65
|
-
def todo
|
66
|
-
Tapioca.silence_warnings do
|
67
|
-
generator.build_todos
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
desc "dsl [constant...]", "generate RBIs for dynamic methods"
|
72
|
-
option :generators,
|
73
|
-
type: :array,
|
74
|
-
aliases: ["--gen", "-g"],
|
75
|
-
banner: "generator [generator ...]",
|
76
|
-
desc: "Only run supplied DSL generators"
|
77
|
-
def dsl(*constants)
|
78
|
-
Tapioca.silence_warnings do
|
79
|
-
generator.build_dsl(constants)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
desc "generate [gem...]", "generate RBIs from gems"
|
84
|
-
def generate(*gems)
|
85
|
-
Tapioca.silence_warnings do
|
86
|
-
generator.build_gem_rbis(gems)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
desc "sync", "sync RBIs to Gemfile"
|
91
|
-
def sync
|
92
|
-
Tapioca.silence_warnings do
|
93
|
-
generator.sync_rbis_with_gemfile
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
desc "--version, -v", "show version"
|
98
|
-
def __print_version
|
99
|
-
puts "Tapioca v#{Tapioca::VERSION}"
|
100
|
-
end
|
101
|
-
|
102
|
-
no_commands do
|
103
|
-
def self.exit_on_failure?
|
104
|
-
true
|
105
|
-
end
|
106
|
-
|
107
|
-
def generator
|
108
|
-
current_command = T.must(current_command_chain.first)
|
109
|
-
@generator ||= Generator.new(
|
110
|
-
ConfigBuilder.from_options(current_command, options)
|
111
|
-
)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
7
|
+
module Cli; end
|
115
8
|
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Tapioca
|
5
|
+
module Cli
|
6
|
+
class Main < Thor
|
7
|
+
include(Thor::Actions)
|
8
|
+
|
9
|
+
class_option :prerequire,
|
10
|
+
aliases: ["--pre", "-b"],
|
11
|
+
banner: "file",
|
12
|
+
desc: "A file to be required before Bundler.require is called"
|
13
|
+
class_option :postrequire,
|
14
|
+
aliases: ["--post", "-a"],
|
15
|
+
banner: "file",
|
16
|
+
desc: "A file to be required after Bundler.require is called"
|
17
|
+
class_option :outdir,
|
18
|
+
aliases: ["--out", "-o"],
|
19
|
+
banner: "directory",
|
20
|
+
desc: "The output directory for generated RBI files"
|
21
|
+
class_option :generate_command,
|
22
|
+
aliases: ["--cmd", "-c"],
|
23
|
+
banner: "command",
|
24
|
+
desc: "The command to run to regenerate RBI files"
|
25
|
+
class_option :exclude,
|
26
|
+
aliases: ["-x"],
|
27
|
+
type: :array,
|
28
|
+
banner: "gem [gem ...]",
|
29
|
+
desc: "Excludes the given gem(s) from RBI generation"
|
30
|
+
class_option :typed_overrides,
|
31
|
+
aliases: ["--typed", "-t"],
|
32
|
+
type: :hash,
|
33
|
+
banner: "gem:level [gem:level ...]",
|
34
|
+
desc: "Overrides for typed sigils for generated gem RBIs"
|
35
|
+
|
36
|
+
map T.unsafe(%w[--version -v] => :__print_version)
|
37
|
+
|
38
|
+
desc "init", "initializes folder structure"
|
39
|
+
def init
|
40
|
+
create_config
|
41
|
+
create_post_require
|
42
|
+
generate_binstub
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "require", "generate the list of files to be required by tapioca"
|
46
|
+
def require
|
47
|
+
Tapioca.silence_warnings do
|
48
|
+
generator.build_requires
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "todo", "generate the list of unresolved constants"
|
53
|
+
def todo
|
54
|
+
Tapioca.silence_warnings do
|
55
|
+
generator.build_todos
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "dsl [constant...]", "generate RBIs for dynamic methods"
|
60
|
+
option :generators,
|
61
|
+
type: :array,
|
62
|
+
aliases: ["--gen", "-g"],
|
63
|
+
banner: "generator [generator ...]",
|
64
|
+
desc: "Only run supplied DSL generators"
|
65
|
+
option :verify,
|
66
|
+
type: :boolean,
|
67
|
+
default: false,
|
68
|
+
desc: "Verifies RBIs are up-to-date"
|
69
|
+
option :quiet,
|
70
|
+
aliases: ["-q"],
|
71
|
+
type: :boolean,
|
72
|
+
desc: "Supresses file creation output"
|
73
|
+
def dsl(*constants)
|
74
|
+
Tapioca.silence_warnings do
|
75
|
+
generator.build_dsl(constants, should_verify: options[:verify], quiet: options[:quiet])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
desc "generate [gem...]", "generate RBIs from gems"
|
80
|
+
def generate(*gems)
|
81
|
+
Tapioca.silence_warnings do
|
82
|
+
generator.build_gem_rbis(gems)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
desc "sync", "sync RBIs to Gemfile"
|
87
|
+
def sync
|
88
|
+
Tapioca.silence_warnings do
|
89
|
+
generator.sync_rbis_with_gemfile
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
desc "--version, -v", "show version"
|
94
|
+
def __print_version
|
95
|
+
puts "Tapioca v#{Tapioca::VERSION}"
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def create_config
|
101
|
+
create_file(Config::SORBET_CONFIG, skip: true) do
|
102
|
+
<<~CONTENT
|
103
|
+
--dir
|
104
|
+
.
|
105
|
+
CONTENT
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_post_require
|
110
|
+
create_file(Config::DEFAULT_POSTREQUIRE, skip: true) do
|
111
|
+
<<~CONTENT
|
112
|
+
# typed: false
|
113
|
+
# frozen_string_literal: true
|
114
|
+
|
115
|
+
# Add your extra requires here
|
116
|
+
CONTENT
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def generate_binstub
|
121
|
+
bin_stub_exists = File.exist?("bin/tapioca")
|
122
|
+
installer = Bundler::Installer.new(Bundler.root, Bundler.definition)
|
123
|
+
spec = Bundler.definition.specs.find { |s| s.name == "tapioca" }
|
124
|
+
installer.generate_bundler_executable_stubs(spec, { force: true })
|
125
|
+
if bin_stub_exists
|
126
|
+
shell.say_status(:force, "bin/tapioca", :yellow)
|
127
|
+
else
|
128
|
+
shell.say_status(:create, "bin/tapioca", :green)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
no_commands do
|
133
|
+
def self.exit_on_failure?
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
137
|
+
def generator
|
138
|
+
current_command = T.must(current_command_chain.first)
|
139
|
+
@generator ||= Generator.new(
|
140
|
+
ConfigBuilder.from_options(current_command, options)
|
141
|
+
)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -371,7 +371,6 @@ module Tapioca
|
|
371
371
|
return unless signature
|
372
372
|
|
373
373
|
return_type = signature.return_type
|
374
|
-
return if T::Types::Simple === return_type && T::Generic === return_type.raw_type
|
375
374
|
return if return_type == T::Private::Types::Void || return_type == T::Private::Types::NotTyped
|
376
375
|
|
377
376
|
return_type.to_s
|
@@ -382,10 +381,11 @@ module Tapioca
|
|
382
381
|
signature = T::Private::Methods.signature_for_method(column_type.method(method))
|
383
382
|
return unless signature
|
384
383
|
|
385
|
-
|
386
|
-
|
384
|
+
# Arg types is an array [name, type] entries, so we desctructure the type of
|
385
|
+
# first argument to get the first argument type
|
386
|
+
_, first_argument_type = signature.arg_types.first
|
387
387
|
|
388
|
-
|
388
|
+
first_argument_type.to_s
|
389
389
|
end
|
390
390
|
|
391
391
|
sig { params(type: String).returns(String) }
|
@@ -123,7 +123,7 @@ module Tapioca
|
|
123
123
|
# Compile a Ruby method return type into a Parlour type
|
124
124
|
sig do
|
125
125
|
params(method_def: T.any(Method, UnboundMethod))
|
126
|
-
.returns(String)
|
126
|
+
.returns(T.nilable(String))
|
127
127
|
end
|
128
128
|
def compile_method_return_type_to_parlour(method_def)
|
129
129
|
signature = T::Private::Methods.signature_for_method(method_def)
|
@@ -89,7 +89,7 @@ module Tapioca
|
|
89
89
|
class UrlHelpers < Base
|
90
90
|
extend T::Sig
|
91
91
|
|
92
|
-
sig { override.params(root: Parlour::RbiGenerator::Namespace, constant:
|
92
|
+
sig { override.params(root: Parlour::RbiGenerator::Namespace, constant: Module).void }
|
93
93
|
def decorate(root, constant)
|
94
94
|
case constant
|
95
95
|
when GeneratedPathHelpersModule.singleton_class, GeneratedUrlHelpersModule.singleton_class
|
@@ -127,7 +127,7 @@ module Tapioca
|
|
127
127
|
|
128
128
|
private
|
129
129
|
|
130
|
-
sig { params(root: Parlour::RbiGenerator::Namespace, constant:
|
130
|
+
sig { params(root: Parlour::RbiGenerator::Namespace, constant: Module).void }
|
131
131
|
def generate_module_for(root, constant)
|
132
132
|
root.create_module(T.must(constant.name)) do |mod|
|
133
133
|
mod.create_include("::ActionDispatch::Routing::UrlFor")
|
@@ -143,7 +143,7 @@ module Tapioca
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
-
sig { params(mod: Parlour::RbiGenerator::Namespace, constant:
|
146
|
+
sig { params(mod: Parlour::RbiGenerator::Namespace, constant: Module, helper_module: Module).void }
|
147
147
|
def create_mixins_for(mod, constant, helper_module)
|
148
148
|
include_helper = constant.ancestors.include?(helper_module) || NON_DISCOVERABLE_INCLUDERS.include?(constant)
|
149
149
|
extend_helper = constant.singleton_class.ancestors.include?(helper_module)
|
@@ -8,6 +8,7 @@ module Tapioca
|
|
8
8
|
module Compilers
|
9
9
|
module Sorbet
|
10
10
|
SORBET = Pathname.new(Gem::Specification.find_by_name("sorbet-static").full_gem_path) / "libexec" / "sorbet"
|
11
|
+
EXE_PATH_ENV_VAR = "TAPIOCA_SORBET_EXE"
|
11
12
|
|
12
13
|
class << self
|
13
14
|
extend(T::Sig)
|
@@ -26,7 +27,9 @@ module Tapioca
|
|
26
27
|
|
27
28
|
sig { returns(String) }
|
28
29
|
def sorbet_path
|
29
|
-
SORBET
|
30
|
+
sorbet_path = ENV.fetch(EXE_PATH_ENV_VAR, SORBET)
|
31
|
+
sorbet_path = SORBET if sorbet_path.empty?
|
32
|
+
sorbet_path.to_s.shellescape
|
30
33
|
end
|
31
34
|
end
|
32
35
|
end
|
@@ -74,9 +74,15 @@ module Tapioca
|
|
74
74
|
compile(symbol, constant)
|
75
75
|
end
|
76
76
|
|
77
|
-
sig
|
78
|
-
|
79
|
-
|
77
|
+
sig do
|
78
|
+
params(
|
79
|
+
symbol: String,
|
80
|
+
inherit: T::Boolean,
|
81
|
+
namespace: Module
|
82
|
+
).returns(BasicObject).checked(:never)
|
83
|
+
end
|
84
|
+
def resolve_constant(symbol, inherit: false, namespace: Object)
|
85
|
+
namespace.const_get(symbol, inherit)
|
80
86
|
rescue NameError, LoadError, RuntimeError, ArgumentError, TypeError
|
81
87
|
nil
|
82
88
|
end
|
@@ -95,6 +101,7 @@ module Tapioca
|
|
95
101
|
return if alias_namespaced?(name)
|
96
102
|
return if seen?(name)
|
97
103
|
return unless parent_declares_constant?(name)
|
104
|
+
return if T::Enum === constant # T::Enum instances are defined via `compile_enums`
|
98
105
|
|
99
106
|
mark_seen(name)
|
100
107
|
compile_constant(name, constant)
|
@@ -141,9 +148,11 @@ module Tapioca
|
|
141
148
|
def compile_object(name, value)
|
142
149
|
return if symbol_ignored?(name)
|
143
150
|
klass = class_of(value)
|
144
|
-
|
151
|
+
klass_name = name_of(klass)
|
152
|
+
|
153
|
+
return if klass_name&.start_with?("T::Types::", "T::Private::")
|
145
154
|
|
146
|
-
type_name = public_module?(klass) &&
|
155
|
+
type_name = public_module?(klass) && klass_name || "T.untyped"
|
147
156
|
indented("#{name} = T.let(T.unsafe(nil), #{type_name})")
|
148
157
|
end
|
149
158
|
|
@@ -180,15 +189,17 @@ module Tapioca
|
|
180
189
|
|
181
190
|
[
|
182
191
|
compile_module_helpers(constant),
|
192
|
+
compile_type_variables(constant),
|
183
193
|
compile_mixins(constant),
|
184
194
|
compile_mixes_in_class_methods(constant),
|
185
195
|
compile_props(constant),
|
196
|
+
compile_enums(constant),
|
186
197
|
methods,
|
187
|
-
].select { |b| b
|
198
|
+
].select { |b| b && !b.empty? }.join("\n\n")
|
188
199
|
end
|
189
200
|
end
|
190
201
|
|
191
|
-
sig { params(constant: Module).returns(String) }
|
202
|
+
sig { params(constant: Module).returns(T.nilable(String)) }
|
192
203
|
def compile_module_helpers(constant)
|
193
204
|
abstract_type = T::Private::Abstract::Data.get(constant, :abstract_type)
|
194
205
|
|
@@ -197,12 +208,14 @@ module Tapioca
|
|
197
208
|
helpers << indented("final!") if T::Private::Final.final_module?(constant)
|
198
209
|
helpers << indented("sealed!") if T::Private::Sealed.sealed_module?(constant)
|
199
210
|
|
211
|
+
return if helpers.empty?
|
212
|
+
|
200
213
|
helpers.join("\n")
|
201
214
|
end
|
202
215
|
|
203
|
-
sig { params(constant: Module).returns(String) }
|
216
|
+
sig { params(constant: Module).returns(T.nilable(String)) }
|
204
217
|
def compile_props(constant)
|
205
|
-
return
|
218
|
+
return unless T::Props::ClassMethods === constant
|
206
219
|
|
207
220
|
constant.props.map do |name, prop|
|
208
221
|
method = "prop"
|
@@ -217,6 +230,23 @@ module Tapioca
|
|
217
230
|
end.join("\n")
|
218
231
|
end
|
219
232
|
|
233
|
+
sig { params(constant: Module).returns(T.nilable(String)) }
|
234
|
+
def compile_enums(constant)
|
235
|
+
return unless T::Enum > constant
|
236
|
+
|
237
|
+
enums = T.unsafe(constant).values.map do |enum_type|
|
238
|
+
enum_type.instance_variable_get(:@const_name).to_s
|
239
|
+
end
|
240
|
+
|
241
|
+
content = [
|
242
|
+
indented('enums do'),
|
243
|
+
*enums.map { |e| indented(" #{e} = new") }.join("\n"),
|
244
|
+
indented('end'),
|
245
|
+
]
|
246
|
+
|
247
|
+
content.join("\n")
|
248
|
+
end
|
249
|
+
|
220
250
|
sig { params(name: String, constant: Module).returns(T.nilable(String)) }
|
221
251
|
def compile_subconstants(name, constant)
|
222
252
|
output = constants_of(constant).sort.uniq.map do |constant_name|
|
@@ -231,11 +261,71 @@ module Tapioca
|
|
231
261
|
compile(symbol, subconstant)
|
232
262
|
end.compact
|
233
263
|
|
234
|
-
return
|
264
|
+
return if output.empty?
|
235
265
|
|
236
266
|
"\n" + output.join("\n\n")
|
237
267
|
end
|
238
268
|
|
269
|
+
sig { params(constant: Module).returns(T.nilable(String)) }
|
270
|
+
def compile_type_variables(constant)
|
271
|
+
type_variables = compile_type_variable_declarations(constant)
|
272
|
+
singleton_class_type_variables = compile_type_variable_declarations(singleton_class_of(constant))
|
273
|
+
|
274
|
+
return if !type_variables && !singleton_class_type_variables
|
275
|
+
|
276
|
+
type_variables += "\n" if type_variables
|
277
|
+
singleton_class_type_variables += "\n" if singleton_class_type_variables
|
278
|
+
|
279
|
+
[
|
280
|
+
type_variables,
|
281
|
+
singleton_class_type_variables,
|
282
|
+
].compact.join("\n").rstrip
|
283
|
+
end
|
284
|
+
|
285
|
+
sig { params(constant: Module).returns(T.nilable(String)) }
|
286
|
+
def compile_type_variable_declarations(constant)
|
287
|
+
with_indentation_for_constant(constant) do
|
288
|
+
# Try to find the type variables defined on this constant, bail if we can't
|
289
|
+
type_variables = GenericTypeRegistry.lookup_type_variables(constant)
|
290
|
+
return unless type_variables
|
291
|
+
|
292
|
+
# Create a map of subconstants (via their object ids) to their names.
|
293
|
+
# We need this later when we want to lookup the name of the registered type
|
294
|
+
# variable via the value of the type variable constant.
|
295
|
+
subconstant_to_name_lookup = constants_of(constant).map do |constant_name|
|
296
|
+
[
|
297
|
+
object_id_of(resolve_constant(constant_name.to_s, namespace: constant)),
|
298
|
+
constant_name,
|
299
|
+
]
|
300
|
+
end.to_h
|
301
|
+
|
302
|
+
# Map each type variable to its string representation.
|
303
|
+
#
|
304
|
+
# Each entry of `type_variables` maps an object_id to a String,
|
305
|
+
# and the order they are inserted into the hash is the order they should be
|
306
|
+
# defined in the source code.
|
307
|
+
#
|
308
|
+
# By looping over these entries and then getting the actual constant name
|
309
|
+
# from the `subconstant_to_name_lookup` we defined above, gives us all the
|
310
|
+
# information we need to serialize type variable definitions.
|
311
|
+
type_variable_declarations = type_variables.map do |type_variable_id, serialized_type_variable|
|
312
|
+
constant_name = subconstant_to_name_lookup[type_variable_id]
|
313
|
+
# Here, we know that constant_value will be an instance of
|
314
|
+
# T::Types::CustomTypeVariable, which knows how to serialize
|
315
|
+
# itself to a type_member/type_template
|
316
|
+
indented("#{constant_name} = #{serialized_type_variable}")
|
317
|
+
end.compact
|
318
|
+
|
319
|
+
return if type_variable_declarations.empty?
|
320
|
+
|
321
|
+
[
|
322
|
+
indented("extend T::Generic"),
|
323
|
+
"",
|
324
|
+
*type_variable_declarations,
|
325
|
+
].compact.join("\n")
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
239
329
|
sig { params(constant: Class).returns(String) }
|
240
330
|
def compile_superclass(constant)
|
241
331
|
superclass = T.let(nil, T.nilable(Class)) # rubocop:disable Lint/UselessAssignment
|
@@ -345,9 +435,13 @@ module Tapioca
|
|
345
435
|
end
|
346
436
|
|
347
437
|
define_singleton_method(:include) do |mod|
|
348
|
-
|
349
|
-
|
350
|
-
|
438
|
+
begin
|
439
|
+
before = singleton_class.ancestors
|
440
|
+
super(mod).tap do
|
441
|
+
mixins_from_modules[mod] = singleton_class.ancestors - before
|
442
|
+
end
|
443
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
444
|
+
# this is a best effort, bail if we can't perform this
|
351
445
|
end
|
352
446
|
end
|
353
447
|
|
@@ -408,29 +502,19 @@ module Tapioca
|
|
408
502
|
instance_methods = compile_directly_owned_methods(name, constant)
|
409
503
|
singleton_methods = compile_directly_owned_methods(name, singleton_class_of(constant))
|
410
504
|
|
411
|
-
return if symbol_ignored?(name) && instance_methods
|
505
|
+
return if symbol_ignored?(name) && !instance_methods && !singleton_methods
|
412
506
|
|
413
507
|
[
|
414
|
-
initialize_method
|
508
|
+
initialize_method,
|
415
509
|
instance_methods,
|
416
510
|
singleton_methods,
|
417
|
-
].select { |b| b.strip
|
511
|
+
].select { |b| b && !b.strip.empty? }.join("\n\n")
|
418
512
|
end
|
419
513
|
|
420
|
-
sig { params(module_name: String, mod: Module, for_visibility: T::Array[Symbol]).returns(String) }
|
514
|
+
sig { params(module_name: String, mod: Module, for_visibility: T::Array[Symbol]).returns(T.nilable(String)) }
|
421
515
|
def compile_directly_owned_methods(module_name, mod, for_visibility = [:public, :protected, :private])
|
422
|
-
|
423
|
-
|
424
|
-
postamble = nil
|
425
|
-
|
426
|
-
if mod.singleton_class?
|
427
|
-
indent_step = 1
|
428
|
-
preamble = indented("class << self")
|
429
|
-
postamble = indented("end")
|
430
|
-
end
|
431
|
-
|
432
|
-
methods = with_indentation(indent_step) do
|
433
|
-
method_names_by_visibility(mod)
|
516
|
+
with_indentation_for_constant(mod) do
|
517
|
+
methods = method_names_by_visibility(mod)
|
434
518
|
.delete_if { |visibility, _method_list| !for_visibility.include?(visibility) }
|
435
519
|
.flat_map do |visibility, method_list|
|
436
520
|
compiled = method_list.sort!.map do |name|
|
@@ -447,16 +531,11 @@ module Tapioca
|
|
447
531
|
compiled
|
448
532
|
end
|
449
533
|
.compact
|
450
|
-
.join("\n")
|
451
|
-
end
|
452
534
|
|
453
|
-
|
535
|
+
return if methods.empty?
|
454
536
|
|
455
|
-
|
456
|
-
|
457
|
-
methods,
|
458
|
-
postamble,
|
459
|
-
].compact.join("\n")
|
537
|
+
methods.join("\n")
|
538
|
+
end
|
460
539
|
end
|
461
540
|
|
462
541
|
sig { params(mod: Module).returns(T::Hash[Symbol, T::Array[Symbol]]) }
|
@@ -636,6 +715,33 @@ module Tapioca
|
|
636
715
|
@indent -= 2 * step
|
637
716
|
end
|
638
717
|
|
718
|
+
sig do
|
719
|
+
params(
|
720
|
+
constant: Module,
|
721
|
+
blk: T.proc
|
722
|
+
.returns(T.nilable(String))
|
723
|
+
)
|
724
|
+
.returns(T.nilable(String))
|
725
|
+
end
|
726
|
+
def with_indentation_for_constant(constant, &blk)
|
727
|
+
step = if constant.singleton_class?
|
728
|
+
1
|
729
|
+
else
|
730
|
+
0
|
731
|
+
end
|
732
|
+
|
733
|
+
result = with_indentation(step, &blk)
|
734
|
+
|
735
|
+
return result unless result
|
736
|
+
return result unless constant.singleton_class?
|
737
|
+
|
738
|
+
[
|
739
|
+
indented("class << self"),
|
740
|
+
result,
|
741
|
+
indented("end"),
|
742
|
+
].compact.join("\n")
|
743
|
+
end
|
744
|
+
|
639
745
|
sig { params(str: String).returns(String) }
|
640
746
|
def indented(str)
|
641
747
|
" " * @indent + str
|
@@ -836,12 +942,12 @@ module Tapioca
|
|
836
942
|
nil
|
837
943
|
end
|
838
944
|
|
839
|
-
sig { params(constant:
|
945
|
+
sig { params(constant: T::Types::Base).returns(String) }
|
840
946
|
def type_of(constant)
|
841
947
|
constant.to_s.gsub(/\bAttachedClass\b/, "T.attached_class")
|
842
948
|
end
|
843
949
|
|
844
|
-
sig { params(object:
|
950
|
+
sig { params(object: BasicObject).returns(Integer).checked(:never) }
|
845
951
|
def object_id_of(object)
|
846
952
|
Object.instance_method(:object_id).bind(object).call
|
847
953
|
end
|