tapioca 0.4.13 → 0.4.18
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/Gemfile +0 -1
- 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 +136 -0
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +38 -7
- 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 +152 -49
- data/lib/tapioca/config.rb +1 -1
- data/lib/tapioca/config_builder.rb +7 -12
- data/lib/tapioca/gemfile.rb +30 -22
- data/lib/tapioca/generator.rb +127 -24
- data/lib/tapioca/generic_type_registry.rb +170 -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 +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3709ab7f54589bdf97715ffc2cc7c3a98ad703a6e318a264db70429078cdf26
|
4
|
+
data.tar.gz: 3e38366a54f9b3286de28a2b6d557762b2cf564848b8184a76aadc75504a41fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ef5e31a85a71607340ad54e2fff59b1a5bd0d3f81b1481bd4830ca5e73e5ea9ed1504d38808d8bf49a9686276685167a35103769fdb30d66b61362c4d9c072b
|
7
|
+
data.tar.gz: e0b1daab32f024cc5f3f1fc019b5c1dcd040ce0657e59b81d736702e3cb380a9e7265397f108f4787163e50d9b31e793b960cd33f875cf45a061cee17fa1857e
|
data/Gemfile
CHANGED
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,136 @@
|
|
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
|
+
def dsl(*constants)
|
70
|
+
Tapioca.silence_warnings do
|
71
|
+
generator.build_dsl(constants, should_verify: options[:verify])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
desc "generate [gem...]", "generate RBIs from gems"
|
76
|
+
def generate(*gems)
|
77
|
+
Tapioca.silence_warnings do
|
78
|
+
generator.build_gem_rbis(gems)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "sync", "sync RBIs to Gemfile"
|
83
|
+
def sync
|
84
|
+
Tapioca.silence_warnings do
|
85
|
+
generator.sync_rbis_with_gemfile
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
desc "--version, -v", "show version"
|
90
|
+
def __print_version
|
91
|
+
puts "Tapioca v#{Tapioca::VERSION}"
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def create_config
|
97
|
+
create_file(Config::SORBET_CONFIG, skip: true) do
|
98
|
+
<<~CONTENT
|
99
|
+
--dir
|
100
|
+
.
|
101
|
+
CONTENT
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def create_post_require
|
106
|
+
create_file(Config::DEFAULT_POSTREQUIRE, skip: true) do
|
107
|
+
<<~CONTENT
|
108
|
+
# typed: false
|
109
|
+
# frozen_string_literal: true
|
110
|
+
|
111
|
+
# Add your extra requires here
|
112
|
+
CONTENT
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def generate_binstub
|
117
|
+
installer = Bundler::Installer.new(Bundler.root, Bundler.definition)
|
118
|
+
spec = Bundler.definition.specs.find { |s| s.name == "tapioca" }
|
119
|
+
installer.generate_bundler_executable_stubs(spec, { force: true })
|
120
|
+
end
|
121
|
+
|
122
|
+
no_commands do
|
123
|
+
def self.exit_on_failure?
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
def generator
|
128
|
+
current_command = T.must(current_command_chain.first)
|
129
|
+
@generator ||= Generator.new(
|
130
|
+
ConfigBuilder.from_options(current_command, options)
|
131
|
+
)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -24,6 +24,8 @@ module Tapioca
|
|
24
24
|
# belongs_to :category
|
25
25
|
# has_many :comments
|
26
26
|
# has_one :author, class_name: "User"
|
27
|
+
#
|
28
|
+
# accepts_nested_attributes_for :category, :comments, :author
|
27
29
|
# end
|
28
30
|
# ~~~
|
29
31
|
#
|
@@ -44,6 +46,9 @@ module Tapioca
|
|
44
46
|
# sig { params(value: T.nilable(::User)).void }
|
45
47
|
# def author=(value); end
|
46
48
|
#
|
49
|
+
# sig { params(attributes: T.untyped).returns(T.untyped) }
|
50
|
+
# def author_attributes=(attributes); end
|
51
|
+
#
|
47
52
|
# sig { params(args: T.untyped, blk: T.untyped).returns(::User) }
|
48
53
|
# def build_author(*args, &blk); end
|
49
54
|
#
|
@@ -56,6 +61,9 @@ module Tapioca
|
|
56
61
|
# sig { params(value: T.nilable(::Category)).void }
|
57
62
|
# def category=(value); end
|
58
63
|
#
|
64
|
+
# sig { params(attributes: T.untyped).returns(T.untyped) }
|
65
|
+
# def category_attributes=(attributes); end
|
66
|
+
#
|
59
67
|
# sig { returns(T::Array[T.untyped]) }
|
60
68
|
# def comment_ids; end
|
61
69
|
#
|
@@ -68,6 +76,9 @@ module Tapioca
|
|
68
76
|
# sig { params(value: T::Enumerable[::Comment]).void }
|
69
77
|
# def comments=(value); end
|
70
78
|
#
|
79
|
+
# sig { params(attributes: T.untyped).returns(T.untyped) }
|
80
|
+
# def comments_attributes=(attributes); end
|
81
|
+
#
|
71
82
|
# sig { params(args: T.untyped, blk: T.untyped).returns(::User) }
|
72
83
|
# def create_author(*args, &blk); end
|
73
84
|
#
|
@@ -103,13 +114,8 @@ module Tapioca
|
|
103
114
|
module_name = "GeneratedAssociationMethods"
|
104
115
|
|
105
116
|
model.create_module(module_name) do |mod|
|
106
|
-
|
107
|
-
|
108
|
-
populate_collection_assoc_getter_setter(mod, constant, association_name, reflection)
|
109
|
-
else
|
110
|
-
populate_single_assoc_getter_setter(mod, constant, association_name, reflection)
|
111
|
-
end
|
112
|
-
end
|
117
|
+
populate_nested_attribute_writers(mod, constant)
|
118
|
+
populate_associations(mod, constant)
|
113
119
|
end
|
114
120
|
|
115
121
|
model.create_include(module_name)
|
@@ -123,6 +129,31 @@ module Tapioca
|
|
123
129
|
|
124
130
|
private
|
125
131
|
|
132
|
+
sig { params(mod: Parlour::RbiGenerator::Namespace, constant: T.class_of(ActiveRecord::Base)).void }
|
133
|
+
def populate_nested_attribute_writers(mod, constant)
|
134
|
+
constant.nested_attributes_options.keys.each do |association_name|
|
135
|
+
create_method(
|
136
|
+
mod,
|
137
|
+
"#{association_name}_attributes=",
|
138
|
+
parameters: [
|
139
|
+
Parlour::RbiGenerator::Parameter.new("attributes", type: "T.untyped"),
|
140
|
+
],
|
141
|
+
return_type: "T.untyped"
|
142
|
+
)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
sig { params(mod: Parlour::RbiGenerator::Namespace, constant: T.class_of(ActiveRecord::Base)).void }
|
147
|
+
def populate_associations(mod, constant)
|
148
|
+
constant.reflections.each do |association_name, reflection|
|
149
|
+
if reflection.collection?
|
150
|
+
populate_collection_assoc_getter_setter(mod, constant, association_name, reflection)
|
151
|
+
else
|
152
|
+
populate_single_assoc_getter_setter(mod, constant, association_name, reflection)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
126
157
|
sig do
|
127
158
|
params(
|
128
159
|
klass: Parlour::RbiGenerator::Namespace,
|
@@ -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) }
|