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.
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