How to find out what is intercepting 'method_m

2020-06-21 05:10发布

问题:

Using Ruby 1.8.6 / Rails 2.3.2

I am noticing that any method called on any of my ActiveRecord model classes is returning nil instead of a NoMethodError. Besides annoying, this is breaking the dynamic finders (find_by_name, find_by_id, etc.) because they always return nil even where records exist. Standard classes that don't derive from ActiveRecord::Base aren't affected.

Is there a way to track down what is intercepting method_missing before ActiveRecord::Base?

UPDATE:

After switching to 1.8.7, I have found (thanks to @MichaelKohl) that the will_paginate plugin is handling method_missing first. But will_paginate has been around in our system (unaltered) for quite a while and the culprit must be something later up the chain. Any ideas how to see what comes next in this chain?

UPDATE:

It turned out that there was a gem (annotate-2.4.0) that was monkey patching ActiveRecord::Base#method_missing as a blank method. Uninstalling the gem solved my problem. Although none of the answers given actually found the problem, the answer by @Yanhao came closest as it only needed a minor tweak to discover the offending aliased method

回答1:

I think @Sebi's answer is helpful, but I'd like to improve it like this:

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'
}

The result is like this:

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


回答2:

You could try this : In rails console

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

The last line of the output should be the culprit.



回答3:

Have you tried TheModel.method(:method_missing).owner? I have no Rails console available, but look at this example:

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

As you can see this shows you the closest method_missing definition in the ancestors chain.

Edit: sorry, didn't take into account your old Ruby version. In that case, go with @aNoble's suggestion, and also look at How to find where a method is defined at runtime? in this context.



回答4:

Use the Ruby debugger, insert a breakpoint before an ActiveRecord-call that returns nil and start stepping your way through. I think this is superior to all other solutions discussed here, because it's easy to understand what's really going on and gives you a more thorough understanding of the call hierarchy that causes your problem. Apart from that, it's quite an universal skill that helps solving many other problems.



回答5:

Try this in the Rails console:

MyModel.method(:method_missing)

Replacing MyModel with an actual model in your app of course. It should tell what class defines the method_missing method. If you're using Ruby 1.9 you can even do MyModel.method(:method_missing).source_location to get the exact file and line.