class Tapioca::Compilers::Dsl::ActiveModelAttributes

`Tapioca::Compilers::Dsl::ActiveModelAttributes` decorates RBI files for all classes that use [`ActiveModel::Attributes`](edgeapi.rubyonrails.org/classes/ActiveModel/Attributes/ClassMethods.html).

For example, with the following class:

~~~rb class Shop

include ActiveModel::Attributes

attribute :name, :string

end ~~~

this generator will produce an RBI file with the following content: ~~~rbi # typed: true

class Shop

sig { returns(T.nilable(::String)) }
def name; end

sig { params(name: T.nilable(::String)).returns(T.nilable(::String)) }
def name=(name); end

end ~~~

Constants

HANDLED_METHOD_TARGETS

Public Instance Methods

decorate(root, constant) click to toggle source
# File lib/tapioca/compilers/dsl/active_model_attributes.rb, line 43
def decorate(root, constant)
  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
gather_constants() click to toggle source
# File lib/tapioca/compilers/dsl/active_model_attributes.rb, line 55
def gather_constants
  all_classes.grep(::ActiveModel::Attributes::ClassMethods)
end

Private Instance Methods

attribute_methods_for(constant) click to toggle source
# File lib/tapioca/compilers/dsl/active_model_attributes.rb, line 64
def attribute_methods_for(constant)
  constant.attribute_method_matchers.flat_map do |matcher|
    constant.attribute_types.map do |name, value|
      next unless handle_method_matcher?(matcher)

      [matcher.method_name(name), type_for(value)]
    end.compact
  end
end
generate_method(klass, method, type) click to toggle source
# File lib/tapioca/compilers/dsl/active_model_attributes.rb, line 116
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
handle_method_matcher?(matcher) click to toggle source
# File lib/tapioca/compilers/dsl/active_model_attributes.rb, line 78
def handle_method_matcher?(matcher)
  target = if matcher.respond_to?(:method_missing_target)
    # Pre-Rails 6.0, the field is named "method_missing_target"
    T.unsafe(matcher).method_missing_target
  else
    # Rails 6.0+ has renamed the field to "target"
    matcher.target
  end

  HANDLED_METHOD_TARGETS.include?(target.to_s)
end
type_for(attribute_type_value) click to toggle source
# File lib/tapioca/compilers/dsl/active_model_attributes.rb, line 91
def type_for(attribute_type_value)
  type = case attribute_type_value
  when ActiveModel::Type::Boolean
    "T::Boolean"
  when ActiveModel::Type::Date
    "::Date"
  when ActiveModel::Type::DateTime, ActiveModel::Type::Time
    "::DateTime"
  when ActiveModel::Type::Decimal
    "::BigDecimal"
  when ActiveModel::Type::Float
    "::Float"
  when ActiveModel::Type::Integer
    "::Integer"
  when ActiveModel::Type::String
    "::String"
  else
    # we don't want untyped to be wrapped by T.nilable, so just return early
    return "T.untyped"
  end

  "T.nilable(#{type})"
end