tapioca 0.6.4 → 0.7.2
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 +8 -2
- data/README.md +27 -15
- data/Rakefile +10 -14
- data/lib/tapioca/cli.rb +65 -80
- data/lib/tapioca/{generators/base.rb → commands/command.rb} +16 -9
- data/lib/tapioca/{generators → commands}/dsl.rb +59 -45
- data/lib/tapioca/{generators → commands}/gem.rb +93 -30
- data/lib/tapioca/{generators → commands}/init.rb +9 -13
- data/lib/tapioca/{generators → commands}/require.rb +8 -10
- data/lib/tapioca/commands/todo.rb +86 -0
- data/lib/tapioca/commands.rb +13 -0
- data/lib/tapioca/dsl/compiler.rb +185 -0
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/aasm.rb +12 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_controller_helpers.rb +13 -20
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_mailer.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_job.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_attributes.rb +13 -11
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_secure_password.rb +10 -12
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_associations.rb +28 -34
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_columns.rb +18 -16
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_enum.rb +14 -12
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_fixtures.rb +12 -8
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +712 -0
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_scope.rb +21 -20
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_typed_store.rb +11 -16
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_resource.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_storage.rb +14 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_concern.rb +19 -14
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_current_attributes.rb +16 -21
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/config.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/frozen_record.rb +13 -11
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/identity_cache.rb +23 -22
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/mixed_in_class_attributes.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/protobuf.rb +22 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/rails_generators.rb +12 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/sidekiq_worker.rb +14 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/smart_properties.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/state_machines.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/url_helpers.rb +20 -15
- data/lib/tapioca/dsl/compilers.rb +31 -0
- data/lib/tapioca/{compilers/dsl → dsl}/extensions/frozen_record.rb +2 -2
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +114 -0
- data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +29 -0
- data/lib/tapioca/{compilers/dsl → dsl/helpers}/param_helper.rb +6 -3
- data/lib/tapioca/dsl/pipeline.rb +169 -0
- data/lib/tapioca/gem/events.rb +120 -0
- data/lib/tapioca/gem/listeners/base.rb +48 -0
- data/lib/tapioca/gem/listeners/dynamic_mixins.rb +32 -0
- data/lib/tapioca/gem/listeners/methods.rb +183 -0
- data/lib/tapioca/gem/listeners/mixins.rb +101 -0
- data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +21 -0
- data/lib/tapioca/gem/listeners/sorbet_enums.rb +26 -0
- data/lib/tapioca/gem/listeners/sorbet_helpers.rb +29 -0
- data/lib/tapioca/gem/listeners/sorbet_props.rb +33 -0
- data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +23 -0
- data/lib/tapioca/gem/listeners/sorbet_signatures.rb +79 -0
- data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +51 -0
- data/lib/tapioca/gem/listeners/subconstants.rb +37 -0
- data/lib/tapioca/gem/listeners/yard_doc.rb +96 -0
- data/lib/tapioca/gem/listeners.rb +16 -0
- data/lib/tapioca/gem/pipeline.rb +365 -0
- data/lib/tapioca/helpers/cli_helper.rb +7 -0
- data/lib/tapioca/helpers/config_helper.rb +5 -8
- data/lib/tapioca/helpers/shims_helper.rb +87 -0
- data/lib/tapioca/helpers/signatures_helper.rb +17 -0
- data/lib/tapioca/helpers/sorbet_helper.rb +57 -0
- data/lib/tapioca/helpers/test/dsl_compiler.rb +118 -0
- data/lib/tapioca/helpers/test/isolation.rb +1 -1
- data/lib/tapioca/helpers/test/template.rb +13 -2
- data/lib/tapioca/helpers/type_variable_helper.rb +43 -0
- data/lib/tapioca/internal.rb +18 -10
- data/lib/tapioca/rbi_ext/model.rb +14 -50
- data/lib/tapioca/rbi_formatter.rb +37 -0
- data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +227 -0
- data/lib/tapioca/runtime/generic_type_registry.rb +168 -0
- data/lib/tapioca/runtime/loader.rb +123 -0
- data/lib/tapioca/runtime/reflection.rb +157 -0
- data/lib/tapioca/runtime/trackers/autoload.rb +72 -0
- data/lib/tapioca/runtime/trackers/constant_definition.rb +44 -0
- data/lib/tapioca/runtime/trackers/mixin.rb +80 -0
- data/lib/tapioca/runtime/trackers/required_ancestor.rb +50 -0
- data/lib/tapioca/{trackers.rb → runtime/trackers.rb} +4 -3
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +69 -34
- data/lib/tapioca/sorbet_ext/name_patch.rb +7 -1
- data/lib/tapioca/{compilers → static}/requires_compiler.rb +2 -2
- data/lib/tapioca/static/symbol_loader.rb +83 -0
- data/lib/tapioca/static/symbol_table_parser.rb +63 -0
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +2 -7
- metadata +83 -62
- data/lib/tapioca/compilers/dsl/active_record_relations.rb +0 -720
- data/lib/tapioca/compilers/dsl/base.rb +0 -195
- data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +0 -27
- data/lib/tapioca/compilers/dsl_compiler.rb +0 -134
- data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +0 -223
- data/lib/tapioca/compilers/sorbet.rb +0 -59
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +0 -780
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +0 -90
- data/lib/tapioca/compilers/symbol_table_compiler.rb +0 -17
- data/lib/tapioca/compilers/todos_compiler.rb +0 -32
- data/lib/tapioca/generators/todo.rb +0 -76
- data/lib/tapioca/generators.rb +0 -9
- data/lib/tapioca/generic_type_registry.rb +0 -164
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +0 -108
- data/lib/tapioca/loader.rb +0 -119
- data/lib/tapioca/reflection.rb +0 -151
- data/lib/tapioca/trackers/autoload.rb +0 -70
- data/lib/tapioca/trackers/constant_definition.rb +0 -42
- data/lib/tapioca/trackers/mixin.rb +0 -78
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Tapioca
|
|
5
|
+
module Commands
|
|
6
|
+
class Todo < Command
|
|
7
|
+
include SorbetHelper
|
|
8
|
+
|
|
9
|
+
sig do
|
|
10
|
+
params(
|
|
11
|
+
todo_file: String,
|
|
12
|
+
file_header: T::Boolean
|
|
13
|
+
).void
|
|
14
|
+
end
|
|
15
|
+
def initialize(todo_file:, file_header:)
|
|
16
|
+
@todo_file = todo_file
|
|
17
|
+
@file_header = file_header
|
|
18
|
+
|
|
19
|
+
super()
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
sig { override.void }
|
|
23
|
+
def execute
|
|
24
|
+
say("Finding all unresolved constants, this may take a few seconds... ")
|
|
25
|
+
|
|
26
|
+
# Clean all existing unresolved constants before regenerating the list
|
|
27
|
+
# so Sorbet won't grab them as already resolved.
|
|
28
|
+
File.delete(@todo_file) if File.exist?(@todo_file)
|
|
29
|
+
|
|
30
|
+
constants = unresolved_constants
|
|
31
|
+
|
|
32
|
+
if constants.empty?
|
|
33
|
+
say("Nothing to do", :green)
|
|
34
|
+
return
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
say("Done", :green)
|
|
38
|
+
contents = rbi(constants, command: default_command(:todo))
|
|
39
|
+
create_file(@todo_file, contents.string, verbose: false)
|
|
40
|
+
|
|
41
|
+
name = set_color(@todo_file, :yellow, :bold)
|
|
42
|
+
say("\nAll unresolved constants have been written to #{name}.", [:green, :bold])
|
|
43
|
+
say("Please review changes and commit them.", [:green, :bold])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
sig { params(constants: T::Array[String], command: String).returns(RBI::File) }
|
|
49
|
+
def rbi(constants, command:)
|
|
50
|
+
file = RBI::File.new
|
|
51
|
+
|
|
52
|
+
if @file_header
|
|
53
|
+
file.comments << RBI::Comment.new("DO NOT EDIT MANUALLY")
|
|
54
|
+
file.comments << RBI::Comment.new("This is an autogenerated file for unresolved constants.")
|
|
55
|
+
file.comments << RBI::Comment.new("Please instead update this file by running `#{command}`.")
|
|
56
|
+
file.comments << RBI::BlankLine.new
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
file.comments << RBI::Comment.new("typed: false")
|
|
60
|
+
|
|
61
|
+
constants.each do |name|
|
|
62
|
+
file << RBI::Module.new(name)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
file
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
sig { returns(T::Array[String]) }
|
|
69
|
+
def unresolved_constants
|
|
70
|
+
# Taken from https://github.com/sorbet/sorbet/blob/master/gems/sorbet/lib/todo-rbi.rb
|
|
71
|
+
sorbet("--print=missing-constants", "--quiet", "--stdout-hup-hack", "--no-error-count")
|
|
72
|
+
.out
|
|
73
|
+
.strip
|
|
74
|
+
.each_line
|
|
75
|
+
.map do |line|
|
|
76
|
+
next if line.include?("<")
|
|
77
|
+
|
|
78
|
+
line.strip
|
|
79
|
+
.gsub(/T\.class_of\(([:\w]+)\)/, '\1') # Turn T.class_of(Foo)::Bar into Foo::Bar
|
|
80
|
+
end
|
|
81
|
+
.compact
|
|
82
|
+
.sort
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Tapioca
|
|
5
|
+
module Commands
|
|
6
|
+
autoload :Command, "tapioca/commands/command"
|
|
7
|
+
autoload :Dsl, "tapioca/commands/dsl"
|
|
8
|
+
autoload :Init, "tapioca/commands/init"
|
|
9
|
+
autoload :Gem, "tapioca/commands/gem"
|
|
10
|
+
autoload :Require, "tapioca/commands/require"
|
|
11
|
+
autoload :Todo, "tapioca/commands/todo"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "rbi"
|
|
5
|
+
require "tapioca/rbi_ext/model"
|
|
6
|
+
require "tapioca/rbi_formatter"
|
|
7
|
+
require "tapioca/dsl/helpers/param_helper"
|
|
8
|
+
require "tapioca/dsl/pipeline"
|
|
9
|
+
|
|
10
|
+
module Tapioca
|
|
11
|
+
module Dsl
|
|
12
|
+
class Compiler
|
|
13
|
+
extend T::Sig
|
|
14
|
+
extend T::Helpers
|
|
15
|
+
extend T::Generic
|
|
16
|
+
|
|
17
|
+
include Runtime::Reflection
|
|
18
|
+
extend Runtime::Reflection
|
|
19
|
+
|
|
20
|
+
ConstantType = type_member { { upper: Module } }
|
|
21
|
+
|
|
22
|
+
abstract!
|
|
23
|
+
|
|
24
|
+
sig { returns(ConstantType) }
|
|
25
|
+
attr_reader :constant
|
|
26
|
+
|
|
27
|
+
sig { returns(RBI::Tree) }
|
|
28
|
+
attr_reader :root
|
|
29
|
+
|
|
30
|
+
sig { params(pipeline: Tapioca::Dsl::Pipeline, root: RBI::Tree, constant: ConstantType).void }
|
|
31
|
+
def initialize(pipeline, root, constant)
|
|
32
|
+
@pipeline = pipeline
|
|
33
|
+
@root = root
|
|
34
|
+
@constant = constant
|
|
35
|
+
@errors = T.let([], T::Array[String])
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
sig { params(constant: Module).returns(T::Boolean) }
|
|
39
|
+
def self.handles?(constant)
|
|
40
|
+
processable_constants.include?(constant)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
sig { params(compiler_name: String).returns(T::Boolean) }
|
|
44
|
+
def compiler_enabled?(compiler_name)
|
|
45
|
+
@pipeline.compiler_enabled?(compiler_name)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
sig { abstract.void }
|
|
49
|
+
def decorate; end
|
|
50
|
+
|
|
51
|
+
sig { abstract.returns(T::Enumerable[Module]) }
|
|
52
|
+
def self.gather_constants; end
|
|
53
|
+
|
|
54
|
+
sig { returns(T::Set[Module]) }
|
|
55
|
+
def self.processable_constants
|
|
56
|
+
@processable_constants ||= T.let(
|
|
57
|
+
T::Set[Module].new(gather_constants).compare_by_identity,
|
|
58
|
+
T.nilable(T::Set[Module])
|
|
59
|
+
)
|
|
60
|
+
T.must(@processable_constants)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# NOTE: This should eventually accept an `Error` object or `Exception` rather than simply a `String`.
|
|
64
|
+
sig { params(error: String).void }
|
|
65
|
+
def add_error(error)
|
|
66
|
+
@pipeline.add_error(error)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
sig { returns(T::Enumerable[Class]) }
|
|
72
|
+
private_class_method def self.all_classes
|
|
73
|
+
@all_classes = T.let(@all_classes, T.nilable(T::Enumerable[Class]))
|
|
74
|
+
@all_classes ||= T.cast(ObjectSpace.each_object(Class), T::Enumerable[Class]).each
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
sig { returns(T::Enumerable[Module]) }
|
|
78
|
+
private_class_method def self.all_modules
|
|
79
|
+
@all_modules = T.let(@all_modules, T.nilable(T::Enumerable[Module]))
|
|
80
|
+
@all_modules ||= T.cast(ObjectSpace.each_object(Module), T::Enumerable[Module]).each
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Get the types of each parameter from a method signature
|
|
84
|
+
sig do
|
|
85
|
+
params(
|
|
86
|
+
method_def: T.any(Method, UnboundMethod),
|
|
87
|
+
signature: T.untyped # as `T::Private::Methods::Signature` is private
|
|
88
|
+
).returns(T::Array[String])
|
|
89
|
+
end
|
|
90
|
+
def parameters_types_from_signature(method_def, signature)
|
|
91
|
+
params = T.let([], T::Array[String])
|
|
92
|
+
|
|
93
|
+
return method_def.parameters.map { "T.untyped" } unless signature
|
|
94
|
+
|
|
95
|
+
# parameters types
|
|
96
|
+
signature.arg_types.each { |arg_type| params << arg_type[1].to_s }
|
|
97
|
+
|
|
98
|
+
# keyword parameters types
|
|
99
|
+
signature.kwarg_types.each { |_, kwarg_type| params << kwarg_type.to_s }
|
|
100
|
+
|
|
101
|
+
# rest parameter type
|
|
102
|
+
params << signature.rest_type.to_s if signature.has_rest
|
|
103
|
+
|
|
104
|
+
# special case `.void` in a proc
|
|
105
|
+
unless signature.block_name.nil?
|
|
106
|
+
params << signature.block_type.to_s.gsub("returns(<VOID>)", "void")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
params
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
sig { params(scope: RBI::Scope, method_def: T.any(Method, UnboundMethod), class_method: T::Boolean).void }
|
|
113
|
+
def create_method_from_def(scope, method_def, class_method: false)
|
|
114
|
+
scope.create_method(
|
|
115
|
+
method_def.name.to_s,
|
|
116
|
+
parameters: compile_method_parameters_to_rbi(method_def),
|
|
117
|
+
return_type: compile_method_return_type_to_rbi(method_def),
|
|
118
|
+
class_method: class_method
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
include Helpers::ParamHelper
|
|
123
|
+
|
|
124
|
+
sig { params(method_def: T.any(Method, UnboundMethod)).returns(T::Array[RBI::TypedParam]) }
|
|
125
|
+
def compile_method_parameters_to_rbi(method_def)
|
|
126
|
+
signature = signature_of(method_def)
|
|
127
|
+
method_def = signature.nil? ? method_def : signature.method
|
|
128
|
+
method_types = parameters_types_from_signature(method_def, signature)
|
|
129
|
+
|
|
130
|
+
parameters = T.let(method_def.parameters, T::Array[[Symbol, T.nilable(Symbol)]])
|
|
131
|
+
|
|
132
|
+
parameters.each_with_index.map do |(type, name), index|
|
|
133
|
+
fallback_arg_name = "_arg#{index}"
|
|
134
|
+
|
|
135
|
+
name = name ? name.to_s : fallback_arg_name
|
|
136
|
+
name = fallback_arg_name unless valid_parameter_name?(name)
|
|
137
|
+
method_type = T.must(method_types[index])
|
|
138
|
+
|
|
139
|
+
case type
|
|
140
|
+
when :req
|
|
141
|
+
create_param(name, type: method_type)
|
|
142
|
+
when :opt
|
|
143
|
+
create_opt_param(name, type: method_type, default: "T.unsafe(nil)")
|
|
144
|
+
when :rest
|
|
145
|
+
create_rest_param(name, type: method_type)
|
|
146
|
+
when :keyreq
|
|
147
|
+
create_kw_param(name, type: method_type)
|
|
148
|
+
when :key
|
|
149
|
+
create_kw_opt_param(name, type: method_type, default: "T.unsafe(nil)")
|
|
150
|
+
when :keyrest
|
|
151
|
+
create_kw_rest_param(name, type: method_type)
|
|
152
|
+
when :block
|
|
153
|
+
create_block_param(name, type: method_type)
|
|
154
|
+
else
|
|
155
|
+
raise "Unknown type `#{type}`."
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
sig { params(method_def: T.any(Method, UnboundMethod)).returns(String) }
|
|
161
|
+
def compile_method_return_type_to_rbi(method_def)
|
|
162
|
+
signature = signature_of(method_def)
|
|
163
|
+
return_type = signature.nil? ? "T.untyped" : name_of_type(signature.return_type)
|
|
164
|
+
return_type = "void" if return_type == "<VOID>"
|
|
165
|
+
# Map <NOT-TYPED> to `T.untyped`
|
|
166
|
+
return_type = "T.untyped" if return_type == "<NOT-TYPED>"
|
|
167
|
+
return_type
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
sig { params(type: String).returns(String) }
|
|
171
|
+
def as_nilable_type(type)
|
|
172
|
+
if type.start_with?("T.nilable(", "::T.nilable(") || type == "T.untyped" || type == "::T.untyped"
|
|
173
|
+
type
|
|
174
|
+
else
|
|
175
|
+
"T.nilable(#{type})"
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
sig { params(name: String).returns(T::Boolean) }
|
|
180
|
+
def valid_parameter_name?(name)
|
|
181
|
+
name.match?(/^[[[:alnum:]]_]+$/)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -9,9 +9,9 @@ rescue LoadError
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
module Tapioca
|
|
12
|
-
module
|
|
13
|
-
module
|
|
14
|
-
# `Tapioca::Compilers::
|
|
12
|
+
module Dsl
|
|
13
|
+
module Compilers
|
|
14
|
+
# `Tapioca::Dsl::Compilers::AASM` generate types for AASM state machines.
|
|
15
15
|
# This gem dynamically defines constants and methods at runtime. For
|
|
16
16
|
# example, given a class:
|
|
17
17
|
#
|
|
@@ -37,7 +37,7 @@ module Tapioca
|
|
|
37
37
|
# sleeping?, running?, cleaning?
|
|
38
38
|
# run, run!, run_without_validation!, may_run?
|
|
39
39
|
#
|
|
40
|
-
class AASM <
|
|
40
|
+
class AASM < Compiler
|
|
41
41
|
extend T::Sig
|
|
42
42
|
|
|
43
43
|
# Taken directly from the AASM::Core::Event class, here:
|
|
@@ -45,12 +45,14 @@ module Tapioca
|
|
|
45
45
|
EVENT_CALLBACKS =
|
|
46
46
|
T.let(
|
|
47
47
|
["after", "after_commit", "after_transaction", "before", "before_transaction", "ensure", "error",
|
|
48
|
-
"before_success", "success"].freeze,
|
|
48
|
+
"before_success", "success",].freeze,
|
|
49
49
|
T::Array[String]
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
ConstantType = type_member { { fixed: T.all(::AASM::ClassMethods, Class) } }
|
|
53
|
+
|
|
54
|
+
sig { override.void }
|
|
55
|
+
def decorate
|
|
54
56
|
aasm = constant.aasm
|
|
55
57
|
return if !aasm || aasm.states.empty?
|
|
56
58
|
|
|
@@ -103,7 +105,8 @@ module Tapioca
|
|
|
103
105
|
event.create_method(
|
|
104
106
|
method,
|
|
105
107
|
parameters: [
|
|
106
|
-
|
|
108
|
+
create_opt_param("symbol", type: "T.nilable(Symbol)", default: "nil"),
|
|
109
|
+
create_block_param("block", type: "T.nilable(T.proc.bind(#{name_of(constant)}).void)"),
|
|
107
110
|
]
|
|
108
111
|
)
|
|
109
112
|
end
|
|
@@ -113,7 +116,7 @@ module Tapioca
|
|
|
113
116
|
end
|
|
114
117
|
|
|
115
118
|
sig { override.returns(T::Enumerable[Module]) }
|
|
116
|
-
def gather_constants
|
|
119
|
+
def self.gather_constants
|
|
117
120
|
T.cast(ObjectSpace.each_object(::AASM::ClassMethods), T::Enumerable[Module])
|
|
118
121
|
end
|
|
119
122
|
end
|
|
@@ -8,9 +8,9 @@ rescue LoadError
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module Tapioca
|
|
11
|
-
module
|
|
12
|
-
module
|
|
13
|
-
# `Tapioca::Compilers::
|
|
11
|
+
module Dsl
|
|
12
|
+
module Compilers
|
|
13
|
+
# `Tapioca::Dsl::Compilers::ActionControllerHelpers` decorates RBI files for all
|
|
14
14
|
# subclasses of [`ActionController::Base`](https://api.rubyonrails.org/classes/ActionController/Helpers.html).
|
|
15
15
|
#
|
|
16
16
|
# For example, with the following `MyHelper` module:
|
|
@@ -41,7 +41,7 @@ module Tapioca
|
|
|
41
41
|
# end
|
|
42
42
|
# ~~~
|
|
43
43
|
#
|
|
44
|
-
# this
|
|
44
|
+
# this compiler will produce an RBI file `user_controller.rbi` with the following content:
|
|
45
45
|
#
|
|
46
46
|
# ~~~rbi
|
|
47
47
|
# # user_controller.rbi
|
|
@@ -65,15 +65,13 @@ module Tapioca
|
|
|
65
65
|
# def helpers; end
|
|
66
66
|
# end
|
|
67
67
|
# ~~~
|
|
68
|
-
class ActionControllerHelpers <
|
|
68
|
+
class ActionControllerHelpers < Compiler
|
|
69
69
|
extend T::Sig
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
end
|
|
76
|
-
def decorate(root, constant)
|
|
71
|
+
ConstantType = type_member { { fixed: T.class_of(::ActionController::Base) } }
|
|
72
|
+
|
|
73
|
+
sig { override.void }
|
|
74
|
+
def decorate
|
|
77
75
|
helpers_module = constant._helpers
|
|
78
76
|
proxied_helper_methods = constant._helper_methods.map(&:to_s).map(&:to_sym)
|
|
79
77
|
|
|
@@ -103,7 +101,7 @@ module Tapioca
|
|
|
103
101
|
# helper method defined via the `helper_method` call in the controller.
|
|
104
102
|
helpers_module.instance_methods(false).each do |method_name|
|
|
105
103
|
method = if proxied_helper_methods.include?(method_name)
|
|
106
|
-
helper_method_proxy_target(
|
|
104
|
+
helper_method_proxy_target(method_name)
|
|
107
105
|
else
|
|
108
106
|
helpers_module.instance_method(method_name)
|
|
109
107
|
end
|
|
@@ -124,19 +122,14 @@ module Tapioca
|
|
|
124
122
|
end
|
|
125
123
|
|
|
126
124
|
sig { override.returns(T::Enumerable[Module]) }
|
|
127
|
-
def gather_constants
|
|
125
|
+
def self.gather_constants
|
|
128
126
|
descendants_of(::ActionController::Base).reject(&:abstract?).select(&:name)
|
|
129
127
|
end
|
|
130
128
|
|
|
131
129
|
private
|
|
132
130
|
|
|
133
|
-
sig
|
|
134
|
-
|
|
135
|
-
constant: T.class_of(::ActionController::Base),
|
|
136
|
-
method_name: Symbol
|
|
137
|
-
).returns(T.nilable(UnboundMethod))
|
|
138
|
-
end
|
|
139
|
-
def helper_method_proxy_target(constant, method_name)
|
|
131
|
+
sig { params(method_name: Symbol).returns(T.nilable(UnboundMethod)) }
|
|
132
|
+
def helper_method_proxy_target(method_name)
|
|
140
133
|
# Lookup the proxy target method only if it is defined as a public/protected or private method.
|
|
141
134
|
if constant.method_defined?(method_name) || constant.private_method_defined?(method_name)
|
|
142
135
|
constant.instance_method(method_name)
|
|
@@ -8,9 +8,9 @@ rescue LoadError
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module Tapioca
|
|
11
|
-
module
|
|
12
|
-
module
|
|
13
|
-
# `Tapioca::Compilers::
|
|
11
|
+
module Dsl
|
|
12
|
+
module Compilers
|
|
13
|
+
# `Tapioca::Dsl::Compilers::ActionMailer` generates RBI files for subclasses of
|
|
14
14
|
# [`ActionMailer::Base`](https://api.rubyonrails.org/classes/ActionMailer/Base.html).
|
|
15
15
|
#
|
|
16
16
|
# For example, with the following `ActionMailer` subclass:
|
|
@@ -23,7 +23,7 @@ module Tapioca
|
|
|
23
23
|
# end
|
|
24
24
|
# ~~~
|
|
25
25
|
#
|
|
26
|
-
# this
|
|
26
|
+
# this compiler will produce the RBI file `notifier_mailer.rbi` with the following content:
|
|
27
27
|
#
|
|
28
28
|
# ~~~rbi
|
|
29
29
|
# # notifier_mailer.rbi
|
|
@@ -33,11 +33,13 @@ module Tapioca
|
|
|
33
33
|
# def self.notify_customer(customer_id); end
|
|
34
34
|
# end
|
|
35
35
|
# ~~~
|
|
36
|
-
class ActionMailer <
|
|
36
|
+
class ActionMailer < Compiler
|
|
37
37
|
extend T::Sig
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
ConstantType = type_member { { fixed: T.class_of(::ActionMailer::Base) } }
|
|
40
|
+
|
|
41
|
+
sig { override.void }
|
|
42
|
+
def decorate
|
|
41
43
|
root.create_path(constant) do |mailer|
|
|
42
44
|
constant.action_methods.to_a.each do |mailer_method|
|
|
43
45
|
method_def = constant.instance_method(mailer_method)
|
|
@@ -53,7 +55,7 @@ module Tapioca
|
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
sig { override.returns(T::Enumerable[Module]) }
|
|
56
|
-
def gather_constants
|
|
58
|
+
def self.gather_constants
|
|
57
59
|
descendants_of(::ActionMailer::Base).reject(&:abstract?)
|
|
58
60
|
end
|
|
59
61
|
end
|
|
@@ -8,9 +8,9 @@ rescue LoadError
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module Tapioca
|
|
11
|
-
module
|
|
12
|
-
module
|
|
13
|
-
# `Tapioca::Compilers::
|
|
11
|
+
module Dsl
|
|
12
|
+
module Compilers
|
|
13
|
+
# `Tapioca::Dsl::Compilers::ActiveJob` generates RBI files for subclasses of
|
|
14
14
|
# [`ActiveJob::Base`](https://api.rubyonrails.org/classes/ActiveJob/Base.html).
|
|
15
15
|
#
|
|
16
16
|
# For example, with the following `ActiveJob` subclass:
|
|
@@ -24,7 +24,7 @@ module Tapioca
|
|
|
24
24
|
# end
|
|
25
25
|
# ~~~
|
|
26
26
|
#
|
|
27
|
-
# this
|
|
27
|
+
# this compiler will produce the RBI file `notify_user_job.rbi` with the following content:
|
|
28
28
|
#
|
|
29
29
|
# ~~~rbi
|
|
30
30
|
# # notify_user_job.rbi
|
|
@@ -37,11 +37,13 @@ module Tapioca
|
|
|
37
37
|
# def self.perform_now(user); end
|
|
38
38
|
# end
|
|
39
39
|
# ~~~
|
|
40
|
-
class ActiveJob <
|
|
40
|
+
class ActiveJob < Compiler
|
|
41
41
|
extend T::Sig
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
ConstantType = type_member { { fixed: T.class_of(::ActiveJob::Base) } }
|
|
44
|
+
|
|
45
|
+
sig { override.void }
|
|
46
|
+
def decorate
|
|
45
47
|
return unless constant.instance_methods(false).include?(:perform)
|
|
46
48
|
|
|
47
49
|
root.create_path(constant) do |job|
|
|
@@ -52,7 +54,7 @@ module Tapioca
|
|
|
52
54
|
job.create_method(
|
|
53
55
|
"perform_later",
|
|
54
56
|
parameters: parameters,
|
|
55
|
-
return_type: "T.any(#{constant
|
|
57
|
+
return_type: "T.any(#{name_of(constant)}, FalseClass)",
|
|
56
58
|
class_method: true
|
|
57
59
|
)
|
|
58
60
|
|
|
@@ -66,7 +68,7 @@ module Tapioca
|
|
|
66
68
|
end
|
|
67
69
|
|
|
68
70
|
sig { override.returns(T::Enumerable[Module]) }
|
|
69
|
-
def gather_constants
|
|
71
|
+
def self.gather_constants
|
|
70
72
|
descendants_of(::ActiveJob::Base)
|
|
71
73
|
end
|
|
72
74
|
end
|
|
@@ -8,9 +8,9 @@ rescue LoadError
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module Tapioca
|
|
11
|
-
module
|
|
12
|
-
module
|
|
13
|
-
# `Tapioca::Compilers::
|
|
11
|
+
module Dsl
|
|
12
|
+
module Compilers
|
|
13
|
+
# `Tapioca::Dsl::Compilers::ActiveModelAttributes` decorates RBI files for all
|
|
14
14
|
# classes that use [`ActiveModel::Attributes`](https://edgeapi.rubyonrails.org/classes/ActiveModel/Attributes/ClassMethods.html).
|
|
15
15
|
#
|
|
16
16
|
# For example, with the following class:
|
|
@@ -23,7 +23,7 @@ module Tapioca
|
|
|
23
23
|
# end
|
|
24
24
|
# ~~~
|
|
25
25
|
#
|
|
26
|
-
# this
|
|
26
|
+
# this compiler will produce an RBI file with the following content:
|
|
27
27
|
# ~~~rbi
|
|
28
28
|
# # typed: true
|
|
29
29
|
#
|
|
@@ -36,12 +36,14 @@ module Tapioca
|
|
|
36
36
|
# def name=(name); end
|
|
37
37
|
# end
|
|
38
38
|
# ~~~
|
|
39
|
-
class ActiveModelAttributes <
|
|
39
|
+
class ActiveModelAttributes < Compiler
|
|
40
40
|
extend T::Sig
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
ConstantType = type_member { { fixed: T.all(Class, ::ActiveModel::Attributes::ClassMethods) } }
|
|
43
|
+
|
|
44
|
+
sig { override.void }
|
|
45
|
+
def decorate
|
|
46
|
+
attribute_methods = attribute_methods_for_constant
|
|
45
47
|
return if attribute_methods.empty?
|
|
46
48
|
|
|
47
49
|
root.create_path(constant) do |klass|
|
|
@@ -52,7 +54,7 @@ module Tapioca
|
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
sig { override.returns(T::Enumerable[Module]) }
|
|
55
|
-
def gather_constants
|
|
57
|
+
def self.gather_constants
|
|
56
58
|
all_classes.grep(::ActiveModel::Attributes::ClassMethods)
|
|
57
59
|
end
|
|
58
60
|
|
|
@@ -60,8 +62,8 @@ module Tapioca
|
|
|
60
62
|
|
|
61
63
|
HANDLED_METHOD_TARGETS = T.let(["attribute", "attribute="], T::Array[String])
|
|
62
64
|
|
|
63
|
-
sig {
|
|
64
|
-
def
|
|
65
|
+
sig { returns(T::Array[[::String, ::String]]) }
|
|
66
|
+
def attribute_methods_for_constant
|
|
65
67
|
patterns = if constant.respond_to?(:attribute_method_patterns)
|
|
66
68
|
# https://github.com/rails/rails/pull/44367
|
|
67
69
|
T.unsafe(constant).attribute_method_patterns
|
|
@@ -8,9 +8,9 @@ rescue LoadError
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module Tapioca
|
|
11
|
-
module
|
|
12
|
-
module
|
|
13
|
-
# `Tapioca::Compilers::
|
|
11
|
+
module Dsl
|
|
12
|
+
module Compilers
|
|
13
|
+
# `Tapioca::Dsl::Compilers::ActiveModelSecurePassword` decorates RBI files for all
|
|
14
14
|
# classes that use [`ActiveModel::SecurePassword`](http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html).
|
|
15
15
|
#
|
|
16
16
|
# For example, with the following class:
|
|
@@ -24,7 +24,7 @@ module Tapioca
|
|
|
24
24
|
# end
|
|
25
25
|
# ~~~
|
|
26
26
|
#
|
|
27
|
-
# this
|
|
27
|
+
# this compiler will produce an RBI file with the following content:
|
|
28
28
|
# ~~~rbi
|
|
29
29
|
# # typed: true
|
|
30
30
|
#
|
|
@@ -57,15 +57,13 @@ module Tapioca
|
|
|
57
57
|
# def token_confirmation=(unencrypted_password); end
|
|
58
58
|
# end
|
|
59
59
|
# ~~~
|
|
60
|
-
class ActiveModelSecurePassword <
|
|
60
|
+
class ActiveModelSecurePassword < Compiler
|
|
61
61
|
extend T::Sig
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
end
|
|
68
|
-
def decorate(root, constant)
|
|
63
|
+
ConstantType = type_member { { fixed: T.all(Class, ::ActiveModel::SecurePassword::ClassMethods) } }
|
|
64
|
+
|
|
65
|
+
sig { override.void }
|
|
66
|
+
def decorate
|
|
69
67
|
instance_methods_modules = if constant < ActiveModel::SecurePassword::InstanceMethodsOnActivation
|
|
70
68
|
# pre Rails 6.0, this used to be a single static module
|
|
71
69
|
[ActiveModel::SecurePassword::InstanceMethodsOnActivation]
|
|
@@ -88,7 +86,7 @@ module Tapioca
|
|
|
88
86
|
end
|
|
89
87
|
|
|
90
88
|
sig { override.returns(T::Enumerable[Module]) }
|
|
91
|
-
def gather_constants
|
|
89
|
+
def self.gather_constants
|
|
92
90
|
# This selects all classes that are `ActiveModel::SecurePassword::ClassMethods === klass`.
|
|
93
91
|
# In other words, we select all classes that have `ActiveModel::SecurePassword::ClassMethods`
|
|
94
92
|
# as an ancestor of its singleton class, i.e. all classes that have extended the
|