tapioca 0.5.6 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +114 -23
- data/lib/tapioca/cli.rb +188 -64
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +94 -8
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +5 -4
- data/lib/tapioca/compilers/dsl/active_record_relations.rb +703 -0
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +43 -13
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +2 -4
- data/lib/tapioca/compilers/dsl/base.rb +25 -42
- data/lib/tapioca/compilers/dsl/extensions/frozen_record.rb +29 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +37 -0
- data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +27 -0
- data/lib/tapioca/compilers/dsl/param_helper.rb +52 -0
- data/lib/tapioca/compilers/dsl/rails_generators.rb +120 -0
- data/lib/tapioca/compilers/dsl_compiler.rb +32 -6
- data/lib/tapioca/compilers/sorbet.rb +2 -0
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +47 -46
- data/lib/tapioca/executor.rb +79 -0
- data/lib/tapioca/gemfile.rb +23 -0
- data/lib/tapioca/generators/base.rb +11 -18
- data/lib/tapioca/generators/dsl.rb +33 -38
- data/lib/tapioca/generators/gem.rb +50 -29
- data/lib/tapioca/generators/init.rb +41 -16
- data/lib/tapioca/generators/todo.rb +6 -6
- data/lib/tapioca/helpers/cli_helper.rb +26 -0
- data/lib/tapioca/helpers/config_helper.rb +84 -0
- data/lib/tapioca/helpers/test/content.rb +51 -0
- data/lib/tapioca/helpers/test/isolation.rb +125 -0
- data/lib/tapioca/helpers/test/template.rb +34 -0
- data/lib/tapioca/internal.rb +3 -2
- data/lib/tapioca/rbi_ext/model.rb +12 -9
- data/lib/tapioca/reflection.rb +13 -0
- data/lib/tapioca/trackers/autoload.rb +70 -0
- data/lib/tapioca/trackers/constant_definition.rb +42 -0
- data/lib/tapioca/trackers/mixin.rb +78 -0
- data/lib/tapioca/trackers.rb +14 -0
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +28 -2
- metadata +19 -7
- data/lib/tapioca/config.rb +0 -45
- data/lib/tapioca/config_builder.rb +0 -73
- data/lib/tapioca/constant_locator.rb +0 -40
@@ -7,6 +7,8 @@ rescue LoadError
|
|
7
7
|
return
|
8
8
|
end
|
9
9
|
|
10
|
+
require "tapioca/compilers/dsl/helper/active_record_constants"
|
11
|
+
|
10
12
|
module Tapioca
|
11
13
|
module Compilers
|
12
14
|
module Dsl
|
@@ -99,6 +101,23 @@ module Tapioca
|
|
99
101
|
# ~~~
|
100
102
|
class ActiveRecordAssociations < Base
|
101
103
|
extend T::Sig
|
104
|
+
include Helper::ActiveRecordConstants
|
105
|
+
|
106
|
+
class SourceReflectionError < StandardError
|
107
|
+
end
|
108
|
+
|
109
|
+
class MissingConstantError < StandardError
|
110
|
+
extend T::Sig
|
111
|
+
|
112
|
+
sig { returns(String) }
|
113
|
+
attr_reader :class_name
|
114
|
+
|
115
|
+
sig { params(class_name: String).void }
|
116
|
+
def initialize(class_name)
|
117
|
+
@class_name = class_name
|
118
|
+
super
|
119
|
+
end
|
120
|
+
end
|
102
121
|
|
103
122
|
ReflectionType = T.type_alias do
|
104
123
|
T.any(::ActiveRecord::Reflection::ThroughReflection, ::ActiveRecord::Reflection::AssociationReflection)
|
@@ -109,14 +128,12 @@ module Tapioca
|
|
109
128
|
return if constant.reflections.empty?
|
110
129
|
|
111
130
|
root.create_path(constant) do |model|
|
112
|
-
|
113
|
-
|
114
|
-
model.create_module(module_name) do |mod|
|
131
|
+
model.create_module(AssociationMethodsModuleName) do |mod|
|
115
132
|
populate_nested_attribute_writers(mod, constant)
|
116
133
|
populate_associations(mod, constant)
|
117
134
|
end
|
118
135
|
|
119
|
-
model.create_include(
|
136
|
+
model.create_include(AssociationMethodsModuleName)
|
120
137
|
end
|
121
138
|
end
|
122
139
|
|
@@ -146,6 +163,14 @@ module Tapioca
|
|
146
163
|
else
|
147
164
|
populate_single_assoc_getter_setter(mod, constant, association_name, reflection)
|
148
165
|
end
|
166
|
+
rescue SourceReflectionError
|
167
|
+
add_error(<<~MSG.strip)
|
168
|
+
Cannot generate association `#{reflection.name}` on `#{constant}` since the source of the through association is missing.
|
169
|
+
MSG
|
170
|
+
rescue MissingConstantError => error
|
171
|
+
add_error(<<~MSG.strip)
|
172
|
+
Cannot generate association `#{declaration(reflection)}` on `#{constant}` since the constant `#{error.class_name}` does not exist.
|
173
|
+
MSG
|
149
174
|
end
|
150
175
|
end
|
151
176
|
|
@@ -241,11 +266,61 @@ module Tapioca
|
|
241
266
|
).returns(String)
|
242
267
|
end
|
243
268
|
def type_for(constant, reflection)
|
269
|
+
validate_reflection!(reflection)
|
270
|
+
|
244
271
|
return "T.untyped" if !constant.table_exists? || polymorphic_association?(reflection)
|
245
272
|
|
246
273
|
T.must(qualified_name_of(reflection.klass))
|
247
274
|
end
|
248
275
|
|
276
|
+
sig do
|
277
|
+
params(
|
278
|
+
reflection: ReflectionType
|
279
|
+
).void
|
280
|
+
end
|
281
|
+
def validate_reflection!(reflection)
|
282
|
+
# Check existence of source reflection, first, since, calling
|
283
|
+
# `.klass` also tries to go through the source reflection
|
284
|
+
# and fails with a cryptic error, otherwise.
|
285
|
+
if reflection.through_reflection?
|
286
|
+
raise SourceReflectionError unless reflection.source_reflection
|
287
|
+
end
|
288
|
+
|
289
|
+
# For non-polymorphic reflections, `.klass` should not be raising
|
290
|
+
# a `NameError`.
|
291
|
+
unless reflection.polymorphic?
|
292
|
+
reflection.klass
|
293
|
+
end
|
294
|
+
rescue NameError
|
295
|
+
class_name = if reflection.through_reflection?
|
296
|
+
reflection.send(:delegate_reflection).class_name
|
297
|
+
else
|
298
|
+
reflection.class_name
|
299
|
+
end
|
300
|
+
|
301
|
+
raise MissingConstantError, class_name
|
302
|
+
end
|
303
|
+
|
304
|
+
sig { params(reflection: ReflectionType).returns(T.nilable(String)) }
|
305
|
+
def declaration(reflection)
|
306
|
+
case reflection
|
307
|
+
when ActiveRecord::Reflection::HasOneReflection
|
308
|
+
"has_one :#{reflection.name}"
|
309
|
+
when ActiveRecord::Reflection::HasManyReflection
|
310
|
+
"has_many :#{reflection.name}"
|
311
|
+
when ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
312
|
+
"has_and_belongs_to_many :#{reflection.name}"
|
313
|
+
when ActiveRecord::Reflection::BelongsToReflection
|
314
|
+
"belongs_to :#{reflection.name}"
|
315
|
+
when ActiveRecord::Reflection::ThroughReflection
|
316
|
+
delegate_reflection = reflection.send(:delegate_reflection)
|
317
|
+
declaration = declaration(delegate_reflection)
|
318
|
+
through_name = delegate_reflection.options[:through]
|
319
|
+
|
320
|
+
"#{declaration}, through: :#{through_name}"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
249
324
|
sig do
|
250
325
|
params(
|
251
326
|
constant: T.class_of(ActiveRecord::Base),
|
@@ -253,11 +328,22 @@ module Tapioca
|
|
253
328
|
).returns(String)
|
254
329
|
end
|
255
330
|
def relation_type_for(constant, reflection)
|
256
|
-
|
257
|
-
|
331
|
+
validate_reflection!(reflection)
|
332
|
+
|
333
|
+
relations_enabled = generator_enabled?("ActiveRecordRelations")
|
334
|
+
polymorphic_association = !constant.table_exists? || polymorphic_association?(reflection)
|
258
335
|
|
259
|
-
|
260
|
-
|
336
|
+
if relations_enabled
|
337
|
+
if polymorphic_association
|
338
|
+
"ActiveRecord::Associations::CollectionProxy"
|
339
|
+
else
|
340
|
+
"#{qualified_name_of(reflection.klass)}::#{AssociationsCollectionProxyClassName}"
|
341
|
+
end
|
342
|
+
elsif polymorphic_association
|
343
|
+
"ActiveRecord::Associations::CollectionProxy[T.untyped]"
|
344
|
+
else
|
345
|
+
"::ActiveRecord::Associations::CollectionProxy[#{qualified_name_of(reflection.klass)}]"
|
346
|
+
end
|
261
347
|
end
|
262
348
|
|
263
349
|
sig do
|
@@ -7,6 +7,8 @@ rescue LoadError
|
|
7
7
|
return
|
8
8
|
end
|
9
9
|
|
10
|
+
require "tapioca/compilers/dsl/helper/active_record_constants"
|
11
|
+
|
10
12
|
module Tapioca
|
11
13
|
module Compilers
|
12
14
|
module Dsl
|
@@ -96,15 +98,14 @@ module Tapioca
|
|
96
98
|
# ~~~
|
97
99
|
class ActiveRecordColumns < Base
|
98
100
|
extend T::Sig
|
101
|
+
include Helper::ActiveRecordConstants
|
99
102
|
|
100
103
|
sig { override.params(root: RBI::Tree, constant: T.class_of(ActiveRecord::Base)).void }
|
101
104
|
def decorate(root, constant)
|
102
105
|
return unless constant.table_exists?
|
103
106
|
|
104
107
|
root.create_path(constant) do |model|
|
105
|
-
|
106
|
-
|
107
|
-
model.create_module(module_name) do |mod|
|
108
|
+
model.create_module(AttributeMethodsModuleName) do |mod|
|
108
109
|
constant.columns_hash.each_key do |column_name|
|
109
110
|
column_name = column_name.to_s
|
110
111
|
add_methods_for_attribute(mod, constant, column_name)
|
@@ -121,7 +122,7 @@ module Tapioca
|
|
121
122
|
end
|
122
123
|
end
|
123
124
|
|
124
|
-
model.create_include(
|
125
|
+
model.create_include(AttributeMethodsModuleName)
|
125
126
|
end
|
126
127
|
end
|
127
128
|
|