Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'respond_to?' versus 'defined?'

If I want to check whether a method with a given name is defined, which is better to use, respond_to?, or defined??

From the point of view of efficiency, there can be an argument for using defined? because defined? is a built in keyword, whereas respond_to? is a method, and hence the former might be faster. But on the other hand, under the situation that the expression to be checked is known to be a simple method, defined? needs to parse the whole expression, and that may be a drawback as compared to using respond_to?, which just needs to accept the argument as a method name.

Which is better? Are there points other than efficiency that should be considered?

like image 855
sawa Avatar asked May 22 '17 07:05

sawa


People also ask

What is Respond_to do format?

respond_to is a method on the superclass ActionController . it takes a block, which is like a delegate. The block is from do until end , with |format| as an argument to the block. respond_to executes your block, passing a Responder into the format argument.

What is Respond_to in Ruby?

respond_to is a Rails method for responding to particular request types.

What is send method in Ruby?

Ruby Language Metaprogramming send() method send() is used to pass message to object . send() is an instance method of the Object class. The first argument in send() is the message that you're sending to the object - that is, the name of a method. It could be string or symbol but symbols are preferred.


2 Answers

My short answer is I would say either. However, Module#method_defined? perhaps does not fit in most cases. It depends on exactly what you want to know. Here is a long answer, explaining the purposes.

I guess the most popular purpose is for Duck-typing, namely to check whether an object responds to the method with a certain name, and if it does, then you don't care what class of the object it actually is or where/how the method is implemented internally. In this case, Object#respond_to? is the exact method that is (I think) designed for it (as the method name suggests). It is true (as @Jörg W Mittag pointed out) that the result could be altered if Object#respond_to_missing? is redefined in its class. However, that is the whole purpose of respond_to_missing? — to register methods as valid — see, for example, the (old) blog post "Method_missing, Politely" by a Ruby core developer Marc-André for clarification.

Another and the traditional way (since Ruby 1) is to use Ruby built-in defined?. This past answer to "In Ruby, how do I check if method “foo=()” is defined?" explains a potentially useful case specific with defined?, namely telling whether it is assignment. For the purpose of checking whether a method exists, defined? works similar to Object#respond_to? in most (or all?) cases, except it returns a String object or nil as opposed to Boolean. I'd say it is up to personal preference; however, I am in favour of Object#respond_to? in genearal as it is clearly more specific and hence is readable, and a potential trouble of actual typing, like forgetting a pair of parentheses for defined?, is more unlikely.

Alternatively, to check whether the class of the object has the method explicity defined with its name in one of itself, its superclasses and included modules, use Module#method_defined?. Specifically, this method ignores respond_to_missing?, which means in practice any meta programming with BasicObject#method_missing (which ActiveRecord of Rails uses extensively, for example) is disregarded.

Also, as of Ruby 2.6.5, this Module#method_defined? ignores refinements, that is, the module methods introduced by using are regarded not defined, in contrast of Object#respond_to? and built-in defined?, both of which regards those refine-d methods as defined.

A more primitive(?) way is to use Object#methods like obj.methods.include?(:bar) to check public and protected methods. An advantage of this way is, you can exclude the methods in the modules included in the class of the object, by specifying the argument true, like obj.methods(false).include?(:bar). Also, you can make a finer distinguishment with Object#private_methods, Object#protected_methods, Object#public_method instead, if need be. They all take into account Object#respond_to_missing?.

One more comment re private methods, which include built-in functions (aka Kernel methods). Object#respond_to? does not respond to a private method in default, unless the second argument is given true (see the past question "How to check if private method is defined in ruby"). For that purpose, use either self.respond_to?(:bar, true) or Module#private_method_defined? like self.class.private_method_defined?(:bar), depending on exactly what you want to check.

To summarise, to check the method called bar(), do

  • obj.respond_to?(:bar) (Object#respond_to?) for duck-typing check
  • defined?(obj.bar) (Built-in `defined?) for general purposes
  • obj.class.method_defined?(:bar) (Module#method_defined?) for literal definition check, excluding refinements
  • obj.methods(true).include?(:bar) (Object#methods) to get the method list and evaluate (where you can exclude the included modules by specifying false)
like image 96
Masa Sakano Avatar answered Oct 07 '22 00:10

Masa Sakano


If I want to check whether a method with a given name is defined, which is better to use, respond_to?, or defined??

Neither. Use Module#method_defined?

It's not really a question which is "better" to use: neither the Object#respond_to? method nor the defined? unary prefix operator (despite the name!) checks whether the method is defined: they both check whether the receiver responds to a message, which is a completely different thing.

Only Module#method_defined? will actually check whether the method is defined:

class Foo
  def method_missing(*) end
  def respond_to_missing?(*) true end
end

foo = Foo.new

defined? foo.bar
#=> 'method'

foo.respond_to?(:bar)
#=> true

Foo.method_defined?(:bar)
#=> false
like image 37
Jörg W Mittag Avatar answered Oct 07 '22 02:10

Jörg W Mittag