1

Я строю камень Rails 3, который существенно изменяет записи, возвращаемые из запроса ActiveRecord. Одна из вещей, которые я делаю, - переопределить методы method_missing и respond_to?, но, похоже, мое определение respond_to? приводит к бесконечному циклу, который вызывает ошибку «SystemStackError: уровень стека слишком глубокий».Object.respond_to? застрял в бесконечном цикле

Вот мои оригинальные определения этих методов:

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 
... 

Однако, в любое время я вижу найти обратный вызов, что кажется, чтобы быть пойманным в бесконечном цикле:

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

Проблема закончилась тем, эта функция:

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? в месте до того, как вызывается обратный вызов find (в коде, не включенном в мой исходный вопрос), вызов send(parent_assocation_name) вызывал рекурсивный вызов respond_to?.

+1

Было бы понятнее, если бы вы использовали точный язык. 'parent_association_name' не является переменной, и я думаю, что' employee' всегда определен, хотя он может и не быть загружен. Не уверен, что вы действительно имеете в виду «обратный вызов». Я все еще не уверен, как то, что вы описываете, не то, что я говорил, и то, как вы его исправили. –

0

parent_association Если возвращает новый объект AR, он также наследует свой хак, поэтому вызов respond_to? на нем будет вызывать respond_to?, который создаст AR новый объект, и т.д ...

+0

Это неверно, если другой объект ActiveRecord, который он создает, фактически реализует метод, который должен иметь место здесь. Например, у другого объекта AR будет такой метод, как '.name' (из-за реализованных атрибутов), который, возможно, не был у исходного объекта AR. В какой-то момент он просто называет 'super' и возвращает, поскольку метод фактически определен на объекте. –

+0

Просто для того, чтобы следить за ним, это было рекурсией через 'response_to?', Но это не проблема того, что вызываемая функция не определена рекурсивно на отдельных объектах AR, как вы упомянули. –