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
@@ -1,720 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- begin
5
- require "active_record"
6
- rescue LoadError
7
- return
8
- end
9
-
10
- require "tapioca/compilers/dsl/helper/active_record_constants"
11
-
12
- module Tapioca
13
- module Compilers
14
- module Dsl
15
- # `Tapioca::Compilers::Dsl::ActiveRecordRelations` decorates RBI files for subclasses of
16
- # `ActiveRecord::Base` and adds
17
- # [relation](http://api.rubyonrails.org/classes/ActiveRecord/Relation.html),
18
- # [collection proxy](https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html),
19
- # [query](http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html),
20
- # [spawn](http://api.rubyonrails.org/classes/ActiveRecord/SpawnMethods.html),
21
- # [finder](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html), and
22
- # [calculation](http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html) methods.
23
- #
24
- # The generator defines 3 (synthetic) modules and 3 (synthetic) classes to represent relations properly.
25
- #
26
- # For a given model `Model`, we generate the following classes:
27
- #
28
- # 1. A `Model::PrivateRelation` that subclasses `ActiveRecord::Relation`. This synthetic class represents
29
- # a relation on `Model` whose methods which return a relation always return a `Model::PrivateRelation` instance.
30
- #
31
- # 2. `Model::PrivateAssocationRelation` that subclasses `ActiveRecord::AssociationRelation`. This synthetic
32
- # class represents a relation on a singular association of type `Model` (e.g. `foo.model`) whose methods which
33
- # return a relation will always return a `Model::PrivateAssocationRelation` instance. The difference between this
34
- # class and the previous one is mainly that an association relation also keeps track of the resource association
35
- # for this relation.
36
- #
37
- # 3. `Model::PrivateCollectionProxy` that subclasses from `ActiveRecord::Associations::CollectionProxy`.
38
- # This synthetic class represents a relation on a plural association of type `Model` (e.g. `foo.models`)
39
- # whose methods which return a relation will always return a `Model::PrivateAssocationRelation` instance.
40
- # This class represents a collection of `Model` instances with some extra methods to `build`, `create`,
41
- # etc new `Model` instances in the collection.
42
- #
43
- # and the following modules:
44
- #
45
- # 1. `Model::GeneratedRelationMethods` holds all the relation methods with the return type of
46
- # `Model::PrivateRelation`. For example, calling `all` on the `Model` class or an instance of
47
- # `Model::PrivateRelation` class will always return a `Model::PrivateRelation` instance, thus the
48
- # signature of `all` is defined with that return type in this module.
49
- #
50
- # 2. `Model::GeneratedAssociationRelationMethods` holds all the relation methods with the return type
51
- # of `Model::PrivateAssociationRelation`. For example, calling `all` on an instance of
52
- # `Model::PrivateAssociationRelation` or an instance of `Model::PrivateCollectionProxy` class will
53
- # always return a `Model::PrivateAssociationRelation` instance, thus the signature of `all` is defined
54
- # with that return type in this module.
55
- #
56
- # 3. `Model::CommonRelationMethods` holds all the relation methods that do not depend on the type of
57
- # relation in their return type. For example, `find_by!` will always return the same type (a `Model`
58
- # instance), regardless of what kind of relation it is called on, and so belongs in this module.
59
- # This module is used to reduce the replication of methods between the previous two modules.
60
- #
61
- # Additionally, the actual `Model` class extends both `Model::CommonRelationMethods` and
62
- # `Model::PrivateRelation` modules, so that, for example, `find_by` and `all` can be chained off of the
63
- # `Model` class.
64
- #
65
- # **CAUTION**: The generated relation classes are named `PrivateXXX` intentionally to reflect the fact
66
- # that they represent private subconstants of the Active Record model. As such, these types do not
67
- # exist at runtime, and their counterparts that do exist at runtime are marked `private_constant` anyway.
68
- # For that reason, these types cannot be used in user code or in `sig`s inside Ruby files, since that will
69
- # make the runtime checks fail.
70
- #
71
- # For example, with the following `ActiveRecord::Base` subclass:
72
- #
73
- # ~~~rb
74
- # class Post < ApplicationRecord
75
- # end
76
- # ~~~
77
- #
78
- # this generator will produce the RBI file `post.rbi` with the following content:
79
- # ~~~rbi
80
- # # post.rbi
81
- # # typed: true
82
- #
83
- # class Post
84
- # extend CommonRelationMethods
85
- # extend GeneratedRelationMethods
86
- #
87
- # module CommonRelationMethods
88
- # sig { params(block: T.nilable(T.proc.params(record: ::Post).returns(T.untyped))).returns(T::Boolean) }
89
- # def any?(&block); end
90
- #
91
- # # ...
92
- # end
93
- #
94
- # module GeneratedAssociationRelationMethods
95
- # sig { returns(PrivateAssociationRelation) }
96
- # def all; end
97
- #
98
- # # ...
99
- #
100
- # sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
101
- # def where(*args, &blk); end
102
- # end
103
- #
104
- # module GeneratedRelationMethods
105
- # sig { returns(PrivateRelation) }
106
- # def all; end
107
- #
108
- # # ...
109
- #
110
- # sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
111
- # def where(*args, &blk); end
112
- # end
113
- #
114
- # class PrivateAssociationRelation < ::ActiveRecord::AssociationRelation
115
- # include CommonRelationMethods
116
- # include GeneratedAssociationRelationMethods
117
- #
118
- # sig { returns(T::Array[::Post]) }
119
- # def to_ary; end
120
- #
121
- # Elem = type_member(fixed: ::Post)
122
- # end
123
- #
124
- # class PrivateCollectionProxy < ::ActiveRecord::Associations::CollectionProxy
125
- # include CommonRelationMethods
126
- # include GeneratedAssociationRelationMethods
127
- #
128
- # sig do
129
- # params(records: T.any(::Post, T::Array[::Post], T::Array[PrivateCollectionProxy]))
130
- # .returns(PrivateCollectionProxy)
131
- # end
132
- # def <<(*records); end
133
- #
134
- # # ...
135
- # end
136
- #
137
- # class PrivateRelation < ::ActiveRecord::Relation
138
- # include CommonRelationMethods
139
- # include GeneratedRelationMethods
140
- #
141
- # sig { returns(T::Array[::Post]) }
142
- # def to_ary; end
143
- #
144
- # Elem = type_member(fixed: ::Post)
145
- # end
146
- # end
147
- # ~~~
148
- class ActiveRecordRelations < Base
149
- extend T::Sig
150
-
151
- sig do
152
- override
153
- .params(root: RBI::Tree, constant: T.class_of(::ActiveRecord::Base))
154
- .void
155
- end
156
- def decorate(root, constant)
157
- root.create_path(constant) do |model|
158
- constant_name = T.must(qualified_name_of(constant))
159
- RelationGenerator.new(model, constant_name).generate
160
- end
161
- end
162
-
163
- sig { override.returns(T::Enumerable[Module]) }
164
- def gather_constants
165
- ActiveRecord::Base.descendants.reject(&:abstract_class?)
166
- end
167
-
168
- class RelationGenerator
169
- extend T::Sig
170
- include ParamHelper
171
- include Reflection
172
- include Helper::ActiveRecordConstants
173
-
174
- sig do
175
- params(
176
- model: RBI::Scope,
177
- constant_name: String
178
- ).void
179
- end
180
- def initialize(model, constant_name)
181
- @model = model
182
- @constant_name = constant_name
183
- @relation_methods_module = T.let(
184
- model.create_module(RelationMethodsModuleName),
185
- RBI::Scope
186
- )
187
- @association_relation_methods_module = T.let(
188
- model.create_module(AssociationRelationMethodsModuleName),
189
- RBI::Scope
190
- )
191
- @common_relation_methods_module = T.let(
192
- model.create_module(CommonRelationMethodsModuleName),
193
- RBI::Scope
194
- )
195
- end
196
-
197
- sig { void }
198
- def generate
199
- create_classes_and_includes
200
- create_common_methods
201
- create_relation_methods
202
- create_association_relation_methods
203
- end
204
-
205
- ASSOCIATION_METHODS = T.let(
206
- ::ActiveRecord::AssociationRelation.instance_methods -
207
- ::ActiveRecord::Relation.instance_methods,
208
- T::Array[Symbol]
209
- )
210
- COLLECTION_PROXY_METHODS = T.let(
211
- ::ActiveRecord::Associations::CollectionProxy.instance_methods -
212
- ::ActiveRecord::AssociationRelation.instance_methods,
213
- T::Array[Symbol]
214
- )
215
-
216
- QUERY_METHODS = T.let(begin
217
- # Grab all Query methods
218
- query_methods = ActiveRecord::QueryMethods.instance_methods(false)
219
- # Grab all Spawn methods
220
- query_methods |= ActiveRecord::SpawnMethods.instance_methods(false)
221
- # Remove the ones we know are private API
222
- query_methods -= [:arel, :build_subquery, :construct_join_dependency, :extensions, :spawn]
223
- # Remove "where" which needs a custom return type for WhereChains
224
- query_methods -= [:where]
225
- # Remove the methods that ...
226
- query_methods
227
- .grep_v(/_clause$/) # end with "_clause"
228
- .grep_v(/_values?$/) # end with "_value" or "_values"
229
- .grep_v(/=$/) # end with "=""
230
- .grep_v(/(?<!uniq)!$/) # end with "!" except for "uniq!"
231
- end, T::Array[Symbol])
232
- WHERE_CHAIN_QUERY_METHODS = T.let(
233
- ActiveRecord::QueryMethods::WhereChain.instance_methods(false),
234
- T::Array[Symbol]
235
- )
236
- FINDER_METHODS = T.let(ActiveRecord::FinderMethods.instance_methods(false), T::Array[Symbol])
237
- CALCULATION_METHODS = T.let(ActiveRecord::Calculations.instance_methods(false), T::Array[Symbol])
238
- ENUMERABLE_QUERY_METHODS = T.let([:any?, :many?, :none?, :one?], T::Array[Symbol])
239
- FIND_OR_CREATE_METHODS = T.let(
240
- [:find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :create_or_find_by, :create_or_find_by!],
241
- T::Array[Symbol]
242
- )
243
- BUILDER_METHODS = T.let([:new, :build, :create, :create!], T::Array[Symbol])
244
-
245
- private
246
-
247
- sig { returns(RBI::Scope) }
248
- attr_reader :model
249
-
250
- sig { returns(String) }
251
- attr_reader :constant_name
252
-
253
- sig { params(type: String).returns(String) }
254
- def as_nilable_type(type)
255
- if type.start_with?("T.nilable(", "::T.nilable(") || type == "T.untyped" || type == "::T.untyped"
256
- type
257
- else
258
- "T.nilable(#{type})"
259
- end
260
- end
261
-
262
- sig { void }
263
- def create_classes_and_includes
264
- model.create_extend(CommonRelationMethodsModuleName)
265
- # The model always extends the generated relation module
266
- model.create_extend(RelationMethodsModuleName)
267
-
268
- # This feature is only available in versions of Sorbet with special support for
269
- # handling `NilClass` returns from `to_ary`. We should not be typing `to_ary` like
270
- # this for older versions since it will make all flatten operations be
271
- # `T::Array[NilClass]`, otherwise.
272
- if Tapioca::Compilers::Sorbet.supports?(:to_ary_nil_support)
273
- # Type the `to_ary` method as returning `NilClass` so that flatten stops recursing
274
- # See https://github.com/sorbet/sorbet/pull/4706 for details
275
- model.create_method("to_ary", return_type: "NilClass", visibility: RBI::Private.new)
276
- end
277
-
278
- create_relation_class
279
- create_association_relation_class
280
- create_collection_proxy_class
281
- end
282
-
283
- sig { void }
284
- def create_relation_class
285
- superclass = "::ActiveRecord::Relation"
286
-
287
- # The relation subclass includes the generated relation module
288
- model.create_class(RelationClassName, superclass_name: superclass) do |klass|
289
- klass.create_include(CommonRelationMethodsModuleName)
290
- klass.create_include(RelationMethodsModuleName)
291
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
292
-
293
- klass.create_method("to_ary", return_type: "T::Array[#{constant_name}]")
294
- end
295
-
296
- create_relation_where_chain_class
297
- end
298
-
299
- sig { void }
300
- def create_association_relation_class
301
- superclass = "::ActiveRecord::AssociationRelation"
302
-
303
- # Association subclasses include the generated association relation module
304
- model.create_class(AssociationRelationClassName, superclass_name: superclass) do |klass|
305
- klass.create_include(CommonRelationMethodsModuleName)
306
- klass.create_include(AssociationRelationMethodsModuleName)
307
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
308
-
309
- klass.create_method("to_ary", return_type: "T::Array[#{constant_name}]")
310
- end
311
-
312
- create_association_relation_where_chain_class
313
- end
314
-
315
- sig { void }
316
- def create_relation_where_chain_class
317
- model.create_class(RelationWhereChainClassName, superclass_name: RelationClassName) do |klass|
318
- create_where_chain_methods(klass, RelationClassName)
319
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
320
- end
321
- end
322
-
323
- sig { void }
324
- def create_association_relation_where_chain_class
325
- model.create_class(
326
- AssociationRelationWhereChainClassName,
327
- superclass_name: AssociationRelationClassName
328
- ) do |klass|
329
- create_where_chain_methods(klass, AssociationRelationClassName)
330
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
331
- end
332
- end
333
-
334
- sig { params(klass: RBI::Scope, return_type: String).void }
335
- def create_where_chain_methods(klass, return_type)
336
- WHERE_CHAIN_QUERY_METHODS.each do |method_name|
337
- case method_name
338
- when :not
339
- klass.create_method(
340
- method_name.to_s,
341
- parameters: [
342
- create_param("opts", type: "T.untyped"),
343
- create_rest_param("rest", type: "T.untyped"),
344
- ],
345
- return_type: return_type
346
- )
347
- when :associated, :missing
348
- klass.create_method(
349
- method_name.to_s,
350
- parameters: [
351
- create_rest_param("args", type: "T.untyped"),
352
- ],
353
- return_type: return_type
354
- )
355
- end
356
- end
357
- end
358
-
359
- sig { void }
360
- def create_collection_proxy_class
361
- superclass = "::ActiveRecord::Associations::CollectionProxy"
362
-
363
- # The relation subclass includes the generated association relation module
364
- model.create_class(AssociationsCollectionProxyClassName, superclass_name: superclass) do |klass|
365
- klass.create_include(CommonRelationMethodsModuleName)
366
- klass.create_include(AssociationRelationMethodsModuleName)
367
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
368
-
369
- klass.create_method("to_ary", return_type: "T::Array[#{constant_name}]")
370
- create_collection_proxy_methods(klass)
371
- end
372
- end
373
-
374
- sig { params(klass: RBI::Scope).void }
375
- def create_collection_proxy_methods(klass)
376
- # For these cases, it is valid to pass:
377
- # - a model instance, thus `Model`
378
- # - a model collection which can be:
379
- # - an array of models, thus `T::Enumerable[Model]`
380
- # - an association relation of a model, thus `T::Enumerable[Model]`
381
- # - a collection proxy of a model, thus, again, a `T::Enumerable[Model]`
382
- # - a collection of relations or collection proxies, thus `T::Enumerable[T::Enumerable[Model]]`
383
- # - or, any mix of the above, thus `T::Enumerable[T.any(Model, T::Enumerable[Model])]`
384
- # which altogether gives us:
385
- # `T.any(Model, T::Enumerable[T.any(Model, T::Enumerable[Model])])`
386
- model_collection =
387
- "T.any(#{constant_name}, T::Enumerable[T.any(#{constant_name}, T::Enumerable[#{constant_name}])])"
388
-
389
- # For these cases, it is valid to pass the above kind of things, but also:
390
- # - a model identifier, which can be:
391
- # - a numeric id, thus `Integer`
392
- # - a string id, thus `String`
393
- # - a collection of identifiers
394
- # - a collection of identifiers, thus `T::Enumerable[T.any(Integer, String)]`
395
- # which, coupled with the above case, gives us:
396
- # `T.any(Model, Integer, String, T::Enumerable[T.any(Model, Integer, String, T::Enumerable[Model])])`
397
- model_or_id_collection =
398
- "T.any(#{constant_name}, Integer, String" \
399
- ", T::Enumerable[T.any(#{constant_name}, Integer, String, T::Enumerable[#{constant_name}])])"
400
-
401
- COLLECTION_PROXY_METHODS.each do |method_name|
402
- case method_name
403
- when :<<, :append, :concat, :prepend, :push
404
- klass.create_method(
405
- method_name.to_s,
406
- parameters: [
407
- create_rest_param("records", type: model_collection),
408
- ],
409
- return_type: AssociationsCollectionProxyClassName
410
- )
411
- when :clear
412
- klass.create_method(
413
- method_name.to_s,
414
- return_type: AssociationsCollectionProxyClassName
415
- )
416
- when :delete, :destroy
417
- klass.create_method(
418
- method_name.to_s,
419
- parameters: [
420
- create_rest_param("records", type: model_or_id_collection),
421
- ],
422
- return_type: "T::Array[#{constant_name}]"
423
- )
424
- when :load_target
425
- klass.create_method(
426
- method_name.to_s,
427
- return_type: "T::Array[#{constant_name}]"
428
- )
429
- when :replace
430
- klass.create_method(
431
- method_name.to_s,
432
- parameters: [
433
- create_param("other_array", type: model_collection),
434
- ],
435
- return_type: "T::Array[#{constant_name}]"
436
- )
437
- when :reset_scope
438
- # skip
439
- when :scope
440
- klass.create_method(
441
- method_name.to_s,
442
- return_type: AssociationRelationClassName
443
- )
444
- when :target
445
- klass.create_method(
446
- method_name.to_s,
447
- return_type: "T::Array[#{constant_name}]"
448
- )
449
- end
450
- end
451
- end
452
-
453
- sig { void }
454
- def create_relation_methods
455
- create_relation_method("all")
456
- create_relation_method(
457
- "where",
458
- parameters: [
459
- create_rest_param("args", type: "T.untyped"),
460
- create_block_param("blk", type: "T.untyped"),
461
- ],
462
- relation_return_type: RelationWhereChainClassName,
463
- association_return_type: AssociationRelationWhereChainClassName,
464
- )
465
-
466
- QUERY_METHODS.each do |method_name|
467
- create_relation_method(
468
- method_name,
469
- parameters: [
470
- create_rest_param("args", type: "T.untyped"),
471
- create_block_param("blk", type: "T.untyped"),
472
- ]
473
- )
474
- end
475
- end
476
-
477
- sig { void }
478
- def create_association_relation_methods
479
- returning_type = "T.nilable(T.any(T::Array[Symbol], FalseClass))"
480
- unique_by_type = "T.nilable(T.any(T::Array[Symbol], Symbol))"
481
-
482
- ASSOCIATION_METHODS.each do |method_name|
483
- case method_name
484
- when :insert_all, :insert_all!, :upsert_all
485
- parameters = [
486
- create_param("attributes", type: "T::Array[Hash]"),
487
- create_kw_opt_param("returning", type: returning_type, default: "nil"),
488
- ]
489
-
490
- # Bang methods don't have the `unique_by` parameter
491
- unless method_name.end_with?("!")
492
- parameters << create_kw_opt_param("unique_by", type: unique_by_type, default: "nil")
493
- end
494
-
495
- @association_relation_methods_module.create_method(
496
- method_name.to_s,
497
- parameters: parameters,
498
- return_type: "ActiveRecord::Result"
499
- )
500
- when :insert, :insert!, :upsert
501
- parameters = [
502
- create_param("attributes", type: "Hash"),
503
- create_kw_opt_param("returning", type: returning_type, default: "nil"),
504
- ]
505
-
506
- # Bang methods don't have the `unique_by` parameter
507
- unless method_name.end_with?("!")
508
- parameters << create_kw_opt_param("unique_by", type: unique_by_type, default: "nil")
509
- end
510
-
511
- @association_relation_methods_module.create_method(
512
- method_name.to_s,
513
- parameters: parameters,
514
- return_type: "ActiveRecord::Result"
515
- )
516
- when :proxy_association
517
- # skip - private method
518
- end
519
- end
520
- end
521
-
522
- sig { void }
523
- def create_common_methods
524
- create_common_method("destroy_all", return_type: "T::Array[#{constant_name}]")
525
-
526
- FINDER_METHODS.each do |method_name|
527
- case method_name
528
- when :exists?
529
- create_common_method(
530
- "exists?",
531
- parameters: [
532
- create_opt_param("conditions", type: "T.untyped", default: ":none"),
533
- ],
534
- return_type: "T::Boolean"
535
- )
536
- when :include?, :member?
537
- create_common_method(
538
- method_name,
539
- parameters: [
540
- create_param("record", type: "T.untyped"),
541
- ],
542
- return_type: "T::Boolean"
543
- )
544
- when :find
545
- create_common_method(
546
- "find",
547
- parameters: [
548
- create_rest_param("args", type: "T.untyped"),
549
- ],
550
- return_type: "T.untyped"
551
- )
552
- when :find_by
553
- create_common_method(
554
- "find_by",
555
- parameters: [
556
- create_rest_param("args", type: "T.untyped"),
557
- ],
558
- return_type: as_nilable_type(constant_name)
559
- )
560
- when :find_by!
561
- create_common_method(
562
- "find_by!",
563
- parameters: [
564
- create_rest_param("args", type: "T.untyped"),
565
- ],
566
- return_type: constant_name
567
- )
568
- when :first, :last, :take
569
- create_common_method(
570
- method_name,
571
- parameters: [
572
- create_opt_param("limit", type: "T.untyped", default: "nil"),
573
- ],
574
- return_type: "T.untyped"
575
- )
576
- when :raise_record_not_found_exception!
577
- # skip
578
- else
579
- return_type = if method_name.end_with?("!")
580
- constant_name
581
- else
582
- as_nilable_type(constant_name)
583
- end
584
-
585
- create_common_method(
586
- method_name,
587
- return_type: return_type
588
- )
589
- end
590
- end
591
-
592
- CALCULATION_METHODS.each do |method_name|
593
- case method_name
594
- when :average, :maximum, :minimum
595
- create_common_method(
596
- method_name,
597
- parameters: [
598
- create_param("column_name", type: "T.any(String, Symbol)"),
599
- ],
600
- return_type: "T.untyped"
601
- )
602
- when :calculate
603
- create_common_method(
604
- "calculate",
605
- parameters: [
606
- create_param("operation", type: "Symbol"),
607
- create_param("column_name", type: "T.any(String, Symbol)"),
608
- ],
609
- return_type: "T.untyped"
610
- )
611
- when :count
612
- create_common_method(
613
- "count",
614
- parameters: [
615
- create_opt_param("column_name", type: "T.untyped", default: "nil"),
616
- ],
617
- return_type: "T.untyped"
618
- )
619
- when :ids
620
- create_common_method("ids", return_type: "Array")
621
- when :pick, :pluck
622
- create_common_method(
623
- method_name,
624
- parameters: [
625
- create_rest_param("column_names", type: "T.untyped"),
626
- ],
627
- return_type: "T.untyped"
628
- )
629
- when :sum
630
- create_common_method(
631
- "sum",
632
- parameters: [
633
- create_opt_param("column_name", type: "T.nilable(T.any(String, Symbol))", default: "nil"),
634
- create_block_param("block", type: "T.nilable(T.proc.params(record: T.untyped).returns(T.untyped))"),
635
- ],
636
- return_type: "T.untyped"
637
- )
638
- end
639
- end
640
-
641
- ENUMERABLE_QUERY_METHODS.each do |method_name|
642
- block_type = "T.nilable(T.proc.params(record: #{constant_name}).returns(T.untyped))"
643
- create_common_method(
644
- method_name,
645
- parameters: [
646
- create_block_param("block", type: block_type),
647
- ],
648
- return_type: "T::Boolean"
649
- )
650
- end
651
-
652
- FIND_OR_CREATE_METHODS.each do |method_name|
653
- block_type = "T.nilable(T.proc.params(object: #{constant_name}).void)"
654
- create_common_method(
655
- method_name,
656
- parameters: [
657
- create_param("attributes", type: "T.untyped"),
658
- create_block_param("block", type: block_type),
659
- ],
660
- return_type: constant_name
661
- )
662
- end
663
-
664
- BUILDER_METHODS.each do |method_name|
665
- create_common_method(
666
- method_name,
667
- parameters: [
668
- create_opt_param("attributes", type: "T.untyped", default: "nil"),
669
- create_block_param("block", type: "T.nilable(T.proc.params(object: #{constant_name}).void)"),
670
- ],
671
- return_type: constant_name
672
- )
673
- end
674
- end
675
-
676
- sig do
677
- params(
678
- name: T.any(Symbol, String),
679
- parameters: T::Array[RBI::TypedParam],
680
- return_type: T.nilable(String)
681
- ).void
682
- end
683
- def create_common_method(name, parameters: [], return_type: nil)
684
- @common_relation_methods_module.create_method(
685
- name.to_s,
686
- parameters: parameters,
687
- return_type: return_type || "void"
688
- )
689
- end
690
-
691
- sig do
692
- params(
693
- name: T.any(Symbol, String),
694
- parameters: T::Array[RBI::TypedParam],
695
- relation_return_type: String,
696
- association_return_type: String,
697
- ).void
698
- end
699
- def create_relation_method(
700
- name,
701
- parameters: [],
702
- relation_return_type: RelationClassName,
703
- association_return_type: AssociationRelationClassName
704
- )
705
- @relation_methods_module.create_method(
706
- name.to_s,
707
- parameters: parameters,
708
- return_type: relation_return_type
709
- )
710
- @association_relation_methods_module.create_method(
711
- name.to_s,
712
- parameters: parameters,
713
- return_type: association_return_type
714
- )
715
- end
716
- end
717
- end
718
- end
719
- end
720
- end