1

我正在构建一个Rails 3 gem,它基本上修改了从ActiveRecord查询返回的记录。我正在做的一件事是重写method_missingrespond_to?方法,但似乎我的respond_to?定义导致了一个无限循环,导致“SystemStackError:堆栈层太深”错误。Object.respond_to?卡在无限循环

这是我原来的这些方法的定义:

def respond_to?(name, *args) 
    super(name, *args) || parent_association.respond_to?(name) 
end 

def method_missing(name, *args, &block) 
    if parent_association.respond_to?(name) 
    parent_association.send(name, *args, &block) 
    else 
    super(name, *args, &block) 
    end 
end 

def parent_association 
    send(parent_association_name) # Essentially yields another ActiveRecord 
           # instance (e.g.: instance of User), but 
           # never returns itself. 
end 

在试图了解为什么这个无限循环正在发生,我与一些“之前”和重组respond_to?输出“后”,看看它卡住。

def respond_to?(name, *args) 
    return true if super(name, *args) 
    puts "before (#{name})" 
    result = parent_association.respond_to?(name) 
    puts "after" 
    result 
end 

运行时,似乎各种回调和属性运行方法如预期,有单前和呼叫后每个:

before (_run__374051839217347232__initialize__1707831318230746190__callbacks) 
after 
before (_run__374051839217347232__validation__1707831318230746190__callbacks) 
after 
before (_run__374051839217347232__validate__1707831318230746190__callbacks) 
after 
before (_run__374051839217347232__save__1707831318230746190__callbacks) 
after 
before (_run__374051839217347232__create__1707831318230746190__callbacks) 
after 
before (created_at) 
after 
before (created_on) 
after 
... 

但是,任何时候我看到查找回调,那似乎正在陷入一个死循环:

before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
... 
SystemStackError: stack level too deep 

如果我砍我的respond_to?,那么一切似乎顺利运行:

def respond_to?(name, *args) 
    return true if super(name, *args) 
    return false if name =~ /^_run_.*_find_.*_callbacks$/ 
    parent_association.respond_to?(name) 
end 

我在做什么错,我似乎需要这个黑客?我该如何避免它?

回答

0

问题最终被此功能:

def parent_association 
    send(parent_association_name) # Essentially yields another ActiveRecord 
           # instance (e.g.: instance of User), but 
           # never returns itself. 
end 

可变parent_association_name是一样的东西employee,其通过类似的东西来定义:

belongs_to :employee 

因为employee没有在模型实例上定义,直到执行查找回调之后,并且因为在查找回调被调用(在我的原始问题中包含代码否)时我第一次调用respond_to?,所以调用send(parent_assocation_name)正在对respond_to?进行递归调用。

+1

如果你使用精确的语言,它会更清晰。 'parent_association_name'不是一个变量,我认为'employee'总是被定义的,尽管它可能不会被加载。不确定你实际上是否指“回调”。我仍然不确定你所描述的不是我所说的,也不是你如何修正它。 – 2012-04-09 20:05:52

0

如果parent_association返回一个新的AR对象,它也将继承你的黑客,所以要求它respond_to?会打电话给你respond_to?,这将创建一个AR新的对象,等...

+0

如果它创建的另一个ActiveRecord对象实际上实现了方法,这应该是这里的情况。例如,另一个AR对象将有一个类似于'.name'的方法(由于实现了属性),原始AR对象可能没有这个方法。在某些时候,它只是调用'super'并返回,因为该方法实际上是在对象上定义的。 – 2012-03-13 14:46:58

+0

只是为了跟进,它确实最终通过'respond_to?'进行递归,但它不是一个被称为在单独AR对象上递归定义的函数的问题,正如您所提到的。 – 2012-04-09 19:22:09