5

I have a Ruby object (an ActiveRecord object, specifically) named User. It responds to methods like find_by_id, find_by_auth_token, etc. However, these aren't methods that are defined via def or define_method. Instead, they are dynamic methods that are handled via method_missing.

I'd like to obtain a reference to one of these methods via Object#method, e.g.:

User.method(:find_by_auth_token)

It doesn't look like this works though. The best solution I've come up with is:

proc { |token| User.find_by_auth_token(token) }

Is there any other way around using such a wrapper method as this? Am I really unable to use Object#method for dynamic methods?

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Matt Huggins
  • 81,398
  • 36
  • 149
  • 218
  • 2
    For anyone who stumbles on this in the future, these dynamic finder methods will be deprecated in Rails 4.0. I'd personally be cautious of adding additional functionality on top of them. https://github.com/rails/activerecord-deprecated_finders – Peter Brown Sep 22 '12 at 16:35

1 Answers1

5

The simplest answer is "no"—the only way to guarantee in general that Object#method(:foo) will return an instance of Method is by defining a method named foo on the object.

The more complected answer is that you can coerce Object#method(:foo) into returning an instance of Method by overriding Object#respond_to_missing? s.t. it returns true when given :foo. For example:

class User
  def respond_to_missing?(method_name, include_private = false)
    method_name.to_s.start_with?('find_by_')
  end
end

m = User.new.method(:find_by_hackish_means)
# => #<Method: User#find_by_hackish_means>

(It's up to you to ensure that the method is actually defined):

m.call
# => NoMethodError: undefined method `find_by_hackish_means' for #<User:0x123>
pje
  • 21,801
  • 10
  • 54
  • 70
  • 2
    Here's a more in-depth blog post on the subject: http://robots.thoughtbot.com/post/28335346416/always-define-respond-to-missing-when-overriding – pje Sep 22 '12 at 15:01
  • 1
    +1. So it's really Rails' fault it doesn't work. I've started to fix it in that regard, e.g. https://github.com/rails/rails/pull/6169 – Marc-André Lafortune Sep 22 '12 at 16:46
  • Alright, that makes sense. And @Marc-AndréLafortune, you answered my question that I immediately had in response to this answer. :) Thanks to you both! – Matt Huggins Sep 22 '12 at 16:53