ymv
ymv

Reputation: 2173

Make ruby object respond to arbitrary messages?

Is there equivalent of python __getattr__ in ruby (for finding methods at least)?

class X(object):
    def __getattr__(self, name):
        return lambda x: print("Calling " + name + ": " + x)

x = X()
x.some_method("some args")

So it could be something like:

class X
    # .. ??? ..
    def default_action(method_name, x)
        puts "Calling {method_name}: {x}"
    end
end

x = X.new()
x.some_method("some args")

Upvotes: 3

Views: 1074

Answers (4)

Jörg W Mittag
Jörg W Mittag

Reputation: 369428

Yes. If an object does not respond to a message, Ruby will send a method_missing message with the message selector and the arguments to the receiver:

class X
  def method_missing(selector, *args, &blk)
    puts "The message was #{selector.inspect}."
    puts "The arguments were #{args.map(&:inspect).join(', ')}."
    puts "And there was #{blk ? 'a' : 'no'} block."
    super
  end
end

x = X.new
x.some_method('some args', :some_other_args, 42)
# The message was :some_method.
# The arguments were "some args", :some_other_args, 42.
# And there was no block.
# NoMethodError: undefined method `some_method'

x.some_other_method do end
# The message was :some_other_method.
# The arguments were .
# And there was a block.
# NoMethodError: undefined method `some_other_method'

Note that if you define method_missing, you should also define respond_to_missing? accordingly. Otherwise you get strange behavior like this:

x.respond_to?(:foo) # => false
x.foo               # Works. Huh?

In this particular case, we handle all messages, therefore we can simply define it as follows:

class X; def respond_to_missing?(*) true end end

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

Upvotes: 8

steenslag
steenslag

Reputation: 80065

class X
  def method_missing(sym,*args)
    puts "Method #{sym} called with #{args}"
  end
end
a = X.new
a.blah("hello","world")

#=> Method blah called with ["hello", "world"]

Upvotes: 5

Neno Ganchev
Neno Ganchev

Reputation: 746

IIRC, you can define method_missing in ruby classes to handle this. Sorry that I can't provide specifics.

Upvotes: 2

RameshVel
RameshVel

Reputation: 65867

class Test
  def say
     puts "hi"
  end
end

and you can invoke say method by

obj = Test.new
obj.send "say"

and checking the method availability using

obj.respond_to? "say"

finally, put together all

if (obj.respond_to? "say")
  obj.send "say"
end

Upvotes: -2

Related Questions