Class: Tapioca::Dsl::Compilers::ActiveRecordRelations
- Inherits:
-
Tapioca::Dsl::Compiler
- Object
- Tapioca::Dsl::Compiler
- Tapioca::Dsl::Compilers::ActiveRecordRelations
- Extended by:
- T::Sig
- Includes:
- Helpers::ActiveRecordConstantsHelper, SorbetHelper
- Defined in:
- lib/tapioca/dsl/compilers/active_record_relations.rb
Overview
‘Tapioca::Dsl::Compilers::ActiveRecordRelations` decorates RBI files for subclasses of `ActiveRecord::Base` and adds [relation](api.rubyonrails.org/classes/ActiveRecord/Relation.html), [collection proxy](api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html), [query](api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html), [spawn](api.rubyonrails.org/classes/ActiveRecord/SpawnMethods.html), [finder](api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html), and [calculation](api.rubyonrails.org/classes/ActiveRecord/Calculations.html) methods.
The compiler defines 3 (synthetic) modules and 3 (synthetic) classes to represent relations properly.
For a given model ‘Model`, we generate the following classes:
-
A ‘Model::PrivateRelation` that subclasses `ActiveRecord::Relation`. This synthetic class represents
a relation on ‘Model` whose methods which return a relation always return a `Model::PrivateRelation` instance.
-
‘Model::PrivateAssociationRelation` that subclasses `ActiveRecord::AssociationRelation`. This synthetic
class represents a relation on a singular association of type ‘Model` (e.g. `foo.model`) whose methods which return a relation will always return a `Model::PrivateAssociationRelation` instance. The difference between this class and the previous one is mainly that an association relation also keeps track of the resource association for this relation.
-
‘Model::PrivateCollectionProxy` that subclasses from `ActiveRecord::Associations::CollectionProxy`.
This synthetic class represents a relation on a plural association of type ‘Model` (e.g. `foo.models`) whose methods which return a relation will always return a `Model::PrivateAssociationRelation` instance. This class represents a collection of `Model` instances with some extra methods to `build`, `create`, etc new `Model` instances in the collection.
and the following modules:
-
‘Model::GeneratedRelationMethods` holds all the relation methods with the return type of
‘Model::PrivateRelation`. For example, calling `all` on the `Model` class or an instance of `Model::PrivateRelation` class will always return a `Model::PrivateRelation` instance, thus the signature of `all` is defined with that return type in this module.
-
‘Model::GeneratedAssociationRelationMethods` holds all the relation methods with the return type
of ‘Model::PrivateAssociationRelation`. For example, calling `all` on an instance of `Model::PrivateAssociationRelation` or an instance of `Model::PrivateCollectionProxy` class will always return a `Model::PrivateAssociationRelation` instance, thus the signature of `all` is defined with that return type in this module.
-
‘Model::CommonRelationMethods` holds all the relation methods that do not depend on the type of
relation in their return type. For example, ‘find_by!` will always return the same type (a `Model` instance), regardless of what kind of relation it is called on, and so belongs in this module. This module is used to reduce the replication of methods between the previous two modules.
Additionally, the actual ‘Model` class extends both `Model::CommonRelationMethods` and `Model::PrivateRelation` modules, so that, for example, `find_by` and `all` can be chained off of the `Model` class.
CAUTION: The generated relation classes are named ‘PrivateXXX` intentionally to reflect the fact that they represent private subconstants of the Active Record model. As such, these types do not exist at runtime, and their counterparts that do exist at runtime are marked `private_constant` anyway. For that reason, these types cannot be used in user code or in `sig`s inside Ruby files, since that will make the runtime checks fail.
For example, with the following ‘ActiveRecord::Base` subclass:
~~~rb class Post < ApplicationRecord end ~~~
this compiler will produce the RBI file ‘post.rbi` with the following content: ~~~rbi # post.rbi # typed: true
class Post
extend CommonRelationMethods
extend GeneratedRelationMethods
module CommonRelationMethods
sig { params(block: T.nilable(T.proc.params(record: ::Post).returns(T.untyped))).returns(T::Boolean) }
def any?(&block); end
# ...
end
module GeneratedAssociationRelationMethods
sig { returns(PrivateAssociationRelation) }
def all; end
# ...
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
def where(*args, &blk); end
end
module GeneratedRelationMethods
sig { returns(PrivateRelation) }
def all; end
# ...
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
def where(*args, &blk); end
end
class PrivateAssociationRelation < ::ActiveRecord::AssociationRelation
include CommonRelationMethods
include GeneratedAssociationRelationMethods
sig { returns(T::Array[::Post]) }
def to_a; end
sig { returns(T::Array[::Post]) }
def to_ary; end
Elem = type_member { { fixed: ::Post } }
end
class PrivateCollectionProxy < ::ActiveRecord::Associations::CollectionProxy
include CommonRelationMethods
include GeneratedAssociationRelationMethods
sig do
params(records: T.any(::Post, T::Array[::Post], T::Array[PrivateCollectionProxy]))
.returns(PrivateCollectionProxy)
end
def <<(*records); end
# ...
end
class PrivateRelation < ::ActiveRecord::Relation
include CommonRelationMethods
include GeneratedRelationMethods
sig { returns(T::Array[::Post]) }
def to_a; end
sig { returns(T::Array[::Post]) }
def to_ary; end
Elem = type_member { { fixed: ::Post } }
end
end ~~~
Constant Summary collapse
- ConstantType =
type_member { { fixed: T.class_of(::ActiveRecord::Base) } }
- ID_TYPES =
From ActiveRecord::ConnectionAdapter::Quoting#quote, minus nil
T.let( [ "String", "Symbol", "::ActiveSupport::Multibyte::Chars", "T::Boolean", "BigDecimal", "Numeric", "::ActiveRecord::Type::Binary::Data", "::ActiveRecord::Type::Time::Value", "Date", "Time", "::ActiveSupport::Duration", "T::Class[T.anything]", ].to_set.freeze, T::Set[String], )
- ASSOCIATION_METHODS =
T.let( ::ActiveRecord::AssociationRelation.instance_methods - ::ActiveRecord::Relation.instance_methods, T::Array[Symbol], )
- COLLECTION_PROXY_METHODS =
T.let( ::ActiveRecord::Associations::CollectionProxy.instance_methods - ::ActiveRecord::AssociationRelation.instance_methods, T::Array[Symbol], )
- QUERY_METHODS =
T.let( begin # Grab all Query methods query_methods = ActiveRecord::QueryMethods.instance_methods(false) # Grab all Spawn methods query_methods |= ActiveRecord::SpawnMethods.instance_methods(false) # Remove the ones we know are private API query_methods -= [:all, :arel, :build_subquery, :construct_join_dependency, :extensions, :spawn] # Remove "group" which needs a custom return type for GroupChains query_methods -= [:group] # Remove "where" which needs a custom return type for WhereChains query_methods -= [:where] # Remove the methods that ... query_methods .grep_v(/_clause$/) # end with "_clause" .grep_v(/_values?$/) # end with "_value" or "_values" .grep_v(/=$/) # end with "="" .grep_v(/(?<!uniq)!$/) # end with "!" except for "uniq!" end, T::Array[Symbol], )
- WHERE_CHAIN_QUERY_METHODS =
T.let( ActiveRecord::QueryMethods::WhereChain.instance_methods(false), T::Array[Symbol], )
- FINDER_METHODS =
T.let(ActiveRecord::FinderMethods.instance_methods(false), T::Array[Symbol])
- SIGNED_FINDER_METHODS =
T.let( defined?(ActiveRecord::SignedId) ? ActiveRecord::SignedId::ClassMethods.instance_methods(false) : [], T::Array[Symbol], )
- BATCHES_METHODS =
T.let(ActiveRecord::Batches.instance_methods(false), T::Array[Symbol])
- CALCULATION_METHODS =
T.let(ActiveRecord::Calculations.instance_methods(false), T::Array[Symbol])
- ENUMERABLE_QUERY_METHODS =
T.let([:any?, :many?, :none?, :one?], T::Array[Symbol])
- FIND_OR_CREATE_METHODS =
T.let( [:find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :create_or_find_by, :create_or_find_by!], T::Array[Symbol], )
- BUILDER_METHODS =
T.let([:new, :create, :create!, :build], T::Array[Symbol])
- TO_ARRAY_METHODS =
T.let([:to_ary, :to_a], T::Array[Symbol])
Constants included from SorbetHelper
SorbetHelper::FEATURE_REQUIREMENTS, SorbetHelper::SORBET_BIN, SorbetHelper::SORBET_EXE_PATH_ENV_VAR, SorbetHelper::SORBET_GEM_SPEC, SorbetHelper::SORBET_PAYLOAD_URL, SorbetHelper::SPOOM_CONTEXT
Constants included from Helpers::ActiveRecordConstantsHelper
Helpers::ActiveRecordConstantsHelper::AssociationMethodsModuleName, Helpers::ActiveRecordConstantsHelper::AssociationRelationClassName, Helpers::ActiveRecordConstantsHelper::AssociationRelationGroupChainClassName, Helpers::ActiveRecordConstantsHelper::AssociationRelationMethodsModuleName, Helpers::ActiveRecordConstantsHelper::AssociationRelationWhereChainClassName, Helpers::ActiveRecordConstantsHelper::AssociationsCollectionProxyClassName, Helpers::ActiveRecordConstantsHelper::AttributeMethodsModuleName, Helpers::ActiveRecordConstantsHelper::CommonRelationMethodsModuleName, Helpers::ActiveRecordConstantsHelper::DelegatedTypesModuleName, Helpers::ActiveRecordConstantsHelper::ReflectionType, Helpers::ActiveRecordConstantsHelper::RelationClassName, Helpers::ActiveRecordConstantsHelper::RelationGroupChainClassName, Helpers::ActiveRecordConstantsHelper::RelationMethodsModuleName, Helpers::ActiveRecordConstantsHelper::RelationWhereChainClassName, Helpers::ActiveRecordConstantsHelper::SecureTokensModuleName, Helpers::ActiveRecordConstantsHelper::StoredAttributesModuleName
Constants included from Runtime::Reflection
Runtime::Reflection::ANCESTORS_METHOD, Runtime::Reflection::CLASS_METHOD, Runtime::Reflection::CONSTANTS_METHOD, Runtime::Reflection::EQUAL_METHOD, Runtime::Reflection::METHOD_METHOD, Runtime::Reflection::NAME_METHOD, Runtime::Reflection::OBJECT_ID_METHOD, Runtime::Reflection::PRIVATE_INSTANCE_METHODS_METHOD, Runtime::Reflection::PROTECTED_INSTANCE_METHODS_METHOD, Runtime::Reflection::PUBLIC_INSTANCE_METHODS_METHOD, Runtime::Reflection::REQUIRED_FROM_LABELS, Runtime::Reflection::SINGLETON_CLASS_METHOD, Runtime::Reflection::SUPERCLASS_METHOD, Runtime::Reflection::UNDEFINED_CONSTANT
Instance Attribute Summary
Attributes inherited from Tapioca::Dsl::Compiler
Class Method Summary collapse
Instance Method Summary collapse
Methods included from SorbetHelper
#sorbet, #sorbet_path, #sorbet_supports?
Methods inherited from Tapioca::Dsl::Compiler
#add_error, #compiler_enabled?, handles?, #initialize, processable_constants, requested_constants=
Methods included from T::Generic::TypeStoragePatch
#[], #has_attached_class!, #type_member, #type_template
Methods included from Runtime::Reflection
#abstract_type_of, #ancestors_of, #are_equal?, #class_of, #constant_defined?, #constantize, #constants_of, #descendants_of, #file_candidates_for, #final_module?, #inherited_ancestors_of, #method_of, #name_of, #name_of_type, #object_id_of, #private_instance_methods_of, #protected_instance_methods_of, #public_instance_methods_of, #qualified_name_of, #resolve_loc, #sealed_module?, #signature_of, #signature_of!, #singleton_class_of, #superclass_of
Methods included from Runtime::AttachedClassOf
Methods included from RBIHelper
#as_nilable_type, #as_non_nilable_type, #create_block_param, #create_kw_opt_param, #create_kw_param, #create_kw_rest_param, #create_opt_param, #create_param, #create_rest_param, #create_typed_param, #sanitize_signature_types, serialize_type_variable, #valid_method_name?, #valid_parameter_name?
Constructor Details
This class inherits a constructor from Tapioca::Dsl::Compiler
Class Method Details
.gather_constants ⇒ Object
189 190 191 |
# File 'lib/tapioca/dsl/compilers/active_record_relations.rb', line 189 def gather_constants ActiveRecord::Base.descendants.reject(&:abstract_class?) end |
Instance Method Details
#decorate ⇒ Object
178 179 180 181 182 183 |
# File 'lib/tapioca/dsl/compilers/active_record_relations.rb', line 178 def decorate create_classes_and_includes create_common_methods create_relation_methods create_association_relation_methods end |