I'm using ActiveModel to validate some form objects in my Ruby on Rails. A simplified reproducible example looks like this:
# typed: true
class Form
include ActiveModel::Model
# ...
validates :name, presence: true
end
The issue is, Sorbet complains that validates does not exist on T.class_of(Form). Even though the RBI files are properly generated for the ActiveModel::Model module.
The reason why you are getting this error is because it is really hard for Sorbet to understand the mechanics of what
ActiveSupport::Concerndoes.What is happening here is that when
include ActiveModel::Modelis called, it then includesActiveModel::Validations. But since bothActiveModel::ModelandActiveModel::Validationsare concerns, bothActiveModel::Model::ClassMethodsandActiveModel::Validations::ClassMethodsare added toFormusingextend. It is theActiveModel::Validations::ClassMethodsthat provides thevalidatesmethod, and thatextendis what Sorbet cannot see statically.First of all, the basic
srbtooling has no knowledge ofActiveSupport::Concernand will not generate propermixes_in_class_methodscalls to make Sorbet aware of theClassMethodsthat are also in play. Moreover, even if that was generated, it would only apply at one level of include and would fail in this case.The workaround is to explicitly add an
extend ActiveModel::Validations::ClassMethodsinForm, but that is ugly.The best solution is to switch over to Tapioca tooling to generate proper RBI output for concerns. We will also soon start generating a solution for nested concerns like this, which should solve this properly.