可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.