tapioca 0.4.27 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +14 -14
  3. data/README.md +2 -2
  4. data/Rakefile +5 -7
  5. data/exe/tapioca +2 -2
  6. data/lib/tapioca/cli.rb +256 -2
  7. data/lib/tapioca/compilers/dsl/aasm.rb +122 -0
  8. data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +52 -12
  9. data/lib/tapioca/compilers/dsl/action_mailer.rb +6 -9
  10. data/lib/tapioca/compilers/dsl/active_job.rb +8 -12
  11. data/lib/tapioca/compilers/dsl/active_model_attributes.rb +131 -0
  12. data/lib/tapioca/compilers/dsl/active_record_associations.rb +33 -54
  13. data/lib/tapioca/compilers/dsl/active_record_columns.rb +10 -105
  14. data/lib/tapioca/compilers/dsl/active_record_enum.rb +8 -10
  15. data/lib/tapioca/compilers/dsl/active_record_scope.rb +7 -10
  16. data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +5 -8
  17. data/lib/tapioca/compilers/dsl/active_resource.rb +9 -37
  18. data/lib/tapioca/compilers/dsl/active_storage.rb +98 -0
  19. data/lib/tapioca/compilers/dsl/active_support_concern.rb +108 -0
  20. data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +13 -8
  21. data/lib/tapioca/compilers/dsl/base.rb +96 -82
  22. data/lib/tapioca/compilers/dsl/config.rb +111 -0
  23. data/lib/tapioca/compilers/dsl/frozen_record.rb +5 -7
  24. data/lib/tapioca/compilers/dsl/identity_cache.rb +66 -29
  25. data/lib/tapioca/compilers/dsl/protobuf.rb +19 -69
  26. data/lib/tapioca/compilers/dsl/sidekiq_worker.rb +25 -12
  27. data/lib/tapioca/compilers/dsl/smart_properties.rb +19 -31
  28. data/lib/tapioca/compilers/dsl/state_machines.rb +56 -78
  29. data/lib/tapioca/compilers/dsl/url_helpers.rb +7 -10
  30. data/lib/tapioca/compilers/dsl_compiler.rb +22 -38
  31. data/lib/tapioca/compilers/requires_compiler.rb +2 -2
  32. data/lib/tapioca/compilers/sorbet.rb +26 -5
  33. data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +138 -153
  34. data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
  35. data/lib/tapioca/compilers/todos_compiler.rb +1 -1
  36. data/lib/tapioca/config.rb +2 -0
  37. data/lib/tapioca/config_builder.rb +4 -2
  38. data/lib/tapioca/constant_locator.rb +6 -8
  39. data/lib/tapioca/gemfile.rb +2 -4
  40. data/lib/tapioca/generator.rb +124 -40
  41. data/lib/tapioca/generic_type_registry.rb +25 -98
  42. data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
  43. data/lib/tapioca/internal.rb +2 -9
  44. data/lib/tapioca/loader.rb +13 -33
  45. data/lib/tapioca/rbi_ext/model.rb +122 -0
  46. data/lib/tapioca/reflection.rb +131 -0
  47. data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
  48. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
  49. data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
  50. data/lib/tapioca/version.rb +1 -1
  51. data/lib/tapioca.rb +2 -1
  52. metadata +34 -22
  53. data/lib/tapioca/cli/main.rb +0 -146
  54. data/lib/tapioca/core_ext/class.rb +0 -28
  55. data/lib/tapioca/core_ext/string.rb +0 -18
  56. data/lib/tapioca/rbi/model.rb +0 -405
  57. data/lib/tapioca/rbi/printer.rb +0 -410
  58. data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
  59. data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
  60. data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
  61. data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -86
  62. data/lib/tapioca/rbi/visitor.rb +0 -21
@@ -1,8 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "parlour"
5
-
6
4
  begin
7
5
  require "action_controller"
8
6
  rescue LoadError
@@ -72,38 +70,54 @@ module Tapioca
72
70
 
73
71
  sig do
74
72
  override
