Class - Rails 4 中的元编程谓词方法
Metaprogramming predicate methods inside a Class - Rails 4
我有一个名为 User
的 class,has_many: roles, through: user_roles
。我正在尝试为用户 class 中的关联角色对谓词方法进行元编程,如下所示:
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)
执行 return 现有角色名称的数组,但 define_method
永远不会被调用,并且我的规范因未定义的方法而失败:
...
subject.roles << create(:role, name: 'foo')
expect(subject.foo?).to be true #<= undefined method `foo?' for #<User...>
...
您正在使用的元编程技术旨在利用这样一个事实,即当加载 class 时,仅位于 class 内的代码将被执行。因此,当 Rails 加载您的 User
class、 时 正在执行定义谓词方法的逻辑,并且正在为任何内容创建方法Role.all
returns 在加载 User
的那一刻 。
创建一个新角色,就像您在测试中所做的那样,因此不会影响在加载 class 和执行代码时创建的谓词方法。
您可以通过在任何目录中创建一个名为 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
class,然后进行断言。
我有一个名为 User
的 class,has_many: roles, through: user_roles
。我正在尝试为用户 class 中的关联角色对谓词方法进行元编程,如下所示:
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)
执行 return 现有角色名称的数组,但 define_method
永远不会被调用,并且我的规范因未定义的方法而失败:
...
subject.roles << create(:role, name: 'foo')
expect(subject.foo?).to be true #<= undefined method `foo?' for #<User...>
...
您正在使用的元编程技术旨在利用这样一个事实,即当加载 class 时,仅位于 class 内的代码将被执行。因此,当 Rails 加载您的 User
class、 时 正在执行定义谓词方法的逻辑,并且正在为任何内容创建方法Role.all
returns 在加载 User
的那一刻 。
创建一个新角色,就像您在测试中所做的那样,因此不会影响在加载 class 和执行代码时创建的谓词方法。
您可以通过在任何目录中创建一个名为 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
class,然后进行断言。