tapioca 0.4.23 → 0.5.0

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 (63) 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 +139 -154
  34. data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
  35. data/lib/tapioca/compilers/symbol_table_compiler.rb +1 -1
  36. data/lib/tapioca/compilers/todos_compiler.rb +1 -1
  37. data/lib/tapioca/config.rb +2 -0
  38. data/lib/tapioca/config_builder.rb +4 -2
  39. data/lib/tapioca/constant_locator.rb +6 -8
  40. data/lib/tapioca/gemfile.rb +26 -19
  41. data/lib/tapioca/generator.rb +127 -43
  42. data/lib/tapioca/generic_type_registry.rb +25 -98
  43. data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
  44. data/lib/tapioca/internal.rb +1 -9
  45. data/lib/tapioca/loader.rb +14 -48
  46. data/lib/tapioca/rbi_ext/model.rb +122 -0
  47. data/lib/tapioca/reflection.rb +131 -0
  48. data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
  49. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
  50. data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
  51. data/lib/tapioca/version.rb +1 -1
  52. data/lib/tapioca.rb +2 -0
  53. metadata +35 -23
  54. data/lib/tapioca/cli/main.rb +0 -146
  55. data/lib/tapioca/core_ext/class.rb +0 -28
  56. data/lib/tapioca/core_ext/string.rb +0 -18
  57. data/lib/tapioca/rbi/model.rb +0 -405
  58. data/lib/tapioca/rbi/printer.rb +0 -410
  59. data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
  60. data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
  61. data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
  62. data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -82
  63. 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