75
- .params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(::ActionController::Base))
73
+ .params(root: RBI::Tree, constant: T.class_of(::ActionController::Base))
76
74
  .void
77
75
  end
78
76
  def decorate(root, constant)
77
+ helpers_module = constant._helpers
78
+ proxied_helper_methods = constant._helper_methods.map(&:to_s).map(&:to_sym)
79
+
79
80
  helper_proxy_name = "HelperProxy"
80
81
  helper_methods_name = "HelperMethods"
81
- proxied_helper_methods = constant._helper_methods.map(&:to_s).map(&:to_sym)
82
82
 
83
83
  # Define the helpers method
84
- root.path(constant) do |controller|
85
- create_method(controller, 'helpers', return_type: helper_proxy_name)
84
+ root.create_path(constant) do |controller|
85
+ controller.create_method("helpers", return_type: helper_proxy_name)
86
86
 
87
87
  # Create helper method module
88
88
  controller.create_module(helper_methods_name) do |helper_methods|
89
- helpers_module = constant._helpers
89
+ # If the controller has no helper defined, then it just inherits
90
+ # the Action Controlller base helper methods module, so we should
91
+ # just add that as an include and stop doing more processing.
92
+ if helpers_module.name == "ActionController::Base::HelperMethods"
93
+ next helper_methods.create_include(T.must(qualified_name_of(helpers_module)))
94
+ end
90
95
 
96
+ # Find all the included helper modules and generate an include
97
+ # for each of those helper modules
91
98
  gather_includes(helpers_module).each do |ancestor|
92
99
  helper_methods.create_include(ancestor)
93
100
  end
94
101
 
102
+ # Generate a method definition in the helper module for each
103
+ # helper method defined via the `helper_method` call in the controller.
95
104
  helpers_module.instance_methods(false).each do |method_name|
96
105
  method = if proxied_helper_methods.include?(method_name)
97
- constant.instance_method(method_name)
106
+ helper_method_proxy_target(constant, method_name)
98
107
  else
99
108
  helpers_module.instance_method(method_name)
100
109
  end
101
- create_method_from_def(helper_methods, method)
110
+
111
+ if method
112
+ create_method_from_def(helper_methods, method)
113
+ else
114
+ create_unknown_proxy_method(helper_methods, method_name)
115
+ end
102
116
  end
103
117
  end
104
118
 
105
119
  # Create helper proxy class
106
- controller.create_class(helper_proxy_name, superclass: "::ActionView::Base") do |proxy|
120
+ controller.create_class(helper_proxy_name, superclass_name: "::ActionView::Base") do |proxy|
107
121
  proxy.create_include(helper_methods_name)
108
122
  end
109
123
  end
@@ -111,16 +125,42 @@ module Tapioca
111
125
 
112
126
  sig { override.returns(T::Enumerable[Module]) }
113
127
  def gather_constants
114
- ::ActionController::Base.descendants.reject(&:abstract?).select(&:name)
128
+ descendants_of(::ActionController::Base).reject(&:abstract?).select(&:name)
115
129
  end
116
130
 
117
131
  private
118
132
 
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)
140
+ # Lookup the proxy target method only if it is defined as a public/protected or private method.
141
+ if constant.method_defined?(method_name) || constant.private_method_defined?(method_name)
142
+ constant.instance_method(method_name)
143
+ end
144
+ end
145
+
146
+ sig { params(helper_methods: RBI::Scope, method_name: Symbol).void }
147
+ def create_unknown_proxy_method(helper_methods, method_name)
148
+ helper_methods.create_method(
149
+ method_name.to_s,
150
+ parameters: [
151
+ create_rest_param("args", type: "T.untyped"),
152
+ create_kw_rest_param("kwargs", type: "T.untyped"),
153
+ create_block_param("blk", type: "T.untyped"),
154
+ ],
155
+ return_type: "T.untyped"
156
+ )
157
+ end
158
+
119
159
  sig { params(mod: Module).returns(T::Array[String]) }
120
160
  def gather_includes(mod)
121
161
  mod.ancestors
122
162
  .reject { |ancestor| ancestor.is_a?(Class) || ancestor == mod || ancestor.name.nil? }
