tapioca 0.4.27 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +15 -15
  3. data/README.md +2 -2
  4. data/Rakefile +5 -7
  5. data/exe/tapioca +2 -2
  6. data/lib/tapioca/cli.rb +172 -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_model_secure_password.rb +101 -0
  13. data/lib/tapioca/compilers/dsl/active_record_associations.rb +33 -54
  14. data/lib/tapioca/compilers/dsl/active_record_columns.rb +10 -105
  15. data/lib/tapioca/compilers/dsl/active_record_enum.rb +8 -10
  16. data/lib/tapioca/compilers/dsl/active_record_fixtures.rb +86 -0
  17. data/lib/tapioca/compilers/dsl/active_record_scope.rb +7 -10
  18. data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +5 -8
  19. data/lib/tapioca/compilers/dsl/active_resource.rb +9 -37
  20. data/lib/tapioca/compilers/dsl/active_storage.rb +98 -0
  21. data/lib/tapioca/compilers/dsl/active_support_concern.rb +106 -0
  22. data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +13 -8
  23. data/lib/tapioca/compilers/dsl/base.rb +108 -82
  24. data/lib/tapioca/compilers/dsl/config.rb +111 -0
  25. data/lib/tapioca/compilers/dsl/frozen_record.rb +5 -7
  26. data/lib/tapioca/compilers/dsl/identity_cache.rb +66 -29
  27. data/lib/tapioca/compilers/dsl/mixed_in_class_attributes.rb +74 -0
  28. data/lib/tapioca/compilers/dsl/protobuf.rb +19 -69
  29. data/lib/tapioca/compilers/dsl/sidekiq_worker.rb +25 -12
  30. data/lib/tapioca/compilers/dsl/smart_properties.rb +21 -33
  31. data/lib/tapioca/compilers/dsl/state_machines.rb +56 -78
  32. data/lib/tapioca/compilers/dsl/url_helpers.rb +7 -10
  33. data/lib/tapioca/compilers/dsl_compiler.rb +25 -40
  34. data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +198 -0
  35. data/lib/tapioca/compilers/requires_compiler.rb +2 -2
  36. data/lib/tapioca/compilers/sorbet.rb +25 -5
  37. data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +122 -206
  38. data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
  39. data/lib/tapioca/compilers/symbol_table_compiler.rb +5 -11
  40. data/lib/tapioca/compilers/todos_compiler.rb +1 -1
  41. data/lib/tapioca/config.rb +3 -0
  42. data/lib/tapioca/config_builder.rb +5 -2
  43. data/lib/tapioca/constant_locator.rb +6 -8
  44. data/lib/tapioca/gemfile.rb +14 -11
  45. data/lib/tapioca/generators/base.rb +61 -0
  46. data/lib/tapioca/generators/dsl.rb +362 -0
  47. data/lib/tapioca/generators/gem.rb +345 -0
  48. data/lib/tapioca/generators/init.rb +79 -0
  49. data/lib/tapioca/generators/require.rb +52 -0
  50. data/lib/tapioca/generators/todo.rb +76 -0
  51. data/lib/tapioca/generators.rb +9 -0
  52. data/lib/tapioca/generic_type_registry.rb +25 -98
  53. data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
  54. data/lib/tapioca/internal.rb +2 -10
  55. data/lib/tapioca/loader.rb +11 -31
  56. data/lib/tapioca/rbi_ext/model.rb +166 -0
  57. data/lib/tapioca/reflection.rb +138 -0
  58. data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
  59. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
  60. data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
  61. data/lib/tapioca/version.rb +1 -1
  62. data/lib/tapioca.rb +3 -0
  63. metadata +45 -23
  64. data/lib/tapioca/cli/main.rb +0 -146
  65. data/lib/tapioca/core_ext/class.rb +0 -28
  66. data/lib/tapioca/core_ext/string.rb +0 -18
  67. data/lib/tapioca/generator.rb +0 -633
  68. data/lib/tapioca/rbi/model.rb +0 -405
  69. data/lib/tapioca/rbi/printer.rb +0 -410
  70. data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
  71. data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
  72. data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
  73. data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -86
  74. data/lib/tapioca/rbi/visitor.rb +0 -21
