5

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

PinnyM
  • 35,165
  • 3
  • 73
  • 81

5 Answers5

4

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
Yanhao
  • 5,264
  • 1
  • 22
  • 15
3

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.

Sebi
  • 669
  • 5
  • 6
  • Pretty close, except the offending method wasn't actually in my application and wasn't included in the output - see @Yanhao's answer – PinnyM Feb 27 '12 at 18:46
2

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.

Community
  • 1
  • 1
Michael Kohl
  • 66,324
  • 14
  • 138
  • 158
  • Thanks for your help! But we're still not there yet - that returns `NoMethodError: undefined method `owner' for #` – PinnyM Feb 23 '12 at 17:24
  • Ah, sorry, I forgot that you are on 1.8.6 :-( Any specific reason you are using such an old Ruby version? I'll see what else I can come up with... – Michael Kohl Feb 23 '12 at 17:27
  • Re: old Ruby version - we intend to move up to REE (1.8.7), but haven't quite gotten to that yet :) – PinnyM Feb 23 '12 at 17:29
  • Ok, tried it on 1.8.7 and got a better result: `WillPaginate::Finder::ClassMethods`. But I'm pretty confident that will_paginate isn't the culprit and that something else is taking it after will_paginate passes it up the chain. How do I find that? – PinnyM Feb 23 '12 at 17:38
  • 1
    You could work your way up through the ancestors chain with something like `TheModel.ancestors.select { |a| a.instance_methods.include? :method_missing }` and check all the `method_missing`s that pop up. It sounds like one of them checks for something with an `if`, but doesn't call `super` in the `else` or has no `else` at all. – Michael Kohl Feb 23 '12 at 18:05
  • The `select` you suggested returns an empty array. I'm not sure if `instance_methods` will help since it's actually being declared on the class level as in will_paginate, and in all likelihood it's an alias_method_chain or something of that nature – PinnyM Feb 23 '12 at 18:38
  • As I said, no Rails available, so I just try with a plain Ruby class. Sorry, guess that's about as much as I can contribute. – Michael Kohl Feb 23 '12 at 18:41
2

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.

jupp0r
  • 4,502
  • 1
  • 27
  • 34
0

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.

aNoble
  • 7,033
  • 2
  • 37
  • 33
  • That returns `#`. Not quite sure where to go from there... – PinnyM Feb 20 '12 at 17:29
  • Not especially helpful, is it. I'm guessing it's being defined by some meta programming, otherwise I'd think it would give you the actual class name. Are you able to try it in Ruby 1.9 and use `source_location`? That might help you find where it's being defined. – aNoble Feb 20 '12 at 22:55
  • @aNoble `method` returns a method object, hence the signature `method(sym) → method` (try `MyModel.method(:method_missing).class` and you'll see `Method`, see: http://ruby-doc.org/core-1.9.3/Method.html). You can invoke methods on `Method` instances though, see my answer. – Michael Kohl Feb 23 '12 at 17:16
  • 1.9.3 still gives some helpful information about the class the method is in. Like `#` but maybe 1.8.6 doesn't. The `Method` class isn't nearly as useful pre 1.9. – aNoble Feb 23 '12 at 19:13
  • Unfortunately, our application won't start up in console using 1.9 – PinnyM Feb 23 '12 at 19:24
  • 1
    @aNoble True enough, I just wanted to say that if you are on 1.9 already, you can call `owner` on the method object to get the information in a better way. – Michael Kohl Feb 24 '12 at 08:03