1

我正在构建一个 Rails 3 gem,它本质上修改了从 ActiveRecord 查询返回的记录。我正在做的一件事是覆盖method_missingandrespond_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

我做错了什么,我似乎需要这个黑客?我该如何避免呢?

4

2 回答 2

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在执行查找回调之后才在模型实例上定义,并且因为我首先在调用respond_to?查找回调之前的某个位置调用(在我的原始问题中未包含的代码中),所以调用send(parent_assocation_name)导致递归调用到respond_to?.

于 2012-04-09T19:21:20.243 回答
0

如果parent_association返回一个新的 AR 对象,它也会继承你的 hack,所以调用respond_to?它会调用 your respond_to?,这将创建一个 AR 新对象,等等......

于 2012-03-13T14:20:34.130 回答