Topic: How to locate a "rogue" method?

I'm having problems with an ActiveRecord table field called "timeout". It appears that ActiveRecord classes already contain a private method called "timeout" that is not an attribute reader. Usually ActiveRecord redefines the "timeout" method when it creates the reader methods and then all is fine, but it doesn't always do this and in those cases the magic via the missed_method exception doesn't work and I get a "wrong number of arguments (0 for 1)" error. Ughhh.
My question: is there a way to figure out where the "timeout" method comes from? I don't see any way to query the method for its source file/line, for example. Any other tricks?

Re: How to locate a "rogue" method?

Hmm, I just looked through the on-line docs and there is no mention of a timeout method.  I grep'd over all the source files in ActiveRecord (including all the database adaptors) and there is no method "timeout" -- there is an instance variable @timeout_p declared in  the sybase adaptor and a class variable @@verification_timeout in connection+adapters/abstract/connection_specifications.rb.  Neither of those should conflict, though.

However in the console a "puts a.private_methods.sort" on any instance of a model class does reveal a timeout method.  Ahh, even

a = Object.new
a.private_methods.include? "timeout"

returns true.  So timeout is apparently a method in the superclass of all Ruby objects.  Looking deeper it seems like part of rails ActiveSupport adds the ruby-standard "timeout" module to many of the standard ruby, such as object.  So that's where the method is coming from in the first place.

Now as to how to deal with this in your code, I'm not sure.  You say it works sometimes, but not all the time?  Can you isolate why it fails, when it fails?

My RoR journey  -- thoughts on learning RoR and lessons learned in applying TDD and agile practices.

Re: How to locate a "rogue" method?

Congrats! Sounds like you found another reserved word. You can add it to this to the growing list of reserved words.

Yeah, reserved words are annoying and I really feel for you. The easiest solution is to rename the field. This might help.

Edit: On second thought, you can try something like this. No clue if it will work.

class MyModel < ActiveRecord::Base
  alias get_timeout timeout
 
  def timeout(sec = nil)
    if sec.nil?
      get_timeout
    else
      super
    end
  end
end

Hope that's right.

Last edited by ryanb (2006-08-06 12:25:23)

Railscasts - Free Ruby on Rails Screencasts

Re: How to locate a "rogue" method?

Thanks for the responses. I eventually also figured out that ruby/lib/ruby/1.8/timeout.rb defines a global function called timeout and that's what gets called.

The reason this case was so difficult to track down is that if you have a table with two fields, say :name and :timeout and you have generate_read_methods == true, then the read accessor methods actually only get generated by the first call to :missing_method. So if you first access the :name attribute, :missed_method gets triggered and it defines methods for name(), name=(val), timeout(), and timeout=(val). So if you now access the :timeout attribute it's all fine. But if the first attribute accessed is :timeout, then you end up with a "wrong arg count" error when ruby tries to invoke the global timeout function instead of triggering a :missing_method exception.

I see two solutions to this:
1_ Define the attribute accessor methods earlier, e.g. when the object gets instantiated. I don't know any of that code, so I really can't tell what the difficulties would be.
2_ When the first object for a table gets instantiated, check whether any of them clash with existing methods, e.g. using the respond_to?() method and print a warning if there is a clash.

All this still leaves me with a question, which is how to debug such a situation. Even after setting a breakpoint in the debugger at a point where the object in question is in scope, how can I figure out what gets called for timeout()? There oesn't seem to be any way to ask for the file:line of the method (something like o.method(timeout).file_line).

Thanks for all the replies!