Ruby on rails 类内的元编程谓词方法-Rails 4

Ruby on rails 类内的元编程谓词方法-Rails 4,ruby-on-rails,ruby,metaprogramming,predicate,Ruby On Rails,Ruby,Metaprogramming,Predicate,我有一个名为User的类,它有许多:角色,通过:User\u角色。我试图为用户类中的关联角色编写元程序谓词方法,如下所示: class User < ActiverRecord::Base ... Role.all.pluck(:name).each do |role_name| define_method("#{role_name}?") do roles.map(&:name).include?(role_name) end e

我有一个名为
User
的类,它有许多:角色,通过:User\u角色。我试图为用户类中的关联角色编写元程序谓词方法,如下所示:

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

您所采用的元编程技术旨在利用这样一个事实,即当类被加载时,将执行位于类内部的代码。因此,当Rails加载您的
User
类时,定义谓词方法的逻辑正在执行,并且正在为任何
角色创建方法。加载
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。现在,如果您再次
要求
该文件,则不会发生任何事情。您可以使用
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
类,然后进行断言

您所使用的元编程技术旨在利用这样一个事实,即在加载类时,将执行位于类内部的代码。因此,当Rails加载您的
User
类时,定义谓词方法的逻辑正在执行,并且正在为任何
角色创建方法。加载
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。现在,如果您再次
要求
该文件,则不会发生任何事情。您可以使用
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
类,然后进行断言

它应该是
polck(:name)
,不带
&
。这可能会中断集合的构建,导致永远不会调用
define_方法
。@64位oops,已更新。我决定将
.map
替换为
pluck
,但忘了将
删除到\u proc
。仍然返回未定义的方法
howeverAh ha。在这种情况下,我认为答案可能更根本地在于元编程是如何工作的。加载
用户
类时,即测试运行之前,正在执行您的
角色.all…
逻辑。因此,将定义的唯一方法是针对在加载
User
之前已经存在的角色。在
User
加载后,在测试中创建角色不会对先前运行的代码产生任何影响。希望这是有意义的…它应该是
pluck(:name)
而不是
&
。这可能会中断集合的构建,导致永远不会调用
define_方法
。@64位oops,已更新。我决定将
.map
替换为
pluck
,但忘了将
删除到\u proc
。仍然返回未定义的方法
howeverAh ha。在这种情况下,我认为答案可能更根本地在于元编程是如何工作的。加载
用户
类时,即测试运行之前,正在执行您的
角色.all…
逻辑。因此,将定义的唯一方法是针对在加载
User
之前已经存在的角色。在
User
加载后,在测试中创建角色不会对先前运行的代码产生任何影响。希望这是有道理的…绝对!最后将此测试逻辑移动到
角色
的单元测试中,以便在创建新角色后实例化
用户
。虽然这是一个答案,提供了一个有效的测试,但我不会采用这种方法。这样,除非冷重启/重新加载,否则向应用程序添加新角色不会产生任何效果。我会在提交后在
角色上定义回调,更新
用户
类中的谓词方法。我只是想解释为什么@Dimitry\N在测试中会遇到这个问题,以确保他理解底层元编程概念是如何工作的,等等!最后将此测试逻辑移动到
角色
的单元测试中,以便在创建新角色后实例化
用户
。虽然这是一个答案,提供了一个有效的测试,但我不会采用这种方法。这样,除非冷重启/重新加载,否则向应用程序添加新角色不会产生任何效果。我会在提交后在
角色上定义回调,更新
用户
类中的谓词方法。我只是想解释为什么@Dimitry\N在测试中会遇到这个问题,以确保他理解底层元编程概念是如何工作的,等等。