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::Concern
does.
What is happening here is that when include ActiveModel::Model
is called, it then includes ActiveModel::Validations
. But since both ActiveModel::Model
and ActiveModel::Validations
are concerns, both ActiveModel::Model::ClassMethods
and ActiveModel::Validations::ClassMethods
are added to Form
using extend
. It is the ActiveModel::Validations::ClassMethods
that provides the validates
method, and that extend
is what Sorbet cannot see statically.
First of all, the basic srb
tooling has no knowledge of ActiveSupport::Concern
and will not generate proper mixes_in_class_methods
calls to make Sorbet aware of the ClassMethods
that 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::ClassMethods
in Form
, 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.