123
- .map { |ancestor| "::#{ancestor.name}" }
163
+ .map { |ancestor| T.must(qualified_name_of(ancestor)) }
124
164
  .reverse
125
165
  end
126
166
  end
@@ -1,8 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "parlour"
5
-
6
4
  begin
7
5
  require "action_mailer"
8
6
  rescue LoadError
@@ -38,17 +36,16 @@ module Tapioca
38
36
  class ActionMailer < Base
39
37
  extend T::Sig
40
38
 
41
- sig { override.params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(::ActionMailer::Base)).void }
39
+ sig { override.params(root: RBI::Tree, constant: T.class_of(::ActionMailer::Base)).void }
42
40
  def decorate(root, constant)
43
- root.path(constant) do |mailer|
41
+ root.create_path(constant) do |mailer|
44
42
  constant.action_methods.to_a.each do |mailer_method|
45
43
  method_def = constant.instance_method(mailer_method)
46
- parameters = compile_method_parameters_to_parlour(method_def)
47
- create_method(
48
- mailer,
44
+ parameters = compile_method_parameters_to_rbi(method_def)
45
+ mailer.create_method(
49
46
  mailer_method,
50
47
  parameters: parameters,
51
- return_type: '::ActionMailer::MessageDelivery',
48
+ return_type: "::ActionMailer::MessageDelivery",
52
49
  class_method: true
53
50
  )
54
51
  end
@@ -57,7 +54,7 @@ module Tapioca
57
54
 
58
55
  sig { override.returns(T::Enumerable[Module]) }
59
56
  def gather_constants
60
- ::ActionMailer::Base.descendants.reject(&:abstract?)
57
+ descendants_of(::ActionMailer::Base).reject(&:abstract?)
61
58
  end
62
59
  end
63
60
  end
@@ -1,8 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "parlour"
5
-
6
4
  begin
7
5
  require "active_job"
8
6
  rescue LoadError
@@ -42,25 +40,23 @@ module Tapioca
42
40
  class ActiveJob < Base
43
41
  extend T::Sig
44
42
 
45
- sig { override.params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(::ActiveJob::Base)).void }
43
+ sig { override.params(root: RBI::Tree, constant: T.class_of(::ActiveJob::Base)).void }
46
44
  def decorate(root, constant)
47
- root.path(constant) do |job|
48
- next unless constant.instance_methods(false).include?(:perform)
45
+ return unless constant.instance_methods(false).include?(:perform)
49
46
 
47
+ root.create_path(constant) do |job|
50
48
  method = constant.instance_method(:perform)
51
- parameters = compile_method_parameters_to_parlour(method)
52
- return_type = compile_method_return_type_to_parlour(method)
49
+ parameters = compile_method_parameters_to_rbi(method)
50
+ return_type = compile_method_return_type_to_rbi(method)
53
51
 
