ActiveRecord::Base uses method_missing to handle dynamic finder methods, but does not alter respond_to? to include the handled methods. So for example:
Topic.find_by_title # => although this works
Topic.respond_to?(:find_by_title) # => this returns false
It's my understanding that if you implement method_missing on a class to handle extra methods, you should add a corresponding implementation of respond_to? which recognizes the methods that method_missing will handle. Dan Manges has a nice explanation on his blog (http://www.dcmanges.com/blog/30). If this were fixed, the above example becomes:
Topic.find_by_title # => this still works
Topic.respond_to?(:find_by_title) # => this returns true
I have a practical reason for wanting this fixed. I've just added functionality to Mocha (http://mocha.rubyforge.org) which allows you to get warnings or errors when stubbing non-existent methods. The problem is that when running in a Rails project, lots of false negatives are produced when you are legitimately stubbing dynamic finder methods. Quite a few people have asked for this functionality and I think it could be really useful in catching incorrectly stubbed methods.
The fact that ActiveRecord::Base.method_missing now explicitly defines the method on first invocation somewhat reduces this problem, but it still means that ActiveRecord::Base.respond_to? will return false, until the method has been invoked at least once. Furthermore, when stubbing a dynamic finder in a test, it's likely that the real method will not be invoked - so the problem will still exist.
The attached patch against trunk (revision 9233) fixes this problem. It includes tests which fail against the current implementation and a couple of tests that check that normal behaviour is preserved. I've only run the tests in the "test_mysql" rake task in activerecord against MySQL (version 5.0.27) using Ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-darwin8.10.3].
Note: I had to modify a few tests in FinderTest which were inappropriately using respond_to? to determine whether the method had already been cached (i.e. explicitly defined). My patch uses something like this:
Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
instead of:
Company.respond_to?(:find_or_initialize_by_name)
The conversion of the method to a String and comparison with a String is so that it works in Ruby 1.8 or 1.9. In Ruby 1.8, Object#public_methods returns an Array of Strings, whereas in Ruby 1.9 it returns an Array of Symbols.