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 includesActiveModel::Validations
. But since bothActiveModel::Model
andActiveModel::Validations
are concerns, bothActiveModel::Model::ClassMethods
andActiveModel::Validations::ClassMethods
are added toForm
usingextend
. It is theActiveModel::Validations::ClassMethods
that provides thevalidates
method, and thatextend
is what Sorbet cannot see statically.First of all, the basic
srb
tooling has no knowledge ofActiveSupport::Concern
and will not generate propermixes_in_class_methods
calls to make Sorbet aware of theClassMethods
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
inForm
, 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.