我正在构建一个 Rails 3 gem,它本质上修改了从 ActiveRecord 查询返回的记录。我正在做的一件事是覆盖method_missing
andrespond_to?
方法,但似乎我的respond_to?
定义导致无限循环抛出“SystemStackError:堆栈级别太深”错误。
以下是我对这些方法的原始定义:
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 和 after 调用:
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
我做错了什么,我似乎需要这个黑客?我该如何避免呢?