我有一个has_many items的Rails模型
class Plan < ApplicationRecord
extend T::Sig
has_many :items, dependent: :destroy
before_save do
# hyper simple test method to illustrat problem
puts items
end
end然而,冰沙似乎不能与has_many :items联系起来。当我运行Sorbet类型检查时,我得到以下错误:
$ srb tc
app/models/plan.rb:11: Method items does not exist on T.class_of(Plan) https://srb.help/7003
11 | items
^^^^^
Did you mean:
sorbet/rails-rbi/models/plan.rbi:86: Plan::GeneratedAssociationMethods#items
86 | def items; end对于索贝特的问题,答案是肯定的--我的意思是这种方法。困惑从何而来?为什么RBI文件中的.items定义不能满足Sorbet需要知道这个方法是在哪里定义的?
发布于 2021-05-26 23:11:04
好吧,所以这被证明是对Rails的误解(Ruby?)而不是冰沙。跳过这一点实际上是为了冰激凌,因为它有助于发现和解决这个问题。
问题是,当您将一个块传递给before_save时,调用的是类(Plan),而不是实例(plan)。取而代之的是,一个实例被传入其中。
所以以原始代码为例:
class Plan < ApplicationRecord
extend T::Sig
has_many :items, dependent: :destroy
before_save do
# hyper simple test method to illustrate problem
puts items
end
end这将导致Plan.before_save(plan)的执行。其中plan是Plan的实例。因此,在上面的例子中,items被凭空拉出来,不能工作。
两种有效的语法
class Plan < ApplicationRecord
extend T::Sig
has_many :items, dependent: :destroy
before_save do |plan| # <= notice the argument
puts plan.items
end
end都会起作用的。也会是这样:
class Plan < ApplicationRecord
extend T::Sig
has_many :items, dependent: :destroy
before_save :put_items
def put_items
puts items
end
end我不太确定是什么让第二个魔术发挥作用,是Ruby魔术还是Rails魔术,但有时魔术太多了,我不喜欢。
发布于 2021-07-09 22:12:05
这是因为传递给before_save的proc是在类实例的上下文中运行的,而不是在类本身的上下文中运行,这在默认情况下Sorbet是无法理解的。self为这种情况提供了T.bind,它允许您告诉Sorbet将块中的self视为不同的类型
class Plan < ApplicationRecord
extend T::Sig
has_many :items, dependent: :destroy
before_save do
T.bind(self, Plan) # treats `self` as `Plan` instead of `T.class_of(Plan)`
# hyper simple test method to illustrate problem
puts items # type-checks fine
end
endhttps://stackoverflow.com/questions/67705439
复制相似问题