Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #11538 (closed defect: fixed)

Opened 8 months ago

Last modified 8 months ago

[PATCH] Models derived from ActiveRecord::Base should "respond_to?" to dynamic finder methods

Reported by: floehopper Assigned to: core
Priority: normal Milestone: 2.x
Component: ActiveRecord Version: edge
Severity: normal Keywords: activerecord method_missing respond_to stub mock verified
Cc:

Description

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.

Attachments

activerecord_base_should_respond_to_dynamic_finder_methods.diff (9.1 kB) - added by floehopper on 04/06/08 16:46:42.
ActiveRecord::Base should "respond_to?" dynamic finder methods

Change History

04/06/08 16:46:42 changed by floehopper

  • attachment activerecord_base_should_respond_to_dynamic_finder_methods.diff added.

ActiveRecord::Base should "respond_to?" dynamic finder methods

04/06/08 18:15:56 changed by jaycfields

+1

04/06/08 18:48:42 changed by threedaymonk

+1

04/06/08 21:38:43 changed by h-lame

  • keywords changed from activerecord method_missing respond_to stub mock to activerecord method_missing respond_to stub mock verified.

+1 seems like a nice idea. Checked with sqlite3 and mysql.

04/06/08 22:25:18 changed by lifofifo

Committed after a few minor tweaks.

Thanks!

04/06/08 22:26:19 changed by pratik

  • status changed from new to closed.
  • resolution set to fixed.

(In [9235]) Ensure that respond_to? considers dynamic finder methods. Closes #11538. [floehopper]