Suggestion: Add an ability to enforce a type on `ActiveModel::Attribute`
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 873
- Forks
- 164
- Avg merge
- 4d 27m
- Merged PRs (30d)
- 9
Description
The idea would be to add a way to express the Sorbet type for attributes.
Example model:
class Foo
include ActiveModel::Model
attribute :model
validates :model, presence: true, type: T.class_of(ApplicationRecord)
end
I have some code that works on my project, but the extension is a bit hacky:
Validator:
module ActiveModel
module Validations
class TypeValidator < EachValidator
sig { params(options: T::Hash[Symbol, T.untyped]).void }
def initialize(options)
with = options.delete(:with)
raise ArgumentError, ":with cannot be blank" if with.nil?
@with = T.let(with, T.any(T::Types::Base, String, Module, Proc))
super
end
sig { params(record: ActiveModel::Validations, attribute: Symbol, value: T.untyped).void }
def validate_each(record, attribute, value)
unless (message = type.error_message_for_obj(value)).nil?
record.errors.add(attribute, :class, message:)
end
end
sig { returns(T::Types::Base) }
def type
@type ||= T.let(
begin
with = @with
with = with.call if with.is_a?(Proc)
with = with.safe_constantize if with.is_a?(String)
with = T::Utils.coerce(with) if with.is_a?(Module)
with
end,
T.nilable(T::Types::Base),
)
end
end
end
end
Extension:
# typed: ignore
# frozen_string_literal: true
require "active_model/attributes"
require "tapioca/dsl/compilers/active_model_attributes"
module Tapioca
module Compilers
module ActiveModelTypeValidator
def attribute_methods_for_constant
return super unless constant.is_a?(ActiveModel::Validations::ClassMethods)
attribute_methods = super.to_h # Convert to hash for easier manipulation
attribute_methods.each do |method, _|
constant
.validators_on(method)
.grep(ActiveModel::Validations::TypeValidator)
.each do |validator|
attribute_methods[method] = validator.type.to_s
attribute_methods["#{method}="] = validator.type.to_s
break
end
end
attribute_methods.to_a
end
end
Dsl::Compilers::ActiveModelAttributes.prepend(ActiveModelTypeValidator)
end
end
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading active_model/attributes and the Tapioca ActiveModel attributes compiler referenced in the issue. Compare the proposed TypeValidator and compiler extension with the existing attribute RBI generation, then define how the shown type-enforced attribute should be represented for both reading and writing; done means the behavior is covered by the project's relevant tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ruby
- Domain
- developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100