Reputation: 13383
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?
Upvotes: 2
Views: 1915
Reputation: 1235
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).
Upvotes: 2
Reputation: 79562
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
Upvotes: 4
Reputation: 258188
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"
Upvotes: 5