我有一个名为User
的班级,has_many: roles, through: user_roles
。我正在尝试为用户类中的关联角色元编程谓词方法,如下所示:
class User < ActiverRecord::Base
...
Role.all.pluck(:name).each do |role_name|
define_method("#{role_name}?") do
roles.map(&:name).include?(role_name)
end
end
...
end
虽然Role.all.pluck(&:name)
确实返回了现有角色名称的数组,但define_method
永远不会被调用,并且我的规范以未定义的方法失败:
...
subject.roles << create(:role, name: 'foo')
expect(subject.foo?).to be true #<= undefined method `foo?' for #<User...>
...
答案 0 :(得分:2)
你正在使用的元编程技术意味着利用这样一个事实,即当加载类时,只是坐在类中的代码将被执行。因此,当Rails加载您的User
类时,此时正在执行定义谓词方法的逻辑,并且正在为Role.all
返回的任何内容创建方法。加载User
的时刻。
因此,在测试中创建新角色将不会影响在加载类和执行代码时创建的谓词方法。
您可以通过在任何目录中创建名为count.rb
的文件来查看此操作,其中包含以下代码:
$count += 1
然后,打开irb
并输入:
irb(main):001:0> $count = 0
=> 0
irb(main):002:0> require './count'
=> true
irb(main):003:0> $count
=> 1
请注意,加载文件时$count
加1。现在,如果您再次require
该文件,则不会发生任何事情。您可以使用load
而不是require:
# ...continued from above
irb(main):004:0> require './count'
=> false
irb(main):005:0> $count
=> 1
irb(main):006:0> load './foo.rb'
=> true
irb(main):007:0> $count
=> 2
因此,要让测试通过,您必须创建角色,然后强制重新加载User
类,然后进行断言。