Object.respond_to?卡在无限循环
我正在构建一个Rails 3 gem,它基本上修改了从ActiveRecord查询返回的记录。我正在做的一件事是重写method_missing
和respond_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
我在做什么错,我似乎需要这个黑客?我该如何避免它?
问题最终被此功能:
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?
进行递归调用。
如果parent_association
返回一个新的AR对象,它也将继承你的黑客,所以要求它respond_to?
会打电话给你respond_to?
,这将创建一个AR新的对象,等...
如果它创建的另一个ActiveRecord对象实际上实现了方法,这应该是这里的情况。例如,另一个AR对象将有一个类似于'.name'的方法(由于实现了属性),原始AR对象可能没有这个方法。在某些时候,它只是调用'super'并返回,因为该方法实际上是在对象上定义的。 – 2012-03-13 14:46:58
只是为了跟进,它确实最终通过'respond_to?'进行递归,但它不是一个被称为在单独AR对象上递归定义的函数的问题,正如您所提到的。 – 2012-04-09 19:22:09
如果你使用精确的语言,它会更清晰。 'parent_association_name'不是一个变量,我认为'employee'总是被定义的,尽管它可能不会被加载。不确定你实际上是否指“回调”。我仍然不确定你所描述的不是我所说的,也不是你如何修正它。 – 2012-04-09 20:05:52