Forum: Ruby respond_to_missing?

Posted by Thomas Sawyer (7rans)
on 2013-01-04 03:15
(Received via mailing list)
Can anyone explain `#respond_to_missing?`.
Posted by Jan E. (jacques1)
on 2013-01-04 03:46
Hi,

"respond_to_missing?" makes "respond_to?" fit for dynamic methods 
defined by "method_missing".

Normally, an object wouldn't know if it can respond to a dynamic method 
call:


#-----------------------
class A
  def method_missing name, *args
    if name == :say_hi
      puts 'Hi there!'
    else
      raise NoMethodError
    end
  end
end

a = A.new
a.say_hi                    # "Hi there!"
puts a.respond_to? :say_hi  # => false
#-----------------------


But if you define "respond_to_missing?", you can work around this 
problem:


#-----------------------
class A
  def method_missing name, *args
    if name == :say_hi
      puts 'Hi there!'
    else
      raise NoMethodError
    end
  end
  def respond_to_missing? name, include_private
    name == :say_hi
  end
end

a = A.new
a.say_hi                    # "Hi there!"
puts a.respond_to? :say_hi  # => true
#-----------------------
Posted by "Sandor Szücs" <sandor.szuecs@fu-berlin.de> (Guest)
on 2013-01-05 11:47
(Received via mailing list)
On 1/4/13 3:14 AM, Intransition wrote:
> Can anyone explain `#respond_to_missing?`.
>

The answer can be found at [1].

irb:0> class Bar
irb:1>     def method_missing name, *args
irb:2>       p args
irb:2>     end
irb:1>
irb:1*     def respond_to? name, include_private = false
irb:2>       true
irb:2>     end
irb:1> end
=> nil
irb:0> Bar.new.respond_to? :does_not_exist
=> true
irb:0> Bar.new.method :does_not_exist
NameError: undefined method `does_not_exist' for class `Bar'
        from (irb):112:in `method'
        from (irb):112
        from /Users/sz/.rvm/rubies/ruby-1.9.3-p362/bin/irb:16:in 
`<main>'


irb:0> class Foo
irb:1>     def respond_to? *args; super; end
irb:1>
irb:1*     def respond_to_missing? *args
irb:2>       true
irb:2>     end
irb:1> end
=> nil
irb:0>  Foo.new.respond_to? :does_not_exist
=> true
irb:0>  Foo.new.method :does_not_exist
=> #<Method: Foo#does_not_exist>


[1]
http://stackoverflow.com/questions/13793060/respon...
Posted by Thomas Sawyer (7rans)
on 2013-01-05 12:43
(Received via mailing list)
Thank you Jan and Sandor. That is basically what I thought, but I had
recently become confused b/c of this:

  https://bugs.ruby-lang.org/issues/5759

If you read toward the bottom of this thread, there is a case that seems
contradictory. I still don't get it.
Posted by Jan E. (jacques1)
on 2013-01-05 21:59
How is that contradictory? Are you sure you got the return values of 
respond_to_missing? right? Because I guess you want to return *true* for 
:to_ary?

respond_to? first checks if the method is actually, physically there. If 
that's not the case, it checks for respond_to_missing?

In your case both checks return "false", so you end up with "false" just 
like you should.
Posted by Thomas Sawyer (7rans)
on 2013-01-06 10:32
(Received via mailing list)
On Saturday, January 5, 2013 4:00:02 PM UTC-5, Jan E. wrote:
>
>
Yes, that is what it should do. But notice #flatten doesn't seem to 
notice
that it returns false. It still calls #to_ary on it,

    class Baz
      def method_missing(s); s; end

      def respond_to_missing?(s, x)
        return false if s == :to_ary
        true
      end
    end


    [Baz.new].flatten
    => in `flatten': can't convert Baz to Array (Baz#to_ary gives 
Symbol) (TypeError)


Even though it appears to work fine if one overrides #respond_to? 
directly.

    class Baz

      def method_missing(s); s; end

      def respond_to?(s, x)
        super unless s == :to_ary
      end
    end

    [Baz.new].flatten => [#<Baz:0x007f8d3115c7d0>]
Posted by "Sandor Szücs" <sandor.szuecs@fu-berlin.de> (Guest)
on 2013-01-06 12:41
(Received via mailing list)
On 1/5/13 12:42 PM, Intransition wrote:
> Thank you Jan and Sandor. That is basically what I thought, but I had
> recently become confused b/c of this:
>
>   https://bugs.ruby-lang.org/issues/5759
>
> If you read toward the bottom of this thread, there is a case that seems
> contradictory. I still don't get it.
>

The difference of respond_to? and respond_to_missing? is that you can
get a Method object using Kernel#method(sym) if a method will be
dynamically implemented using method_missing.

Here again the example:
 13 class Bar1
 14     def respond_to_missing? *args
 15       true
 16     end
 17     def method_missing(*args, &blk)
 18       p args
 19     end
 20 end
 21
 22 class Baz1
 23     def respond_to? *args
 24       true
 25     end
 26
 27     def method_missing(*args, &blk)
 28       p args
 29     end
 30 end

# get the method:
irb:0> Bar1.new.method :foo
=> #<Method: Bar1#foo>

# do not get the method:
irb:0> Baz1.new.method :foo
NameError: undefined method `foo' for class `Baz1'
        from (irb):17:in `method'
        from (irb):17
        from /.../.rvm/rubies/ruby-1.9.3-p362/bin/irb:16:in `<main>'


In your example you define: "#to_ary() is not created dynamically nor
defined static"

class TransBaz1
  def method_missing(s)
    s
  end

  def respond_to_missing?(s, x)
    return false if s == :to_ary
    true
  end
end


irb:0> TransBaz1.new.method :to_ary
NameError: undefined method `to_ary' for class `TransBaz1'
        from (irb):45:in `method'
        from (irb):45
        from /.../.rvm/rubies/ruby-1.9.3-p362/bin/irb:16:in `<main>'
irb:0> TransBaz1.new.method :foo
=> #<Method: TransBaz1#foo>

You can not get Method object :to_ary using Kernel#method, but you can
get :foo.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.