December 28, 2006

The Ruby send hack: how to gain access to an object's privates

lady_liberty01.jpg

I stumbled across an interesting Ruby hack that doesn't seem to be written down anywhere.

It turns out that private methods in Ruby aren't entirely private, and with a bit of indirection you can gain access to them as though they were public methods.

In Ruby 1.8 all we have to do is use the Object#send method.

To demonstrate:

class MyClass
    private
    def say_hello(name)
        puts "Let's go back to my place, #{name}."
    end
end

my_object = MyClass.new
So we get smacked if we try:
> my_object.say_hello
Nomethoderror: private method `say_hello' called for #<MyClass:0x820b4>
	from (irb):8
	from :0
but instead:
> my_object.send :say_hello, "world"
Let's go back to my place, world.

Touché!

Now, honestly, this strikes me as odd. Conceptually, I don't see why send should function differently from an ordinary method call with respect to access control.

The rubydoc for send says nothing of this, and there is some debate on the mailinglist as to whether Ruby 1.9's send will maintain this behavior.

But for now this can be very useful if you disagree with a class designer's method access choices.

In Technology and Software

Posted by Josh Staiger at 01:06 PM

Comments

Object#send works that way because there's no other way it can work – it can't really know if the caller is inside or outside the class, and too many useful features rely on that working.

You can also use instance_eval to voilate encapsulation like that:


my_object.instance_eval { say_hello "everybody, and play ping-pong" }
# produces: Let's go back to my place, everybody, and play ping-pong.

Posted by: Bryce December 28, 2006 06:34 PM | Permanent link