tags:

views:

74

answers:

3

Is it possible to send nothing to an object?

Let me elaborate. For instance I could have something like this:

val = some_stack.include?(some_val) ? some_val : nil

obj1.obj2.send(val).obj3.obj4

The above call wont't work because nil is not a symbol. So the solution is:

if val.nil?
  obj1.obj2.obj3.obj4
else
  obj1.obj2.send(val).obj3.obj4
end

However I'm not too fond of this. Is there any other way?

+4  A: 

There is not builtin way. You could do the following:

module Kernel
  def __self__
    self
  end
end

obj1.obj2.send(val || :__self__).obj3.obj4

even more cryptic:

[:obj1, :obj2, val, :obj3, :obj4].compact.inject(self, :send)
# (assuming that obj1 is a method call like obj2, ...)

but I'd recommend keeping it simple:

intermediate = obj1.obj2
intermediate = intermediate.some_val if some_stack.include?(some_val)
intermediate.obj3.obj4
Marc-André Lafortune
+3  A: 

I'm not aware of any method that returns self without taking any arguments (but I might be overlooking something), but there's no reason you couldn't monkeypatch one into the class you're using:

irb(main)> class Object
irb(main)>   def ignore
irb(main)>     self
irb(main)>   end
irb(main)> end
=> nil
irb(main)> msg = nil
=> nil
irb(main)> 'hi'.send(msg || :ignore).upcase
=> "HI"
irb(main)> msg = :reverse
=> :reverse
irb(main)> 'hi'.send(msg || :ignore).upcase
=> "IH"
Mark Rushakoff
A: 

I might be misunderstanding something, but I think this question is attempting to do something that doesn't make sense.

In Ruby, sending a message to an object means asking that object to respond in some way to the requested message (which is specified in this case with a symbol). A message is what we usually refer to as a method call. In this example "send" is a message sent to the object, and the object that receives the message "send" takes the passed arguments (a symbol for another method) and sends itself (the object) a message corresponding to the method for the passed symbol.

So sending an object nil is almost equivalent to not sending the object anything— except that you have actually sent the object a message, only one that doesn't have any content. So the object is inevitably confused as to what it's supposed to do, since it was told to do nothing. Wouldn't you be confused too if someone demanded you do nothing?:)

So to turn to your specific question:

To rephrase your question (to be clear whether I understand it), I think you are asking: is it possible to chain a series of calls wherein one of the calls in the chain only occurs if a variable (the method to call by way of sending a message) is non-nil?

Perhaps this is better for a general purpose solution?

obj2 = obj1.obj2
obj2 = obj2.send( val ) if val
obj2.obj3.obj4

Otherwise, to actually answer your question (which now does make sense, but might make things more convoluted), you can put this in your class:

def send( method, *args )
    super if method
end

Alternatively you can do this:

class Object
    def send( method, *args )
        super if method
    end
end

Which will cause every object to accept nil (and quietly do nothing) for send.

This solution will provoke a warning [sic]:

warning: redefining `send' may cause serious problem

You can suppress this warning by redirecting $stderr, but it's not threadsafe (says Matz, from a brief Google search).

Asher