class Tapioca::Compilers::Dsl::ActiveRecordAssociations
`Tapioca::Compilers::Dsl::ActiveRecordAssociations` refines RBI
files for subclasses of [`ActiveRecord::Base`](api.rubyonrails.org/classes/ActiveRecord/Base.html). This generator is only responsible for defining the methods that would be created for the associations that are defined in the Active Record model.
For example, with the following model class:
~~~rb class Post < ActiveRecord::Base
belongs_to :category has_many :comments has_one :author, class_name: "User" accepts_nested_attributes_for :category, :comments, :author
end ~~~
this generator will produce the following methods in the RBI
file `post.rbi`:
~~~rbi # post.rbi # typed: true
class Post
include Post::GeneratedAssociationMethods module Post::GeneratedAssociationMethods sig { returns(T.nilable(::User)) } def author; end sig { params(value: T.nilable(::User)).void } def author=(value); end sig { params(attributes: T.untyped).returns(T.untyped) } def author_attributes=(attributes); end sig { params(args: T.untyped, blk: T.untyped).returns(::User) } def build_author(*args, &blk); end sig { params(args: T.untyped, blk: T.untyped).returns(::Category) } def build_category(*args, &blk); end sig { returns(T.nilable(::Category)) } def category; end sig { params(value: T.nilable(::Category)).void } def category=(value); end sig { params(attributes: T.untyped).returns(T.untyped) } def category_attributes=(attributes); end sig { returns(T::Array[T.untyped]) } def comment_ids; end sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) } def comment_ids=(ids); end sig { returns(::ActiveRecord::Associations::CollectionProxy[::Comment]) } def comments; end sig { params(value: T::Enumerable[::Comment]).void } def comments=(value); end sig { params(attributes: T.untyped).returns(T.untyped) } def comments_attributes=(attributes); end sig { params(args: T.untyped, blk: T.untyped).returns(::User) } def create_author(*args, &blk); end sig { params(args: T.untyped, blk: T.untyped).returns(::User) } def create_author!(*args, &blk); end sig { params(args: T.untyped, blk: T.untyped).returns(::Category) } def create_category(*args, &blk); end sig { params(args: T.untyped, blk: T.untyped).returns(::Category) } def create_category!(*args, &blk); end sig { returns(T.nilable(::User)) } def reload_author; end sig { returns(T.nilable(::Category)) } def reload_category; end end
end ~~~
Constants
- ReflectionType
Public Instance Methods
decorate(root, constant)
click to toggle source
# File lib/tapioca/compilers/dsl/active_record_associations.rb, line 108 def decorate(root, constant) return if constant.reflections.empty? root.create_path(constant) do |model| module_name = "GeneratedAssociationMethods" model.create_module(module_name) do |mod| populate_nested_attribute_writers(mod, constant) populate_associations(mod, constant) end model.create_include(module_name) end end
gather_constants()
click to toggle source
# File lib/tapioca/compilers/dsl/active_record_associations.rb, line 124 def gather_constants descendants_of(::ActiveRecord::Base).reject(&:abstract_class?) end
Private Instance Methods
polymorphic_association?(reflection)
click to toggle source
# File lib/tapioca/compilers/dsl/active_record_associations.rb, line 268 def polymorphic_association?(reflection) if reflection.through_reflection? polymorphic_association?(reflection.source_reflection) else !!reflection.polymorphic? end end
populate_associations(mod, constant)
click to toggle source
# File lib/tapioca/compilers/dsl/active_record_associations.rb, line 142 def populate_associations(mod, constant) constant.reflections.each do |association_name, reflection| if reflection.collection? populate_collection_assoc_getter_setter(mod, constant, association_name, reflection) else populate_single_assoc_getter_setter(mod, constant, association_name, reflection) end end end
populate_collection_assoc_getter_setter(klass, constant, association_name, reflection)
click to toggle source
# File lib/tapioca/compilers/dsl/active_record_associations.rb, line 213 def populate_collection_assoc_getter_setter(klass, constant, association_name, reflection) association_class = type_for(constant, reflection) relation_class = relation_type_for(constant, reflection) klass.create_method( association_name.to_s, return_type: relation_class, ) klass.create_method( "#{association_name}=", parameters: [create_param("value", type: "T::Enumerable[#{association_class}]")], return_type: "void", ) klass.create_method( "#{association_name.to_s.singularize}_ids", return_type: "T::Array[T.untyped]" ) klass.create_method( "#{association_name.to_s.singularize}_ids=", parameters: [create_param("ids", type: "T::Array[T.untyped]")], return_type: "T::Array[T.untyped]" ) end
populate_nested_attribute_writers(mod, constant)
click to toggle source
# File lib/tapioca/compilers/dsl/active_record_associations.rb, line 131 def populate_nested_attribute_writers(mod, constant) constant.nested_attributes_options.keys.each do |association_name| mod.create_method( "#{association_name}_attributes=", parameters: [create_param("attributes", type: "T.untyped")], return_type: "T.untyped" ) end end
populate_single_assoc_getter_setter(klass, constant, association_name, reflection)
click to toggle source
# File lib/tapioca/compilers/dsl/active_record_associations.rb, line 160 def populate_single_assoc_getter_setter(klass, constant, association_name, reflection) association_class = type_for(constant, reflection) association_type = "T.nilable(#{association_class})" klass.create_method( association_name.to_s, return_type: association_type, ) klass.create_method( "#{association_name}=", parameters: [create_param("value", type: association_type)], return_type: "void" ) klass.create_method( "reload_#{association_name}", return_type: association_type, ) unless reflection.polymorphic? klass.create_method( "build_#{association_name}", parameters: [ create_rest_param("args", type: "T.untyped"), create_block_param("blk", type: "T.untyped"), ], return_type: association_class ) klass.create_method( "create_#{association_name}", parameters: [ create_rest_param("args", type: "T.untyped"), create_block_param("blk", type: "T.untyped"), ], return_type: association_class ) klass.create_method( "create_#{association_name}!", parameters: [ create_rest_param("args", type: "T.untyped"), create_block_param("blk", type: "T.untyped"), ], return_type: association_class ) end end
relation_type_for(constant, reflection)
click to toggle source
# File lib/tapioca/compilers/dsl/active_record_associations.rb, line 255 def relation_type_for(constant, reflection) "ActiveRecord::Associations::CollectionProxy" if !constant.table_exists? || polymorphic_association?(reflection) # Change to: "::#{reflection.klass.name}::ActiveRecord_Associations_CollectionProxy" "::ActiveRecord::Associations::CollectionProxy[#{qualified_name_of(reflection.klass)}]" end
type_for(constant, reflection)
click to toggle source
# File lib/tapioca/compilers/dsl/active_record_associations.rb, line 243 def type_for(constant, reflection) return "T.untyped" if !constant.table_exists? || polymorphic_association?(reflection) T.must(qualified_name_of(reflection.klass)) end