tapioca 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +13 -2
  3. data/README.md +79 -25
  4. data/Rakefile +10 -14
  5. data/lib/tapioca/cli.rb +66 -80
  6. data/lib/tapioca/{generators/base.rb → commands/command.rb} +17 -10
  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 +84 -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 +32 -24
  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 +29 -35
  21. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_columns.rb +26 -24
  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 +10 -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 +12 -17
  27. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_resource.rb +10 -8
  28. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_storage.rb +11 -11
  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 +10 -8
  32. data/lib/tapioca/{compilers/dsl → dsl/compilers}/frozen_record.rb +13 -11
  33. data/lib/tapioca/{compilers/dsl → dsl/compilers}/identity_cache.rb +28 -25
  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 +10 -8
  36. data/lib/tapioca/{compilers/dsl → dsl/compilers}/rails_generators.rb +13 -14
  37. data/lib/tapioca/{compilers/dsl → dsl/compilers}/sidekiq_worker.rb +14 -13
  38. data/lib/tapioca/{compilers/dsl → dsl/compilers}/smart_properties.rb +12 -13
  39. data/lib/tapioca/{compilers/dsl → dsl/compilers}/state_machines.rb +12 -10
  40. data/lib/tapioca/{compilers/dsl → dsl/compilers}/url_helpers.rb +16 -14
  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 +2 -2
  46. data/lib/tapioca/{compilers/dsl_compiler.rb → dsl/pipeline.rb} +41 -33
  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/gemfile.rb +44 -20
  64. data/lib/tapioca/helpers/cli_helper.rb +16 -8
  65. data/lib/tapioca/helpers/config_helper.rb +113 -0
  66. data/lib/tapioca/helpers/rbi_helper.rb +17 -0
  67. data/lib/tapioca/helpers/shims_helper.rb +87 -0
  68. data/lib/tapioca/helpers/sorbet_helper.rb +57 -0
  69. data/lib/tapioca/helpers/test/dsl_compiler.rb +118 -0
  70. data/lib/tapioca/helpers/test/isolation.rb +1 -1
  71. data/lib/tapioca/helpers/test/template.rb +13 -2
  72. data/lib/tapioca/internal.rb +17 -10
  73. data/lib/tapioca/rbi_ext/model.rb +2 -48
  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 +166 -0
  77. data/lib/tapioca/runtime/loader.rb +123 -0
  78. data/lib/tapioca/runtime/reflection.rb +153 -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 +110 -54
  85. data/lib/tapioca/sorbet_ext/name_patch.rb +7 -1
  86. data/lib/tapioca/{compilers → static}/requires_compiler.rb +5 -12
  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 +82 -62
  92. data/lib/tapioca/compilers/dsl/active_record_relations.rb +0 -711
  93. data/lib/tapioca/compilers/dsl/base.rb +0 -179
  94. data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +0 -27
  95. data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +0 -198
  96. data/lib/tapioca/compilers/sorbet.rb +0 -59
  97. data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +0 -780
  98. data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +0 -90
  99. data/lib/tapioca/compilers/symbol_table_compiler.rb +0 -17
  100. data/lib/tapioca/compilers/todos_compiler.rb +0 -32
  101. data/lib/tapioca/generators/todo.rb +0 -76
  102. data/lib/tapioca/generators.rb +0 -9
  103. data/lib/tapioca/generic_type_registry.rb +0 -149
  104. data/lib/tapioca/helpers/active_record_column_type_helper.rb +0 -98
  105. data/lib/tapioca/loader.rb +0 -119
  106. data/lib/tapioca/reflection.rb +0 -151
  107. data/lib/tapioca/trackers/autoload.rb +0 -70
  108. data/lib/tapioca/trackers/constant_definition.rb +0 -42
  109. data/lib/tapioca/trackers/mixin.rb +0 -78
