251

I know in Ruby that I can use respond_to? to check if an object has a certain method.

But, given the class, how can I check if the instance has a certain method?

i.e, something like

Foo.new.respond_to?(:bar)

But I feel like there's gotta be a better way than instantiating a new instance.

Arslan Ali
  • 17,418
  • 8
  • 58
  • 76
user94154
  • 16,176
  • 20
  • 77
  • 116

12 Answers12

393

I don't know why everyone is suggesting you should be using instance_methods and include? when method_defined? does the job.

class Test
  def hello; end
end

Test.method_defined? :hello #=> true

NOTE

In case you are coming to Ruby from another OO language OR you think that method_defined means ONLY methods that you defined explicitly with:

def my_method
end

then read this:

In Ruby, a property (attribute) on your model is basically a method also. So method_defined? will also return true for properties, not just methods.

For example:

Given an instance of a class that has a String attribute first_name:

<instance>.first_name.class #=> String

<instance>.class.method_defined?(:first_name) #=> true

since first_name is both an attribute and a method (and a string of type String).

rmcsharry
  • 5,363
  • 6
  • 65
  • 108
horseyguy
  • 29,455
  • 20
  • 103
  • 145
  • 9
    method_defined? is the best solution because instance_methods changed between Ruby 1.8 and 1.9. 1.8 returns an array of strings and 1.9 returns an array of symbols. method_defined? takes a symbol in both 1.8 and 1.9. – Jason May 02 '12 at 17:03
  • 2
    Not to mention that :instance_methods will create a new array every call and that :include? will then have to walk the array to tell. In contrast, :method_defined? will internally query the method lookup tables (one hash lookup that is in C) and let you know without creating any new objects. – Asher Jul 12 '13 at 00:40
  • 4
    While there is at least one advantage when you use it like this, `String.instance_methods(false).include? :freeze`, the **false** argument can tell exactly whether `freeze` method is defined in String class or not. In this case it will return false because `freeze` method is inherited from Kernel module by String, but `String.method_defined? :freeze` will always return true, which can't help much with such a purpose. – ugoa Jul 23 '13 at 08:23
  • 1
    @Bane you're confusing methods on the class with methods available on the instance. `method_defined?` must be used on the class (e.g `Array`), but you are trying to use it on instances (e.g `[]`) – horseyguy Nov 09 '13 at 14:55
  • @banister Absolutely correct. Thank you for the clarification, it makes perfect sense on reading it. :) I've removed my comment so as to not confuse. – Bane Nov 09 '13 at 14:57
  • No need to horse around with anything besides `method_defined?` – Jonah Apr 26 '18 at 02:04
  • And what for private methods? Using [`method_defined?`](https://apidock.com/ruby/v1_9_3_392/Module/method_defined%3F) only public and protected methods are matched. Just use [`private_method_defined?`](https://apidock.com/ruby/v1_9_3_392/Module/private_method_defined%3F)! :) – Backo Feb 23 '19 at 11:26
46

You can use method_defined? as follows:

String.method_defined? :upcase # => true

Much easier, portable and efficient than the instance_methods.include? everyone else seems to be suggesting.

Keep in mind that you won't know if a class responds dynamically to some calls with method_missing, for example by redefining respond_to?, or since Ruby 1.9.2 by defining respond_to_missing?.

Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
  • 3
    Note that if you've got an instance in hand and you don't know it's class, you can do `instance.class.method_defined? :upcase` – jaydel Oct 20 '16 at 12:55
29

Actually this doesn't work for both Objects and Classes.

This does:

class TestClass
  def methodName
  end
end

So with the given answer, this works:

TestClass.method_defined? :methodName # => TRUE

But this does NOT work:

t = TestClass.new
t.method_defined? : methodName  # => ERROR!

So I use this for both classes and objects:

Classes:

TestClass.methods.include? 'methodName'  # => TRUE

Objects:

t = TestClass.new
t.methods.include? 'methodName'  # => TRUE
Alex V
  • 18,176
  • 5
  • 36
  • 35
10

The answer to "Given a class, see if instance has method (Ruby)" is better. Apparently Ruby has this built-in, and I somehow missed it. My answer is left for reference, regardless.

Ruby classes respond to the methods instance_methods and public_instance_methods. In Ruby 1.8, the first lists all instance method names in an array of strings, and the second restricts it to public methods. The second behavior is what you'd most likely want, since respond_to? restricts itself to public methods by default, as well.

Foo.public_instance_methods.include?('bar')

In Ruby 1.9, though, those methods return arrays of symbols.

Foo.public_instance_methods.include?(:bar)

If you're planning on doing this often, you might want to extend Module to include a shortcut method. (It may seem odd to assign this to Module instead of Class, but since that's where the instance_methods methods live, it's best to keep in line with that pattern.)

class Module
  def instance_respond_to?(method_name)
    public_instance_methods.include?(method_name)
  end
end

If you want to support both Ruby 1.8 and Ruby 1.9, that would be a convenient place to add the logic to search for both strings and symbols, as well.

Community
  • 1
  • 1
Matchu
  • 83,922
  • 18
  • 153
  • 160
5

Try Foo.instance_methods.include? :bar

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
3

If you're checking to see if an object can respond to a series of methods, you could do something like:

methods = [:valid?, :chase, :test]

def has_methods?(something, methods)
  methods & something.methods == methods
end

the methods & something.methods will join the two arrays on their common/matching elements. something.methods includes all of the methods you're checking for, it'll equal methods. For example:

[1,2] & [1,2,3,4,5]
==> [1,2]

so

[1,2] & [1,2,3,4,5] == [1,2]
==> true

In this situation, you'd want to use symbols, because when you call .methods, it returns an array of symbols and if you used ["my", "methods"], it'd return false.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
TalkativeTree
  • 609
  • 5
  • 10
3

I think there is something wrong with method_defined? in Rails. It may be inconsistent or something, so if you use Rails, it's better to use something from attribute_method?(attribute).

"testing for method_defined? on ActiveRecord classes doesn't work until an instantiation" is a question about the inconsistency.

Community
  • 1
  • 1
NoDisplayName
  • 15,246
  • 12
  • 62
  • 98
3
class Foo
 def self.fclass_method
 end
 def finstance_method
 end
end

foo_obj = Foo.new
foo_obj.class.methods(false)
=> [:fclass_method] 

foo_obj.class.instance_methods(false)
=> [:fclass_method] 

Hope this helps you!

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Rajeev Sharma
  • 1,527
  • 2
  • 13
  • 16
3

Not sure if this is the best way, but you could always do this:

Foo.instance_methods.include? 'bar'
dbyrne
  • 59,111
  • 13
  • 86
  • 103
2

klass.instance_methods.include :method_name or "method_name", depending on the Ruby version I think.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
yxhuvud
  • 108
  • 7
2

While respond_to? will return true only for public methods, checking for "method definition" on a class may also pertain to private methods.

On Ruby v2.0+ checking both public and private sets can be achieved with

Foo.private_instance_methods.include?(:bar) || Foo.instance_methods.include?(:bar)
Epigene
  • 3,634
  • 1
  • 26
  • 31
1

On my case working with ruby 2.5.3 the following sentences have worked perfectly :

value = "hello world"

value.methods.include? :upcase

It will return a boolean value true or false.

Nick
  • 138,499
  • 22
  • 57
  • 95
Manuel Lazo
  • 745
  • 7
  • 7