Object.respond_to?卡在无限循环中

时间:2012-03-13 12:52:49

标签: ruby-on-rails ruby stack-overflow method-missing respond-to

我正在构建一个Rails 3 gem,它实质上修改了从ActiveRecord查询返回的记录。我正在做的事情之一就是重写method_missingrespond_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

我做错了什么,我似乎需要这个黑客?我怎么能避免它?

2 个答案:

答案 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?的递归调用。