@@ -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
@@ -0,0 +1,101 @@
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::ActiveModelSecurePassword` decorates RBI files for all
14
+ # classes that use [`ActiveModel::SecurePassword`](http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html).
15
+ #
16
+ # For example, with the following class:
17
+ #
18
+ # ~~~rb
19
+ # class User
20
+ # include ActiveModel::SecurePassword
21
+ #
22
+ # has_secure_password
23
+ # has_secure_password :token
24
+ # end
25
+ # ~~~
26
+ #
27
+ # this generator will produce an RBI file with the following content:
28
+ # ~~~rbi
29
+ # # typed: true
30
+ #
31
+ # class User
32
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
33
+ # def authenticate(unencrypted_password); end
34
+ #
35
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
36
+ # def authenticate_password(unencrypted_password); end
37
+ #
38
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
39
+ # def authenticate_token(unencrypted_password); end
40
+ #
41
+ # sig { returns(T.untyped) }
42
+ # def password; end
43
+ #
44
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
45
+ # def password=(unencrypted_password); end
46
+ #
47
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
48
+ # def password_confirmation=(unencrypted_password); end
49
+ #
50
+ # sig { returns(T.untyped) }
51
+ # def token; end
52
+ #
53
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
54
+ # def token=(unencrypted_password); end
55
+ #
56
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
57
+ # def token_confirmation=(unencrypted_password); end
58
+ # end
59
+ # ~~~
60
+ class ActiveModelSecurePassword < Base
61
+ extend T::Sig
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)
69
+ instance_methods_modules = if constant < ActiveModel::SecurePassword::InstanceMethodsOnActivation
70
+ # pre Rails 6.0, this used to be a single static module
71
+ [ActiveModel::SecurePassword::InstanceMethodsOnActivation]
72
+ else
73
+ # post Rails 6.0, this is now using a dynmaic module builder pattern
74
+ # and we can have multiple different ones included into the model
75
+ constant.ancestors.grep(ActiveModel::SecurePassword::InstanceMethodsOnActivation)
76
+ end
77
+
78
+ return if instance_methods_modules.empty?
79
+
80
+ methods = instance_methods_modules.flat_map { |mod| mod.instance_methods(false) }
81
+ return if methods.empty?
82
+
83
+ root.create_path(constant) do |klass|
84
+ methods.each do |method|
85
+ create_method_from_def(klass, constant.instance_method(method))
86
+ end
87
+ end
88
+ end
89
+
90
+ sig { override.returns(T::Enumerable[Module]) }
91
+ def gather_constants
92
+ # This selects all classes that are `ActiveModel::SecurePassword::ClassMethods === klass`.
93
+ # In other words, we select all classes that have `ActiveModel::SecurePassword::ClassMethods`
94
+ # as an ancestor of its singleton class, i.e. all classes that have extended the
95
+ # `ActiveModel::SecurePassword::ClassMethods` module.
96
+ all_classes.grep(::ActiveModel::SecurePassword::ClassMethods)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ 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
@@ -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
@@ -99,11 +97,11 @@ module Tapioca
99
97
  class ActiveRecordColumns < Base
100
98
  extend T::Sig
101
99
 
102
- sig { override.params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(ActiveRecord::Base)).void }
100
+ sig { override.params(root: RBI::Tree, constant: T.class_of(ActiveRecord::Base)).void }
103
101
  def decorate(root, constant)
104
102
  return unless constant.table_exists?
105
103
 
106
- root.path(constant) do |model|
104
+ root.create_path(constant) do |model|
107
105
  module_name = "GeneratedAttributeMethods"
108
106
 
109
107
  model.create_module(module_name) do |mod|
@@ -129,34 +127,31 @@ module Tapioca
129
127
 
130
128
  sig { override.returns(T::Enumerable[Module]) }
131
129
  def gather_constants
132
- ActiveRecord::Base.descendants.reject(&:abstract_class?)
130
+ descendants_of(::ActiveRecord::Base).reject(&:abstract_class?)
133
131
  end
134
132
 
135
133
  private
136
134
 
137
135
  sig do
138
136
  params(
139
- klass: Parlour::RbiGenerator::Namespace,
137
+ klass: RBI::Scope,
140
138
  name: String,
141
139
  methods_to_add: T.nilable(T::Array[String]),
142
- return_type: T.nilable(String),
140
+ return_type: String,
143
141
  parameters: T::Array[[String, String]]
144
142
  ).void
145
143
  end
146
- def add_method(klass, name, methods_to_add, return_type: nil, parameters: [])
147
- create_method(
148
- klass,
144
+ def add_method(klass, name, methods_to_add, return_type: "void", parameters: [])
145
+ klass.create_method(
149
146
  name,
150
- parameters: parameters.map do |param, type|
151
- Parlour::RbiGenerator::Parameter.new(param, type: type)
152
- end,
147
+ parameters: parameters.map { |param, type| create_param(param, type: type) },
153
148
  return_type: return_type
154
149
  ) if methods_to_add.nil? || methods_to_add.include?(name)
155
150
  end
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
  column_name: String,
162
157
  attribute_name: String,
@@ -164,7 +159,7 @@ module Tapioca
164
159
  ).void
165
160
  end
166
161
  def add_methods_for_attribute(klass, constant, column_name, attribute_name = column_name, methods_to_add = nil)
167
- getter_type, setter_type = type_for(constant, column_name)
162
+ getter_type, setter_type = ActiveRecordColumnTypeHelper.new(constant).type_for(column_name)
168
163
 
169
164
  # Added by ActiveRecord::AttributeMethods::Read
170
165
  #
@@ -298,96 +293,6 @@ module Tapioca
298
293
  )
299
294
  end
300
295
 
301
- sig do
302
- params(
303
- constant: T.class_of(ActiveRecord::Base),
304
- column_name: String
305
- ).returns([String, String])
306
- end
307
- def type_for(constant, column_name)
308
- return ["T.untyped", "T.untyped"] if do_not_generate_strong_types?(constant)
309
-
310
- column_type = constant.attribute_types[column_name]
311
-
312
- getter_type =
313
- case column_type
314
- when defined?(MoneyColumn) && MoneyColumn::ActiveRecordType
315
- "::Money"
316
- when ActiveRecord::Type::Integer
317
- "::Integer"
318
- when ActiveRecord::Type::String
319
- "::String"
320
- when ActiveRecord::Type::Date
321
- "::Date"
322
- when ActiveRecord::Type::Decimal
323
- "::BigDecimal"
324
- when ActiveRecord::Type::Float
325
- "::Float"
326
- when ActiveRecord::Type::Boolean
327
- "T::Boolean"
328
- when ActiveRecord::Type::DateTime, ActiveRecord::Type::Time
329
- "::DateTime"
330
- when ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter
331
- "::ActiveSupport::TimeWithZone"
332
- else
333
- handle_unknown_type(column_type)
334
- end
335
-
336
- column = constant.columns_hash[column_name]
337
- setter_type = getter_type
338
-
339
- if column&.null
340
- return ["T.nilable(#{getter_type})", "T.nilable(#{setter_type})"]
341
- end
342
-
343
- if column_name == constant.primary_key ||
344
- column_name == "created_at" ||
345
- column_name == "updated_at"
346
- getter_type = "T.nilable(#{getter_type})"
347
- end
348
-
349
- [getter_type, setter_type]
350
- end
351
-
352
- sig { params(constant: Module).returns(T::Boolean) }
353
- def do_not_generate_strong_types?(constant)
354
- Object.const_defined?(:StrongTypeGeneration) &&
355
- !(constant.singleton_class < Object.const_get(:StrongTypeGeneration))
356
- end
357
-
358
- sig { params(column_type: Object).returns(String) }
359
- def handle_unknown_type(column_type)
360
- return "T.untyped" unless ActiveModel::Type::Value === column_type
361
-
362
- lookup_return_type_of_method(column_type, :deserialize) ||
363
- lookup_return_type_of_method(column_type, :cast) ||
364
- lookup_arg_type_of_method(column_type, :serialize) ||
365
- "T.untyped"
366
- end
367
-
368
- sig { params(column_type: ActiveModel::Type::Value, method: Symbol).returns(T.nilable(String)) }
369
- def lookup_return_type_of_method(column_type, method)
370
- signature = T::Private::Methods.signature_for_method(column_type.method(method))
371
- return unless signature
372
-
373
- return_type = signature.return_type
374
- return if return_type == T::Private::Types::Void || return_type == T::Private::Types::NotTyped
375
-
376
- return_type.to_s
377
- end
378
-
379
- sig { params(column_type: ActiveModel::Type::Value, method: Symbol).returns(T.nilable(String)) }
380
- def lookup_arg_type_of_method(column_type, method)
381
- signature = T::Private::Methods.signature_for_method(column_type.method(method))
382
- return unless signature
383
-
384
- # Arg types is an array [name, type] entries, so we desctructure the type of
385
- # first argument to get the first argument type
386
- _, first_argument_type = signature.arg_types.first
387
-
388
- first_argument_type.to_s
389
- end
390
-
391
296
  sig { params(type: String).returns(String) }
392
297
  def as_nilable_type(type)
393
298
  return type if type.start_with?("T.nilable(")
@@ -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
@@ -60,11 +58,11 @@ module Tapioca
60
58
  class ActiveRecordEnum < Base
61
59
  extend T::Sig
62
60
 
63
- sig { override.params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(::ActiveRecord::Base)).void }
61
+ sig { override.params(root: RBI::Tree, constant: T.class_of(::ActiveRecord::Base)).void }
64
62
  def decorate(root, constant)
65
63
  return if constant.defined_enums.empty?
66
64
 
67
- root.path(constant) do |model|
65
+ root.create_path(constant) do |model|
68
66
  module_name = "EnumMethodsModule"
69
67
 
70
68
  model.create_module(module_name) do |mod|
@@ -75,14 +73,14 @@ module Tapioca
75
73
 
76
74
  constant.defined_enums.each do |name, enum_map|
77
75
  type = type_for_enum(enum_map)
78
- create_method(model, name.pluralize, class_method: true, return_type: type)
76
+ model.create_method(name.pluralize, class_method: true, return_type: type)
79
77
  end
80
78
  end
81
79
  end
82
80
 
83
81
  sig { override.returns(T::Enumerable[Module]) }
84
82
  def gather_constants
85
- ::ActiveRecord::Base.descendants.reject(&:abstract_class?)
83
+ descendants_of(::ActiveRecord::Base).reject(&:abstract_class?)
86
84
  end
87
85
 
88
86
  private
@@ -93,21 +91,21 @@ module Tapioca
93
91
  value_type = if value_type.length == 1
94
92
  value_type.first
95
93
  else
96
- "T.any(#{value_type.join(', ')})"
94
+ "T.any(#{value_type.join(", ")})"
97
95
  end
98
96
 
99
97
  "T::Hash[T.any(String, Symbol), #{value_type}]"
100
98
  end
101
99
 
102
- sig { params(constant: T.class_of(::ActiveRecord::Base), klass: Parlour::RbiGenerator::Namespace).void }
100
+ sig { params(constant: T.class_of(::ActiveRecord::Base), klass: RBI::Scope).void }
103
101
  def generate_instance_methods(constant, klass)
104
102
  methods = constant.send(:_enum_methods_module).instance_methods
105
103
 
106
104
  methods.each do |method|
107
105
  method = method.to_s
108
- return_type = "T::Boolean" if method.end_with?("?")
106
+ return_type = method.end_with?("?") ? "T::Boolean" : "void"
109
107
 
110
- create_method(klass, method, return_type: return_type)
108
+ klass.create_method(method, return_type: return_type)
111
109
  end
112
110
  end
113
111
  end