对象是否响应?陷入无限循环



我正在构建一个Rails3gem,它主要修改从ActiveRecord查询返回的记录。我正在做的一件事是覆盖method_missingrespond_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?的递归调用。

最新更新