tapioca 0.6.4 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +8 -2
  3. data/README.md +27 -15
  4. data/Rakefile +10 -14
  5. data/lib/tapioca/cli.rb +65 -80
  6. data/lib/tapioca/{generators/base.rb → commands/command.rb} +16 -9
  7. data/lib/tapioca/{generators → commands}/dsl.rb +59 -45
  8. data/lib/tapioca/{generators → commands}/gem.rb +93 -30
  9. data/lib/tapioca/{generators → commands}/init.rb +9 -13
  10. data/lib/tapioca/{generators → commands}/require.rb +8 -10
  11. data/lib/tapioca/commands/todo.rb +86 -0
  12. data/lib/tapioca/commands.rb +13 -0
  13. data/lib/tapioca/dsl/compiler.rb +185 -0
  14. data/lib/tapioca/{compilers/dsl → dsl/compilers}/aasm.rb +12 -9
  15. data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_controller_helpers.rb +13 -20
  16. data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_mailer.rb +10 -8
  17. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_job.rb +11 -9
  18. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_attributes.rb +13 -11
  19. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_secure_password.rb +10 -12
  20. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_associations.rb +28 -34
  21. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_columns.rb +18 -16
  22. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_enum.rb +14 -12
  23. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_fixtures.rb +12 -8
  24. data/lib/tapioca/dsl/compilers/active_record_relations.rb +712 -0
  25. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_scope.rb +21 -20
  26. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_typed_store.rb +11 -16
  27. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_resource.rb +10 -8
  28. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_storage.rb +14 -10
  29. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_concern.rb +19 -14
  30. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_current_attributes.rb +16 -21
  31. data/lib/tapioca/{compilers/dsl → dsl/compilers}/config.rb +11 -9
  32. data/lib/tapioca/{compilers/dsl → dsl/compilers}/frozen_record.rb +13 -11
  33. data/lib/tapioca/{compilers/dsl → dsl/compilers}/identity_cache.rb +23 -22
  34. data/lib/tapioca/{compilers/dsl → dsl/compilers}/mixed_in_class_attributes.rb +12 -10
  35. data/lib/tapioca/{compilers/dsl → dsl/compilers}/protobuf.rb +22 -10
  36. data/lib/tapioca/{compilers/dsl → dsl/compilers}/rails_generators.rb +12 -13
  37. data/lib/tapioca/{compilers/dsl → dsl/compilers}/sidekiq_worker.rb +14 -13
  38. data/lib/tapioca/{compilers/dsl → dsl/compilers}/smart_properties.rb +11 -9
  39. data/lib/tapioca/{compilers/dsl → dsl/compilers}/state_machines.rb +12 -10
  40. data/lib/tapioca/{compilers/dsl → dsl/compilers}/url_helpers.rb +20 -15
  41. data/lib/tapioca/dsl/compilers.rb +31 -0
  42. data/lib/tapioca/{compilers/dsl → dsl}/extensions/frozen_record.rb +2 -2
  43. data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +114 -0
  44. data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +29 -0
  45. data/lib/tapioca/{compilers/dsl → dsl/helpers}/param_helper.rb +6 -3
  46. data/lib/tapioca/dsl/pipeline.rb +169 -0
  47. data/lib/tapioca/gem/events.rb +120 -0
  48. data/lib/tapioca/gem/listeners/base.rb +48 -0
  49. data/lib/tapioca/gem/listeners/dynamic_mixins.rb +32 -0
  50. data/lib/tapioca/gem/listeners/methods.rb +183 -0
  51. data/lib/tapioca/gem/listeners/mixins.rb +101 -0
  52. data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +21 -0
  53. data/lib/tapioca/gem/listeners/sorbet_enums.rb +26 -0
  54. data/lib/tapioca/gem/listeners/sorbet_helpers.rb +29 -0
  55. data/lib/tapioca/gem/listeners/sorbet_props.rb +33 -0
  56. data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +23 -0
  57. data/lib/tapioca/gem/listeners/sorbet_signatures.rb +79 -0
  58. data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +51 -0
  59. data/lib/tapioca/gem/listeners/subconstants.rb +37 -0
  60. data/lib/tapioca/gem/listeners/yard_doc.rb +96 -0
  61. data/lib/tapioca/gem/listeners.rb +16 -0
  62. data/lib/tapioca/gem/pipeline.rb +365 -0
  63. data/lib/tapioca/helpers/cli_helper.rb +7 -0
  64. data/lib/tapioca/helpers/config_helper.rb +5 -8
  65. data/lib/tapioca/helpers/shims_helper.rb +87 -0
  66. data/lib/tapioca/helpers/signatures_helper.rb +17 -0
  67. data/lib/tapioca/helpers/sorbet_helper.rb +57 -0
  68. data/lib/tapioca/helpers/test/dsl_compiler.rb +118 -0
  69. data/lib/tapioca/helpers/test/isolation.rb +1 -1
  70. data/lib/tapioca/helpers/test/template.rb +13 -2
  71. data/lib/tapioca/helpers/type_variable_helper.rb +43 -0
  72. data/lib/tapioca/internal.rb +18 -10
  73. data/lib/tapioca/rbi_ext/model.rb +14 -50
  74. data/lib/tapioca/rbi_formatter.rb +37 -0
  75. data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +227 -0
  76. data/lib/tapioca/runtime/generic_type_registry.rb +168 -0
  77. data/lib/tapioca/runtime/loader.rb +123 -0
  78. data/lib/tapioca/runtime/reflection.rb +157 -0
  79. data/lib/tapioca/runtime/trackers/autoload.rb +72 -0
  80. data/lib/tapioca/runtime/trackers/constant_definition.rb +44 -0
  81. data/lib/tapioca/runtime/trackers/mixin.rb +80 -0
  82. data/lib/tapioca/runtime/trackers/required_ancestor.rb +50 -0
  83. data/lib/tapioca/{trackers.rb → runtime/trackers.rb} +4 -3
  84. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +69 -34
  85. data/lib/tapioca/sorbet_ext/name_patch.rb +7 -1
  86. data/lib/tapioca/{compilers → static}/requires_compiler.rb +2 -2
  87. data/lib/tapioca/static/symbol_loader.rb +83 -0
  88. data/lib/tapioca/static/symbol_table_parser.rb +63 -0
  89. data/lib/tapioca/version.rb +1 -1
  90. data/lib/tapioca.rb +2 -7
  91. metadata +83 -62
  92. data/lib/tapioca/compilers/dsl/active_record_relations.rb +0 -720
  93. data/lib/tapioca/compilers/dsl/base.rb +0 -195
  94. data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +0 -27
  95. data/lib/tapioca/compilers/dsl_compiler.rb +0 -134
  96. data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +0 -223
  97. data/lib/tapioca/compilers/sorbet.rb +0 -59
  98. data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +0 -780
  99. data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +0 -90
  100. data/lib/tapioca/compilers/symbol_table_compiler.rb +0 -17
  101. data/lib/tapioca/compilers/todos_compiler.rb +0 -32
  102. data/lib/tapioca/generators/todo.rb +0 -76
  103. data/lib/tapioca/generators.rb +0 -9
  104. data/lib/tapioca/generic_type_registry.rb +0 -164
  105. data/lib/tapioca/helpers/active_record_column_type_helper.rb +0 -108
  106. data/lib/tapioca/loader.rb +0 -119
  107. data/lib/tapioca/reflection.rb +0 -151
  108. data/lib/tapioca/trackers/autoload.rb +0 -70
  109. data/lib/tapioca/trackers/constant_definition.rb +0 -42
  110. 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 Compilers