@@ -1,711 +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 { void }
254
- def create_classes_and_includes
255
- model.create_extend(CommonRelationMethodsModuleName)
256
- # The model always extends the generated relation module
257
- model.create_extend(RelationMethodsModuleName)
258
-
259
- # This feature is only available in versions of Sorbet with special support for
260
- # handling `NilClass` returns from `to_ary`. We should not be typing `to_ary` like
261
- # this for older versions since it will make all flatten operations be
262
- # `T::Array[NilClass]`, otherwise.
263
- if Tapioca::Compilers::Sorbet.supports?(:to_ary_nil_support)
264
- # Type the `to_ary` method as returning `NilClass` so that flatten stops recursing
265
- # See https://github.com/sorbet/sorbet/pull/4706 for details
266
- model.create_method("to_ary", return_type: "NilClass", visibility: RBI::Private.new)
267
- end
268
-
269
- create_relation_class
270
- create_association_relation_class
271
- create_collection_proxy_class
272
- end
273
-
274
- sig { void }
275
- def create_relation_class
276
- superclass = "::ActiveRecord::Relation"
277
-
278
- # The relation subclass includes the generated relation module
279
- model.create_class(RelationClassName, superclass_name: superclass) do |klass|
280
- klass.create_include(CommonRelationMethodsModuleName)
281
- klass.create_include(RelationMethodsModuleName)
282
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
283
-
284
- klass.create_method("to_ary", return_type: "T::Array[#{constant_name}]")
285
- end
286
-
287
- create_relation_where_chain_class
288
- end
289
-
290
- sig { void }
291
- def create_association_relation_class
292
- superclass = "::ActiveRecord::AssociationRelation"
293
-
294
- # Association subclasses include the generated association relation module
295
- model.create_class(AssociationRelationClassName, superclass_name: superclass) do |klass|
296
- klass.create_include(CommonRelationMethodsModuleName)
297
- klass.create_include(AssociationRelationMethodsModuleName)
298
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
299
-
300
- klass.create_method("to_ary", return_type: "T::Array[#{constant_name}]")
301
- end
302
-
303
- create_association_relation_where_chain_class
304
- end
305
-
306
- sig { void }
307
- def create_relation_where_chain_class
308
- model.create_class(RelationWhereChainClassName, superclass_name: RelationClassName) do |klass|
309
- create_where_chain_methods(klass, RelationClassName)
310
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
311
- end
312
- end
313
-
314
- sig { void }
315
- def create_association_relation_where_chain_class
316
- model.create_class(
317
- AssociationRelationWhereChainClassName,
318
- superclass_name: AssociationRelationClassName
319
- ) do |klass|
320
- create_where_chain_methods(klass, AssociationRelationClassName)
321
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
322
- end
323
- end
324
-
325
- sig { params(klass: RBI::Scope, return_type: String).void }
326
- def create_where_chain_methods(klass, return_type)
327
- WHERE_CHAIN_QUERY_METHODS.each do |method_name|
328
- case method_name
329
- when :not
330
- klass.create_method(
331
- method_name.to_s,
332
- parameters: [
333
- create_param("opts", type: "T.untyped"),
334
- create_rest_param("rest", type: "T.untyped"),
335
- ],
336
- return_type: return_type
337
- )
338
- when :associated, :missing
339
- klass.create_method(
340
- method_name.to_s,
341
- parameters: [
342
- create_rest_param("args", type: "T.untyped"),
343
- ],
344
- return_type: return_type
345
- )
346
- end
347
- end
348
- end
349
-
350
- sig { void }
351
- def create_collection_proxy_class
352
- superclass = "::ActiveRecord::Associations::CollectionProxy"
353
-
354
- # The relation subclass includes the generated association relation module
355
- model.create_class(AssociationsCollectionProxyClassName, superclass_name: superclass) do |klass|
356
- klass.create_include(CommonRelationMethodsModuleName)
357
- klass.create_include(AssociationRelationMethodsModuleName)
358
- klass.create_constant("Elem", value: "type_member(fixed: #{constant_name})")
359
-
360
- klass.create_method("to_ary", return_type: "T::Array[#{constant_name}]")
361
- create_collection_proxy_methods(klass)
362
- end
363
- end
364
-
365
- sig { params(klass: RBI::Scope).void }
366
- def create_collection_proxy_methods(klass)
367
- # For these cases, it is valid to pass:
368
- # - a model instance, thus `Model`
369
- # - a model collection which can be:
370
- # - an array of models, thus `T::Enumerable[Model]`
371
- # - an association relation of a model, thus `T::Enumerable[Model]`
372
- # - a collection proxy of a model, thus, again, a `T::Enumerable[Model]`
373
- # - a collection of relations or collection proxies, thus `T::Enumerable[T::Enumerable[Model]]`
374
- # - or, any mix of the above, thus `T::Enumerable[T.any(Model, T::Enumerable[Model])]`
375
- # which altogether gives us:
376
- # `T.any(Model, T::Enumerable[T.any(Model, T::Enumerable[Model])])`
377
- model_collection =
378
- "T.any(#{constant_name}, T::Enumerable[T.any(#{constant_name}, T::Enumerable[#{constant_name}])])"
379
-
380
- # For these cases, it is valid to pass the above kind of things, but also:
381
- # - a model identifier, which can be:
382
- # - a numeric id, thus `Integer`
383
- # - a string id, thus `String`
384
- # - a collection of identifiers
385
- # - a collection of identifiers, thus `T::Enumerable[T.any(Integer, String)]`
386
- # which, coupled with the above case, gives us:
387
- # `T.any(Model, Integer, String, T::Enumerable[T.any(Model, Integer, String, T::Enumerable[Model])])`
388
- model_or_id_collection =
389
- "T.any(#{constant_name}, Integer, String" \
390
- ", T::Enumerable[T.any(#{constant_name}, Integer, String, T::Enumerable[#{constant_name}])])"
391
-
392
- COLLECTION_PROXY_METHODS.each do |method_name|
393
- case method_name
394
- when :<<, :append, :concat, :prepend, :push
395
- klass.create_method(
396
- method_name.to_s,
397
- parameters: [
398
- create_rest_param("records", type: model_collection),
399
- ],
400
- return_type: AssociationsCollectionProxyClassName
401
- )
402
- when :clear
403
- klass.create_method(
404
- method_name.to_s,
405
- return_type: AssociationsCollectionProxyClassName
406
- )
407
- when :delete, :destroy
408
- klass.create_method(
409
- method_name.to_s,
410
- parameters: [
411
- create_rest_param("records", type: model_or_id_collection),
412
- ],
413
- return_type: "T::Array[#{constant_name}]"
414
- )
415
- when :load_target
416
- klass.create_method(
417
- method_name.to_s,
418
- return_type: "T::Array[#{constant_name}]"
419
- )
420
- when :replace
421
- klass.create_method(
422
- method_name.to_s,
423
- parameters: [
424
- create_param("other_array", type: model_collection),
425
- ],
426
- return_type: "T::Array[#{constant_name}]"
427
- )
428
- when :reset_scope
429
- # skip
430
- when :scope
431
- klass.create_method(
432
- method_name.to_s,
433
- return_type: AssociationRelationClassName
434
- )
435
- when :target
436
- klass.create_method(
437
- method_name.to_s,
438
- return_type: "T::Array[#{constant_name}]"
439
- )
440
- end
441
- end
442
- end
443
-
444
- sig { void }
445
- def create_relation_methods
446
- create_relation_method("all")
447
- create_relation_method(
448
- "where",
449
- parameters: [
450
- create_rest_param("args", type: "T.untyped"),
451
- create_block_param("blk", type: "T.untyped"),
452
- ],
453
- relation_return_type: RelationWhereChainClassName,
454
- association_return_type: AssociationRelationWhereChainClassName,
455
- )
456
-
457
- QUERY_METHODS.each do |method_name|
458
- create_relation_method(
459
- method_name,
460
- parameters: [
461
- create_rest_param("args", type: "T.untyped"),
462
- create_block_param("blk", type: "T.untyped"),
463
- ]
464
- )
465
- end
466
- end
467
-
468
- sig { void }
469
- def create_association_relation_methods
470
- returning_type = "T.nilable(T.any(T::Array[Symbol], FalseClass))"
471
- unique_by_type = "T.nilable(T.any(T::Array[Symbol], Symbol))"
472
-
473
- ASSOCIATION_METHODS.each do |method_name|
474
- case method_name
475
- when :insert_all, :insert_all!, :upsert_all
476
- parameters = [
477
- create_param("attributes", type: "T::Array[Hash]"),
478
- create_kw_opt_param("returning", type: returning_type, default: "nil"),
479
- ]
480
-
481
- # Bang methods don't have the `unique_by` parameter
482
- unless method_name.end_with?("!")
483
- parameters << create_kw_opt_param("unique_by", type: unique_by_type, default: "nil")
484
- end
485
-
486
- @association_relation_methods_module.create_method(
487
- method_name.to_s,
488
- parameters: parameters,
489
- return_type: "ActiveRecord::Result"
490
- )
491
- when :insert, :insert!, :upsert
492
- parameters = [
493
- create_param("attributes", type: "Hash"),
494
- create_kw_opt_param("returning", type: returning_type, default: "nil"),
495
- ]
496
-
497
- # Bang methods don't have the `unique_by` parameter
498
- unless method_name.end_with?("!")
499
- parameters << create_kw_opt_param("unique_by", type: unique_by_type, default: "nil")
500
- end
501
-
502
- @association_relation_methods_module.create_method(
503
- method_name.to_s,
504
- parameters: parameters,
505
- return_type: "ActiveRecord::Result"
506
- )
507
- when :proxy_association
508
- # skip - private method
509
- end
510
- end
511
- end
512
-
513
- sig { void }
514
- def create_common_methods
515
- create_common_method("destroy_all", return_type: "T::Array[#{constant_name}]")
516
-
517
- FINDER_METHODS.each do |method_name|
518
- case method_name
519
- when :exists?
520
- create_common_method(
521
- "exists?",
522
- parameters: [
523
- create_opt_param("conditions", type: "T.untyped", default: ":none"),
524
- ],
525
- return_type: "T::Boolean"
526
- )
527
- when :include?, :member?
528
- create_common_method(
529
- method_name,
530
- parameters: [
531
- create_param("record", type: "T.untyped"),
532
- ],
533
- return_type: "T::Boolean"
534
- )
535
- when :find
536
- create_common_method(
537
- "find",
538
- parameters: [
539
- create_rest_param("args", type: "T.untyped"),
540
- ],
541
- return_type: "T.untyped"
542
- )
543
- when :find_by
544
- create_common_method(
545
- "find_by",
546
- parameters: [
547
- create_rest_param("args", type: "T.untyped"),
548
- ],
549
- return_type: "T.nilable(#{constant_name})"
550
- )
551
- when :find_by!
552
- create_common_method(
553
- "find_by!",
554
- parameters: [
555
- create_rest_param("args", type: "T.untyped"),
556
- ],
557
- return_type: constant_name
558
- )
559
- when :first, :last, :take
560
- create_common_method(
561
- method_name,
562
- parameters: [
563
- create_opt_param("limit", type: "T.untyped", default: "nil"),
564
- ],
565
- return_type: "T.untyped"
566
- )
567
- when :raise_record_not_found_exception!
568
- # skip
569
- else
570
- return_type = if method_name.end_with?("!")
571
- constant_name
572
- else
573
- "T.nilable(#{constant_name})"
574
- end
575
-
576
- create_common_method(
577
- method_name,
578
- return_type: return_type
579
- )
580
- end
581
- end
582
-
583
- CALCULATION_METHODS.each do |method_name|
584
- case method_name
585
- when :average, :maximum, :minimum
586
- create_common_method(
587
- method_name,
588
- parameters: [
589
- create_param("column_name", type: "T.any(String, Symbol)"),
590
- ],
591
- return_type: "T.untyped"
592
- )
593
- when :calculate
594
- create_common_method(
595
- "calculate",
596
- parameters: [
597
- create_param("operation", type: "Symbol"),
598
- create_param("column_name", type: "T.any(String, Symbol)"),
599
- ],
600
- return_type: "T.untyped"
601
- )
602
- when :count
603
- create_common_method(
604
- "count",
605
- parameters: [
606
- create_opt_param("column_name", type: "T.untyped", default: "nil"),
607
- ],
608
- return_type: "T.untyped"
609
- )
610
- when :ids
611
- create_common_method("ids", return_type: "Array")
612
- when :pick, :pluck
613
- create_common_method(
614
- method_name,
615
- parameters: [
616
- create_rest_param("column_names", type: "T.untyped"),
617
- ],
618
- return_type: "T.untyped"
619
- )
620
- when :sum
621
- create_common_method(
622
- "sum",
623
- parameters: [
624
- create_opt_param("column_name", type: "T.nilable(T.any(String, Symbol))", default: "nil"),
625
- create_block_param("block", type: "T.nilable(T.proc.params(record: T.untyped).returns(T.untyped))"),
626
- ],
627
- return_type: "T.untyped"
628
- )
629
- end
630
- end
631
-
632
- ENUMERABLE_QUERY_METHODS.each do |method_name|
633
- block_type = "T.nilable(T.proc.params(record: #{constant_name}).returns(T.untyped))"
634
- create_common_method(
635
- method_name,
636
- parameters: [
637
- create_block_param("block", type: block_type),
638
- ],
639
- return_type: "T::Boolean"
640
- )
641
- end
642
-
643
- FIND_OR_CREATE_METHODS.each do |method_name|
644
- block_type = "T.nilable(T.proc.params(object: #{constant_name}).void)"
645
- create_common_method(
646
- method_name,
647
- parameters: [
648
- create_param("attributes", type: "T.untyped"),
649
- create_block_param("block", type: block_type),
650
- ],
651
- return_type: constant_name
652
- )
653
- end
654
-
655
- BUILDER_METHODS.each do |method_name|
656
- create_common_method(
657
- method_name,
658
- parameters: [
659
- create_opt_param("attributes", type: "T.untyped", default: "nil"),
660
- create_block_param("block", type: "T.nilable(T.proc.params(object: #{constant_name}).void)"),
661
- ],
662
- return_type: constant_name
663
- )
664
- end
665
- end
666
-
667
- sig do
668
- params(
669
- name: T.any(Symbol, String),
670
- parameters: T::Array[RBI::TypedParam],
671
- return_type: T.nilable(String)
672
- ).void
673
- end
674
- def create_common_method(name, parameters: [], return_type: nil)
675
- @common_relation_methods_module.create_method(
676
- name.to_s,
677
- parameters: parameters,
678
- return_type: return_type || "void"
679
- )
680
- end
681
-
682
- sig do
683
- params(
684
- name: T.any(Symbol, String),
685
- parameters: T::Array[RBI::TypedParam],
686
- relation_return_type: String,
687
- association_return_type: String,
688
- ).void
689
- end
690
- def create_relation_method(
691
- name,
692
- parameters: [],
693
- relation_return_type: RelationClassName,
694
- association_return_type: AssociationRelationClassName
695
- )
696
- @relation_methods_module.create_method(
697
- name.to_s,
698
- parameters: parameters,
699
- return_type: relation_return_type
700
- )
701
- @association_relation_methods_module.create_method(
702
- name.to_s,
703
- parameters: parameters,
704
- return_type: association_return_type
705
- )
706
- end
707
- end
708
- end
709
- end
710
- end
711
- end