2012-02-20 2 views
5

Использование Ruby 1.8.6/Rails 2.3.2Как узнать, что перехватывает «method_missing»

я заметил, что любой метод, который называется на любой из моих классов моделей ActiveRecord возвращается nil вместо NoMethodError. Помимо раздражающего, это разрушает динамические искатели (find_by_name, find_by_id и т. Д.), Потому что они всегда возвращают nil даже там, где существуют записи. Стандартные классы, не относящиеся к ActiveRecord :: Base, не затрагиваются.

Есть ли способ отслеживать, что перехватывает метод_missing перед ActiveRecord :: Base?

UPDATE:

После перехода на 1.8.7, я нашел (спасибо @MichaelKohl), что will_paginate плагин обработки method_missing первый. Но will_paginate существует в нашей системе (неизменной) довольно долгое время, и преступник должен быть чем-то более поздним в цепочке. Любые идеи, как увидеть, что будет дальше в этой цепочке?

ОБНОВЛЕНИЕ:

Оказалось, что существует драгоценный камень (аннотирования-2.4.0), который был обезьяны заплат ActiveRecord::Base#method_missing как пустой метод. Удаление gem решило мою проблему. Хотя ни один из ответов на самом деле не нашел проблему, ответ на @Yanhao ближе всего, как это требуется только незначительные подправить, чтобы обнаружить нарушившие псевдонимы методы

ответ

4

Я думаю, @ ответ СЦББИ является полезным, но я хотел бы, чтобы улучшить это следующим образом:

set_trace_func proc { |event, file, line, id, binding, classname| 
    printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname if id.to_s == 'method_missing' 
} 

Результат таков:

ruby-1.8.7-p334 :036 > SomeModel.some_missing_method 
    call /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1873 method_missing ActiveRecord::Base 
    line /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1874 method_missing ActiveRecord::Base 
    line /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1874 method_missing ActiveRecord::Base 
    line /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1981 method_missing ActiveRecord::Base 
    line /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing ActiveRecord::Base 
    c-call /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing Kernel 
    raise /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing ActiveRecord::Base 
c-return /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing Kernel 
    return /Users/.../.rvm/gems/ruby-1.8.7-p334/gems/activerecord-2.3.12/lib/active_record/base.rb:1998 method_missing ActiveRecord::Base 
+0

Этот ответ оказался ближе всего, за исключением того, что мне нужно было расширить поиск до 'if id.to_s = ~/method_missing/'из-за метода aliasing – PinnyM

+0

Это был Fabolous. +1. благодаря –

0

Попробуйте это в консоли Rails:

MyModel.method(:method_missing) 

Замена MyModel с реальной моделью в вашем приложении, конечно. Он должен указать, какой класс определяет метод method_missing. Если вы используете Ruby 1.9, вы можете даже сделать MyModel.method(:method_missing).source_location, чтобы получить точный файл и строку.

+0

Это возвращает '# <Метод: Класс #method_missing> '. Не совсем уверен, куда идти оттуда ... – PinnyM

+0

Не особенно полезно, не так ли. Я предполагаю, что это определяется некоторыми метапрограммами, иначе я бы подумал, что это даст вам фактическое имя класса. Можете ли вы попробовать в Ruby 1.9 и использовать 'source_location'? Это может помочь вам найти, где он определяется. – aNoble

+0

@aNoble 'method' возвращает объект метода, следовательно, метод подписи (sym) → method' (попробуйте' MyModel.method (: method_missing) .class', и вы увидите 'Method', см. Http: // ruby-doc.org/core-1.9.3/Method.html). Вы можете ссылаться на методы в экземплярах 'Method', хотя, см. Мой ответ. –

2

Вы попробовали TheModel.method(:method_missing).owner? У меня нет консоли Rails доступна, но посмотрите на этом примере:

>> class MyString < String ; end #=> nil 
>> MyString.new.method(:method_missing).owner #=> BasicObject 

Как вы можете видеть, это показывает самое близкое method_missing определения в цепочке предков.

Редактировать: извините, не принял во внимание вашу старую версию Ruby. В этом случае перейдите к предложению @ aNoble, а также посмотрите на How to find where a method is defined at runtime? в этом контексте.

+0

Спасибо за помощь! Но мы все еще не находимся - это возвращает 'NoMethodError: undefined method' owner 'для # ' – PinnyM

+0

А, извините, я забыл, что вы на 1.8.6 :-(Любая конкретная причина, по которой вы используя такую ​​старую версию Ruby? Я посмотрю, что еще я могу придумать ... –

+0

Re: old Ruby version - мы намерены перейти на REE (1.8.7), но не совсем поняли, что но :) – PinnyM

3

Вы можете попробовать это: В рельсах консоли

set_trace_func proc{ |event, file, line, id, binding, classname| 
    printf("%8s %s:%-2d %10s %8s\n", event, file, line, id, classname) if file =~ /my_app_name/ and event == 'return' #show only interesting files 
} 

MyModel.non_existing_method 

В последней строке вывода должен быть виновником.

+0

Довольно близко, за исключением того, что метод оскорбления на самом деле не был в моем приложении и не был включен в вывод - см. Ответ @ Yanhao – PinnyM

2

Используйте Ruby debugger, вставьте точку останова перед ActiveRecord -call, которая возвращает nil и начнет шаг за шагом. Я думаю, что это превосходит все другие решения, обсуждаемые здесь, потому что легко понять, что происходит на самом деле, и дает вам более полное представление о иерархии вызовов, которая вызывает вашу проблему. Кроме того, это довольно универсальный навык, который помогает решить многие другие проблемы.

Смежные вопросы