Class: Tapioca::Dsl::Compilers::ActiveRecordColumns
- Inherits:
-
Tapioca::Dsl::Compiler
- Object
- Tapioca::Dsl::Compiler
- Tapioca::Dsl::Compilers::ActiveRecordColumns
- Extended by:
- T::Sig
- Includes:
- Helpers::ActiveRecordConstantsHelper
- Defined in:
- lib/tapioca/dsl/compilers/active_record_columns.rb
Overview
‘Tapioca::Dsl::Compilers::ActiveRecordColumns` refines RBI files for subclasses of [`ActiveRecord::Base`](api.rubyonrails.org/classes/ActiveRecord/Base.html). This compiler is only responsible for defining the attribute methods that would be created for columns and virtual attributes that are defined in the Active Record model.
This compiler accepts a ‘ActiveRecordColumnTypes` option that can be used to specify how the types of the column related methods should be generated. The option can be one of the following:
- `persisted` (_default_): The methods will be generated with the type that matches the actual database
column type as the return type. This means that if the column is a string, the method return type
will be `String`, but if the column is also nullable, then the return type will be `T.nilable(String)`. This
mode basically treats each model as if it was a valid and persisted model. Note that this makes typing
Active Record models easier, but does not match the behaviour of non-persisted or invalid models, which can
have all kinds of non-sensical values in their column attributes.
- `nilable`: All column methods will be generated with `T.nilable` return types. This is strictly the most
correct way to type the methods, but it can make working with the models more cumbersome, as you will have to
handle the `nil` cases explicitly using `T.must` or the safe navigation operator `&.`, even for valid
persisted models.
- `untyped`: The methods will be generated with `T.untyped` return types. This mode is practical if you are not
ready to start typing your models strictly yet, but still want to generate RBI files for them.
For example, with the following model class: ~~~rb class Post < ActiveRecord::Base end ~~~
and the following database schema:
~~~rb # db/schema.rb create_table :posts do |t|
t.string :title, null: false
t.string :body
t.boolean :published
t.
end ~~~
this compiler will, by default, produce the following methods in the RBI file ‘post.rbi`:
~~~rbi # post.rbi # typed: true class Post
include GeneratedAttributeMethods
module GeneratedAttributeMethods
sig { returns(T.nilable(::String)) }
def body; end
sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) }
def body=; end
sig { returns(T::Boolean) }
def body?; end
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
def created_at; end
sig { params(value: ::ActiveSupport::TimeWithZone).returns(::ActiveSupport::TimeWithZone) }
def created_at=; end
sig { returns(T::Boolean) }
def created_at?; end
sig { returns(T.nilable(T::Boolean)) }
def published; end
sig { params(value: T::Boolean).returns(T::Boolean) }
def published=; end
sig { returns(T::Boolean) }
def published?; end
sig { returns(::String) }
def title; end
sig { params(value: ::String).returns(::String) }
def title=(value); end
sig { returns(T::Boolean) }
def title?; end
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
def updated_at; end
sig { params(value: ::ActiveSupport::TimeWithZone).returns(::ActiveSupport::TimeWithZone) }
def updated_at=; end
sig { returns(T::Boolean) }
def updated_at?; end
## Also the methods added by https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Dirty.html
## Also the methods added by https://api.rubyonrails.org/classes/ActiveModel/Dirty.html
## Also the methods added by https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/BeforeTypeCast.html
end
end ~~~
However, if ‘ActiveRecordColumnTypes` is set to `nilable`, the `title` method will be generated as: ~~~rbi
sig { returns(T.nilable(::String)) }
def title; end
~~~ and if the option is set to ‘untyped`, the `title` method will be generated as: ~~~rbi
sig { returns(T.untyped) }
def title; end
~~~
Constant Summary collapse
- ConstantType =
type_member { { fixed: T.class_of(ActiveRecord::Base) } }
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
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
Instance Attribute Summary
Attributes inherited from Tapioca::Dsl::Compiler
Class Method Summary collapse
Instance Method Summary collapse
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?
Methods included from SorbetHelper
#sorbet, #sorbet_path, #sorbet_supports?
Constructor Details
This class inherits a constructor from Tapioca::Dsl::Compiler
Class Method Details
.gather_constants ⇒ Object
169 170 171 |
# File 'lib/tapioca/dsl/compilers/active_record_columns.rb', line 169 def gather_constants descendants_of(::ActiveRecord::Base).reject(&:abstract_class?) end |
Instance Method Details
#decorate ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/tapioca/dsl/compilers/active_record_columns.rb', line 130 def decorate return unless constant.table_exists? # We need to call this to ensure that some attribute aliases are defined, e.g. # `id_value` as an alias for `id`. # I think this is a regression on Rails 7.1, but we are where we are. constant.define_attribute_methods root.create_path(constant) do |model| model.create_module(AttributeMethodsModuleName) do |mod| (constant.attribute_names + ["id"]).uniq.each do |attribute_name| add_methods_for_attribute(mod, attribute_name) end constant.attribute_aliases.each do |attribute_name, column_name| attribute_name = attribute_name.to_s column_name = column_name.to_s patterns = if constant.respond_to?(:attribute_method_patterns) # https://github.com/rails/rails/pull/44367 constant.attribute_method_patterns else T.unsafe(constant).attribute_method_matchers end new_method_names = patterns.map { |m| m.method_name(attribute_name) } old_method_names = patterns.map { |m| m.method_name(column_name) } methods_to_add = new_method_names - old_method_names add_methods_for_attribute(mod, attribute_name, column_name, methods_to_add) end end model.create_include(AttributeMethodsModuleName) end end |