Ruby send vs __send__

RubySyntax

Ruby Problem Overview


I understand the concept of some_instance.send but I'm trying to figure out why you can call this both ways. The Ruby Koans imply that there is some reason beyond providing lots of different ways to do the same thing. Here are the two examples of usage:

class Foo
  def bar?
    true
  end
end

foo = Foo.new
foo.send(:bar?)
foo.__send__(:bar?)

Anyone have any idea about this?

Ruby Solutions


Solution 1 - Ruby

Some classes (for example the standard library's socket class) define their own send method which has nothing to do with Object#send. So if you want to work with objects of any class, you need to use __send__ to be on the safe side.

Now that leaves the question, why there is send and not just __send__. If there were only __send__ the name send could be used by other classes without any confusion. The reason for that is that send existed first and only later it was realized that the name send might also usefully be used in other contexts, so __send__ was added (that's the same thing that happened with id and object_id by the way).

Solution 2 - Ruby

If you really need send to behave like it would normally do, you should use __send__, because it won't (it shouldn't) be overriden. Using __send__ is especially useful in metaprogramming, when you don't know what methods the class being manipulated defines. It could have overriden send.

Watch:

class Foo
  def bar?
    true
  end

  def send(*args)
    false
  end
end

foo = Foo.new
foo.send(:bar?)
# => false
foo.__send__(:bar?)
# => true

If you override __send__, Ruby will emit a warning:

> warning: redefining `_send_' may > cause serious problems

Some cases where it would be useful to override send would be where that name is appropriate, like message passing, socket classes, etc.

Solution 3 - Ruby

__send__ exists so it can't be over-written by accident.

As for why send exists: I can't speak for anyone else, but object.send(:method_name, *parameters) looks nicer than object.__send__(:method_name, *parameters), so I use send unless I need to use __send__.

Solution 4 - Ruby

Apart from what others already told you, and what boils down to saying that send and __send__ are two aliases of the same method, you might be interested in the third, somwhat different possibility, which is public_send. Example:

A, B, C = Module.new, Module.new, Module.new
B.include A #=> error -- private method
B.send :include, A #=> bypasses the method's privacy
C.public_send :include, A #=> does not bypass privacy

Update: Since Ruby 2.1, Module#include and Module#extend methods become public, so the above example would not work anymore.

Solution 5 - Ruby

The main difference between send, __send__, and public_send is as follow.

  1. send and __send__ are technaically same as used to call Object's method, but the main difference is you can override the send method without any warning and when you override __send__ then there is a warning message

> warning: redefining __send__ may cause serious problems

This is because to avoid conflicts, specially in gems or libraries when the context where it will be used is unknown, always use __send__ instead of send.

  1. The difference between send (or __send__) and public_send is that send / __send__ can call an object’s private methods, and public_send can’t.
class Foo
   def __send__(*args, &block)
       "__send__"
   end
   def send(*args)
     "send"
   end
   def bar
       "bar"
   end
   private
   def private_bar
     "private_bar"
   end
end

Foo.new.bar #=> "bar"
Foo.new.private_bar #=> NoMethodError(private method 'private_bar' called for #Foo)

Foo.new.send(:bar) #=> "send"
Foo.new.__send__(:bar) #=> "__send__"
Foo.new.public_send(:bar) #=> "bar"

Foo.new.send(:private_bar) #=> "send"
Foo.new.__send__(:private_bar) #=> "__send__"
Foo.new.public_send(:private_bar) #=> NoMethodError(private method 'private_bar' called for #Foo)

At the end try to use public_send to avoid direct call to private method instead of using send or send.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjaydelView Question on Stackoverflow
Solution 1 - Rubysepp2kView Answer on Stackoverflow
Solution 2 - RubyThiago SilveiraView Answer on Stackoverflow
Solution 3 - RubyAndrew GrimmView Answer on Stackoverflow
Solution 4 - RubyBoris StitnickyView Answer on Stackoverflow
Solution 5 - RubyKishor VyavahareView Answer on Stackoverflow