Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are respond_to and respond_to_missing different?

I'm confused when to use each of this methods. From respond_to? documentation:

Returns true if obj responds to the given method. Private methods are included in the search only if the optional second parameter evaluates to true.

If the method is not implemented, as Process.fork on Windows, File.lchmod on GNU/Linux, etc., false is returned.

If the method is not defined, respond_to_missing? method is called and the result is returned.

And respond_to_missing?:

Hook method to return whether the obj can respond to id method or not.

See #respond_to?.

Both methods takes 2 arguments.
Both methods seems to the same thing(check if some object respond to given method) so why we should use(have) both?

Defining 'resond_to_missing?` gives you ability to take methods:

class A
  def method_missing name, *args, &block
    if name == :meth1
      puts 'YES!'
    else
      raise NoMethodError
    end
  end

  def respond_to_missing? name, flag = true
    if name == :meth1
      true
    else
      false
    end
  end
end

[65] pry(main)> A.new.method :meth1
# => #<Method: A#meth1>

Why respond_to? couldn't do this?

What I guess:

respond_to? checks if method is in:

  1. Current object.
  2. Parent object.
  3. Included modules.

respond_to_missing? checks if method is:

  1. Defined via method_missing:

Via array of possible methods:

def method_missing name, *args, &block
  arr = [:a, :b, :c]
  if arr.include? name
    puts name
  else
    raise NoMethodError
  end
end

Delegating it to different object:

class A
  def initialize name
    @str = String name
  end

  def method_missing name, *args, &block
    @str.send name, *args, &block
  end
end

2 . Other way that I'm not aware of.

Where should both be defined/used(my guessing too):

Starting from 1.9.3(as fair I remember) define only respond_to_missing? but use only respond_to?

Last questions:

Am I right? Did I missed something? Correct everything that is bad and/or answer questions asked in this question.

like image 645
Darek Nędza Avatar asked Oct 02 '22 01:10

Darek Nędza


1 Answers

respond_to_missing? is supposed to be updated when you make available additional methods using the method missing technique. This will cause the Ruby interpreter to better understand the existence of the new method.

In fact, without using respond_to_missing?, you can't get the method using method.

Marc-André posted a great article about the respond_to_missing?.

In order for respond_to? to return true, one can specialize it, as follows:

class StereoPlayer
  # def method_missing ...
  #   ...
  # end

  def respond_to?(method, *)
    method.to_s =~ /play_(\w+)/ || super
  end
end
p.respond_to? :play_some_Beethoven # => true

This is better, but it still doesn’t make play_some_Beethoven behave exactly like a method. Indeed:

p.method :play_some_Beethoven
# => NameError: undefined method `play_some_Beethoven'
#               for class `StereoPlayer'

Ruby 1.9.2 introduces respond_to_missing? that provides for a clean solution to the problem. Instead of specializing respond_to? one specializes respond_to_missing?. Here’s a full example:

class StereoPlayer
  # def method_missing ...
  #   ...
  # end

  def respond_to_missing?(method, *)
    method =~ /play_(\w+)/ || super
  end
end

p = StereoPlayer.new
p.play_some_Beethoven # => "Here's some_Beethoven"
p.respond_to? :play_some_Beethoven # => true
m = p.method(:play_some_Beethoven) # => #<Method: StereoPlayer#play_some_Beethoven>
# m acts like any other method:
m.call # => "Here's some_Beethoven"
m == p.method(:play_some_Beethoven) # => true
m.name # => :play_some_Beethoven
StereoPlayer.send :define_method, :ludwig, m
p.ludwig # => "Here's some_Beethoven"

See also Always Define respond_to_missing? When Overriding method_missing.

like image 110
Simone Carletti Avatar answered Oct 13 '22 09:10

Simone Carletti