54
- create_method(
55
- job,
52
+ job.create_method(
56
53
  "perform_later",
57
54
  parameters: parameters,
58
55
  return_type: "T.any(#{constant.name}, FalseClass)",
59
56
  class_method: true
60
57
  )
61
58
 
62
- create_method(
63
- job,
59
+ job.create_method(
64
60
  "perform_now",
65
61
  parameters: parameters,
66
62
  return_type: return_type,
@@ -71,7 +67,7 @@ module Tapioca
71
67
 
72
68
  sig { override.returns(T::Enumerable[Module]) }
73
69
  def gather_constants
74
- ::ActiveJob::Base.descendants
70
+ descendants_of(::ActiveJob::Base)
75
71
  end
76
72
  end
77
73
  end
@@ -0,0 +1,131 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "active_model"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ module Tapioca
11
+ module Compilers
12
+ module Dsl
13
+ # `Tapioca::Compilers::Dsl::ActiveModelAttributes` decorates RBI files for all
14
+ # classes that use [`ActiveModel::Attributes`](https://edgeapi.rubyonrails.org/classes/ActiveModel/Attributes/ClassMethods.html).
15
+ #
16
+ # For example, with the following class:
17
+ #
18
+ # ~~~rb
19
+ # class Shop
20
+ # include ActiveModel::Attributes
21
+ #
22
+ # attribute :name, :string
23
+ # end
24
+ # ~~~
25
+ #
26
+ # this generator will produce an RBI file with the following content:
27
+ # ~~~rbi
28
+ # # typed: true
29
+ #
30
+ # class Shop
31
+ #
32
+ # sig { returns(T.nilable(::String)) }
33
+ # def name; end
34
+ #
35
+ # sig { params(name: T.nilable(::String)).returns(T.nilable(::String)) }
36
+ # def name=(name); end
37
+ # end
38
+ # ~~~
39
+ class ActiveModelAttributes < Base
40
+ extend T::Sig
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)
45
+ return if attribute_methods.empty?
46
+
47
+ root.create_path(constant) do |klass|
48
+ attribute_methods.each do |method, attribute_type|
49
+ generate_method(klass, method, attribute_type)
50
+ end
51
+ end
52
+ end
53
+
54
+ sig { override.returns(T::Enumerable[Module]) }
55
+ def gather_constants
56
+ all_classes.grep(::ActiveModel::Attributes::ClassMethods)
57
+ end
58
+
59
+ private
60
+
61
+ HANDLED_METHOD_TARGETS = T.let(["attribute", "attribute="], T::Array[String])
62
+
63
+ sig { params(constant: ::ActiveModel::Attributes::ClassMethods).returns(T::Array[[::String, ::String]]) }
64
+ def attribute_methods_for(constant)
65
+ constant.attribute_method_matchers.flat_map do |matcher|
66
+ constant.attribute_types.map do |name, value|
67
+ next unless handle_method_matcher?(matcher)
68
+
69
+ [matcher.method_name(name), type_for(value)]
70
+ end.compact
71
+ end
72
+ end
73
+
74
+ sig do
75
+ params(matcher: ::ActiveModel::AttributeMethods::ClassMethods::AttributeMethodMatcher)
76
+ .returns(T::Boolean)
77
+ end
78
+ def handle_method_matcher?(matcher)
79
+ target = if matcher.respond_to?(:method_missing_target)
80
+ # Pre-Rails 6.0, the field is named "method_missing_target"
81
+ T.unsafe(matcher).method_missing_target
82
+ else
83
+ # Rails 6.0+ has renamed the field to "target"
84
+ matcher.target
85
+ end
86
+
87
+ HANDLED_METHOD_TARGETS.include?(target.to_s)
88
+ end
89
+
90
+ sig { params(attribute_type_value: ::ActiveModel::Type::Value).returns(::String) }
91
+ def type_for(attribute_type_value)
92
+ type = case attribute_type_value
93
+ when ActiveModel::Type::Boolean
94
+ "T::Boolean"
95
+ when ActiveModel::Type::Date
96
+ "::Date"
97
+ when ActiveModel::Type::DateTime, ActiveModel::Type::Time
98
+ "::DateTime"
99
+ when ActiveModel::Type::Decimal
100
+ "::BigDecimal"
101
+ when ActiveModel::Type::Float
102
+ "::Float"
103
+ when ActiveModel::Type::Integer
104
+ "::Integer"
105
+ when ActiveModel::Type::String
106
+ "::String"
107
+ else
108
+ # we don't want untyped to be wrapped by T.nilable, so just return early
109
+ return "T.untyped"
110
+ end
111
+
112
+ "T.nilable(#{type})"
113
+ end
114
+
115
+ sig { params(klass: RBI::Scope, method: String, type: String).void }
116
+ def generate_method(klass, method, type)
117
+ if method.end_with?("=")
118
+ parameter = create_param("value", type: type)
119
+ klass.create_method(
120
+ method,
121
+ parameters: [parameter],
122
+ return_type: type
123
+ )
124
+ else
125
+ klass.create_method(method, return_type: type)
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -1,8 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "parlour"
5
-
6
4
  begin
7
5
  require "active_record"
8
6
  rescue LoadError
@@ -70,7 +68,7 @@ module Tapioca
70
68
  # sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
71
69
  # def comment_ids=(ids); end
72
70
  #
73
- # sig { returns(::ActiveRecord::Associations::CollectionProxy[Comment]) }
71
+ # sig { returns(::ActiveRecord::Associations::CollectionProxy[::Comment]) }
74
72
  # def comments; end
75
73
  #
76
74
  # sig { params(value: T::Enumerable[::Comment]).void }
@@ -106,11 +104,11 @@ module Tapioca
106
104
  T.any(::ActiveRecord::Reflection::ThroughReflection, ::ActiveRecord::Reflection::AssociationReflection)
107
105
  end
108
106
 
109
- sig { override.params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(ActiveRecord::Base)).void }
107
+ sig { override.params(root: RBI::Tree, constant: T.class_of(ActiveRecord::Base)).void }
110
108
  def decorate(root, constant)
111
109
  return if constant.reflections.empty?
112
110
 
113
- root.path(constant) do |model|
111
+ root.create_path(constant) do |model|
114
112
  module_name = "GeneratedAssociationMethods"
115
113
 
116
114
  model.create_module(module_name) do |mod|
@@ -124,26 +122,23 @@ module Tapioca
124
122
 
125
123
  sig { override.returns(T::Enumerable[Module]) }
126
124
  def gather_constants
127
- ActiveRecord::Base.descendants.reject(&:abstract_class?)
125
+ descendants_of(::ActiveRecord::Base).reject(&:abstract_class?)
128
126
  end
129
127
 
130
128
  private
131
129
 
132
- sig { params(mod: Parlour::RbiGenerator::Namespace, constant: T.class_of(ActiveRecord::Base)).void }
130
+ sig { params(mod: RBI::Scope, constant: T.class_of(ActiveRecord::Base)).void }
133
131
  def populate_nested_attribute_writers(mod, constant)
134
132
  constant.nested_attributes_options.keys.each do |association_name|
135
- create_method(
136
- mod,
133
+ mod.create_method(
137
134
  "#{association_name}_attributes=",
138
- parameters: [
139
- Parlour::RbiGenerator::Parameter.new("attributes", type: "T.untyped"),
140
- ],
135
+ parameters: [create_param("attributes", type: "T.untyped")],
141
136
  return_type: "T.untyped"
142
137
  )
143
138
  end
144
139
  end
145
140
 
146
- sig { params(mod: Parlour::RbiGenerator::Namespace, constant: T.class_of(ActiveRecord::Base)).void }
141
+ sig { params(mod: RBI::Scope, constant: T.class_of(ActiveRecord::Base)).void }
147
142
  def populate_associations(mod, constant)
148
143
  constant.reflections.each do |association_name, reflection|
149
144
  if reflection.collection?
@@ -156,7 +151,7 @@ module Tapioca
156
151
 
157
152
  sig do
158
153
  params(
159
- klass: Parlour::RbiGenerator::Namespace,
154
+ klass: RBI::Scope,
160
155
  constant: T.class_of(ActiveRecord::Base),
161
156
  association_name: T.any(String, Symbol),
162
157
  reflection: ReflectionType
@@ -166,49 +161,41 @@ module Tapioca
166
161
  association_class = type_for(constant, reflection)
167
162
  association_type = "T.nilable(#{association_class})"
168
163
 
169
- create_method(
170
- klass,
164
+ klass.create_method(
171
165
  association_name.to_s,
172
166
  return_type: association_type,
173
167
  )
174
- create_method(
175
- klass,
168
+ klass.create_method(
176
169
  "#{association_name}=",
177
- parameters: [
178
- Parlour::RbiGenerator::Parameter.new("value", type: association_type),
179
- ],
180
- return_type: nil
170
+ parameters: [create_param("value", type: association_type)],
171
+ return_type: "void"
181
172
  )
182
- create_method(
183
- klass,
173
+ klass.create_method(
184
174
  "reload_#{association_name}",
185
175
  return_type: association_type,
186
176
  )
187
177
  unless reflection.polymorphic?
188
- create_method(
189
- klass,
178
+ klass.create_method(
190
179
  "build_#{association_name}",
191
180
  parameters: [
192
- Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped"),
193
- Parlour::RbiGenerator::Parameter.new("&blk", type: "T.untyped"),
181
+ create_rest_param("args", type: "T.untyped"),
182
+ create_block_param("blk", type: "T.untyped"),
194
183
  ],
195
184
  return_type: association_class
196
185
  )
197
- create_method(
198
- klass,
186
+ klass.create_method(
199
187
  "create_#{association_name}",
200
188
  parameters: [
201
- Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped"),
202
- Parlour::RbiGenerator::Parameter.new("&blk", type: "T.untyped"),
189
+ create_rest_param("args", type: "T.untyped"),
190
+ create_block_param("blk", type: "T.untyped"),
203
191
  ],
204
192
  return_type: association_class
205
193
  )
206
- create_method(
207
- klass,
194
+ klass.create_method(
208
195
  "create_#{association_name}!",
209
196
  parameters: [
210
- Parlour::RbiGenerator::Parameter.new("*args", type: "T.untyped"),
211
- Parlour::RbiGenerator::Parameter.new("&blk", type: "T.untyped"),
197
+ create_rest_param("args", type: "T.untyped"),
198
+ create_block_param("blk", type: "T.untyped"),
212
199
  ],
213
200
  return_type: association_class
214
201
  )
@@ -217,7 +204,7 @@ module Tapioca
217
204
 
218
205
  sig do
219
206
  params(
220
- klass: Parlour::RbiGenerator::Namespace,
207
+ klass: RBI::Scope,
221
208
  constant: T.class_of(ActiveRecord::Base),
222
209
  association_name: T.any(String, Symbol),
223
210
  reflection: ReflectionType
@@ -227,30 +214,22 @@ module Tapioca
227
214
  association_class = type_for(constant, reflection)
228
215
  relation_class = relation_type_for(constant, reflection)
229
216
 
230
- create_method(
231
- klass,
217
+ klass.create_method(
232
218
  association_name.to_s,
233
219
  return_type: relation_class,
234
220
  )
235
- create_method(
236
- klass,
221
+ klass.create_method(
237
222
  "#{association_name}=",
238
- parameters: [
239
- Parlour::RbiGenerator::Parameter.new("value", type: "T::Enumerable[#{association_class}]"),
240
- ],
241
- return_type: nil,
223
+ parameters: [create_param("value", type: "T::Enumerable[#{association_class}]")],
224
+ return_type: "void",
242
225
  )
243
- create_method(
244
- klass,
226
+ klass.create_method(
245
227
  "#{association_name.to_s.singularize}_ids",
246
228
  return_type: "T::Array[T.untyped]"
247
229
  )
248
- create_method(
249
- klass,
230
+ klass.create_method(
250
231
  "#{association_name.to_s.singularize}_ids=",
251
- parameters: [
252
- Parlour::RbiGenerator::Parameter.new("ids", type: "T::Array[T.untyped]"),
253
- ],
232
+ parameters: [create_param("ids", type: "T::Array[T.untyped]")],
254
233
  return_type: "T::Array[T.untyped]"
255
234
  )
256
235
  end
@@ -264,7 +243,7 @@ module Tapioca
264
243
  def type_for(constant, reflection)
265
244
  return "T.untyped" if !constant.table_exists? || polymorphic_association?(reflection)
266
245
 
267
- "::#{reflection.klass.name}"
246
+ T.must(qualified_name_of(reflection.klass))
268
247
  end
269
248
 
270
249
  sig do
@@ -278,7 +257,7 @@ module Tapioca
278
257
  polymorphic_association?(reflection)
279
258
 
280
259
  # Change to: "::#{reflection.klass.name}::ActiveRecord_Associations_CollectionProxy"
281
- "::ActiveRecord::Associations::CollectionProxy[#{reflection.klass.name}]"
260
+ "::ActiveRecord::Associations::CollectionProxy[#{qualified_name_of(reflection.klass)}]"
282
261
  end
283
262
 
284
263
  sig do