我正在构建一个Rails3gem,它主要修改从ActiveRecord查询返回的记录。我正在做的一件事是覆盖method_missing
和respond_to?
方法,但我的respond_to?
定义似乎导致了一个无限循环,该循环抛出了一个"SystemStackError:stack level too deep"错误。
以下是我对这些方法的原始定义:
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
在试图了解为什么会发生这种无限循环的过程中,我用一些"之前"one_answers"之后"的输出重新构造了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
...
然而,每当我看到find回调时,它似乎被捕获在一个无限循环中:
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
我做错了什么,我似乎需要这个黑客?我该如何避免呢?
如果parent_association
返回一个新的AR对象,它也会继承你的破解,所以在它上调用respond_to?
会调用你的respond_to?
,它会创建一个AR新对象,等等…
问题最终是这个函数:
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
因为直到find回调执行之后才在模型实例上定义employee
,并且因为我在调用find回调之前的某个位置第一次调用respond_to?
(在我最初的问题中没有包含的代码中),所以对send(parent_assocation_name)
的调用导致了对respond_to?
的递归调用。