在类中的元编程谓词方法 - Rails 4

时间:2016-08-01 03:54:15

标签: ruby-on-rails ruby metaprogramming predicate

我有一个名为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...>
  ...

1 个答案:

答案 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类,然后进行断言。