13
- module Dsl
14
- # `Tapioca::Compilers::Dsl::AASM` generate types for AASM state machines.
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 < Tapioca::Compilers::Dsl::Base
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
- sig { override.params(root: RBI::Tree, constant: T.all(::AASM::ClassMethods, Class)).void }
53
- def decorate(root, constant)
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
- create_block_param("block", type: "T.proc.bind(#{constant.name}).void"),
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 Compilers
12
- module Dsl
13
- # `Tapioca::Compilers::Dsl::ActionControllerHelpers` decorates RBI files for all
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 generator will produce an RBI file `user_controller.rbi` with the following content:
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 < Base
68
+ class ActionControllerHelpers < Compiler
69
69
  extend T::Sig
70
70
 
71
- sig do
72
- override
73
- .params(root: RBI::Tree, constant: T.class_of(::ActionController::Base))
74
- .void
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(constant, method_name)
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 do
134
- params(
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 Compilers
12
- module Dsl
13
- # `Tapioca::Compilers::Dsl::ActionMailer` generates RBI files for subclasses of
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 generator will produce the RBI file `notifier_mailer.rbi` with the following content:
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 < Base
36
+ class ActionMailer < Compiler
37
37
  extend T::Sig
38
38
 
39
- sig { override.params(root: RBI::Tree, constant: T.class_of(::ActionMailer::Base)).void }
40
- def decorate(root, constant)
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 Compilers
12
- module Dsl
13
- # `Tapioca::Compilers::Dsl::ActiveJob` generates RBI files for subclasses of
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 generator will produce the RBI file `notify_user_job.rbi` with the following content:
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 < Base
40
+ class ActiveJob < Compiler
41
41
  extend T::Sig
42
42
 
43
- sig { override.params(root: RBI::Tree, constant: T.class_of(::ActiveJob::Base)).void }
44
- def decorate(root, constant)
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.name}, FalseClass)",
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 Compilers
12
- module Dsl
13
- # `Tapioca::Compilers::Dsl::ActiveModelAttributes` decorates RBI files for all
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 generator will produce an RBI file with the following content:
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 < Base
39
+ class ActiveModelAttributes < Compiler
40
40
  extend T::Sig
41
41
 
42
- sig { override.params(root: RBI::Tree, constant: T.all(Class, ::ActiveModel::Attributes::ClassMethods)).void }
43
- def decorate(root, constant)
44
- attribute_methods = attribute_methods_for(constant)
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 { params(constant: ::ActiveModel::Attributes::ClassMethods).returns(T::Array[[::String, ::String]]) }
64
- def attribute_methods_for(constant)
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 Compilers
12
- module Dsl
13
- # `Tapioca::Compilers::Dsl::ActiveModelSecurePassword` decorates RBI files for all
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 generator will produce an RBI file with the following content:
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 < Base
60
+ class ActiveModelSecurePassword < Compiler
61
61
  extend T::Sig
62
62
 
63
- sig do
64
- override
65
- .params(root: RBI::Tree, constant: T.all(Class, ::ActiveModel::SecurePassword::ClassMethods))
66
- .void
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