class Tapioca::Dsl::Compilers::ActiveModelAttributes
~~~
end
def name=(name); end
sig { params(name: T.nilable(::String)).returns(T.nilable(::String)) }
def name; end
sig { returns(T.nilable(::String)) }
class Shop
# typed: true
~~~rbi
this compiler will produce an RBI file with the following content:
~~~
end
attribute :name, :string
include ActiveModel::Attributes
class Shop
~~~rb
For example, with the following class:
classes that use [‘ActiveModel::Attributes`](edgeapi.rubyonrails.org/classes/ActiveModel/Attributes/ClassMethods.html).
`Tapioca::Dsl::Compilers::ActiveModelAttributes` decorates RBI files for all
def attribute_methods_for_constant
def attribute_methods_for_constant 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 patterns.flat_map do |pattern| constant.attribute_types.filter_map do |name, value| next unless handle_method_pattern?(pattern) [pattern.method_name(name), type_for(value)] end end end
def decorate
def decorate attribute_methods = attribute_methods_for_constant return if attribute_methods.empty? root.create_path(constant) do |klass| attribute_methods.each do |method, attribute_type| generate_method(klass, method, attribute_type) end end end
def gather_constants
def gather_constants all_classes.grep(::ActiveModel::Attributes::ClassMethods) end
def generate_method(klass, method, type)
def generate_method(klass, method, type) if method.end_with?("=") parameter = create_param("value", type: type) klass.create_method( method, parameters: [parameter], return_type: type, ) else klass.create_method(method, return_type: type) end end
def handle_method_pattern?(pattern)
def handle_method_pattern?(pattern) target = if pattern.respond_to?(:method_missing_target) # Pre-Rails 6.0, the field is named "method_missing_target" T.unsafe(pattern).method_missing_target elsif pattern.respond_to?(:target) # Rails 6.0+ has renamed the field to "target" pattern.target else # https://github.com/rails/rails/pull/44367/files T.unsafe(pattern).proxy_target end HANDLED_METHOD_TARGETS.include?(target.to_s) end
def type_for(attribute_type_value)
def type_for(attribute_type_value) case attribute_type_value when ActiveModel::Type::Boolean as_nilable_type("T::Boolean") when ActiveModel::Type::Date as_nilable_type("::Date") when ActiveModel::Type::DateTime, ActiveModel::Type::Time as_nilable_type("::Time") when ActiveModel::Type::Decimal as_nilable_type("::BigDecimal") when ActiveModel::Type::Float as_nilable_type("::Float") when ActiveModel::Type::Integer as_nilable_type("::Integer") when ActiveModel::Type::String as_nilable_type("::String") else type = Helpers::ActiveModelTypeHelper.type_for(attribute_type_value) type = as_nilable_type(type) if Helpers::ActiveModelTypeHelper.assume_nilable?(attribute_type_value) type end end