我正在构建一个Rails 3 gem,它实质上修改了从ActiveRecord查询返回的记录。我正在做的事情之一就是重写method_missing
和respond_to?
方法,但似乎我的respond_to?
定义导致无限循环抛出“SystemStackError:stack level”太深了“错误。
以下是我对这些方法的原始定义:
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
...
但是,每当我看到一个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
我做错了什么,我似乎需要这个黑客?我怎么能避免它?
答案 0 :(得分:0)
如果parent_association
返回一个新的AR对象,它也会继承你的hack,所以在它上面调用respond_to?
会调用你的respond_to?
,这将创建一个AR新对象,等等。 ..
答案 1 :(得分: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
,并且因为我在调用find回调之前首先在某个地方调用respond_to?
(代码中没有包含我的原始问题),对send(parent_assocation_name)
的调用导致对respond_to?
的递归调用。