Sunday 5 June 2011

ruby encapsulation is for $%*t

Wow, I just learned that ruby encapsulation can be broken by simply using:

   my_object.send(:my_secret_private_method)

That's kinda useful to know if you're trying to unit test a private method, but makes the concept of encapsulation totally meaningless in ruby.

Apparently, Ruby 1.9 revokes this "special" privilege, but then there are still many ways to use the power of ruby meta-programming to get to them anyway (see above link for some examples).

3 comments:

tea42 said...

I do not think 1.9 get rid of this. Rather it adds #public_send which has been an unfortunate missing feature --in fact there was much argument over the fact that matz wanted to fix it "right", by renaming #send to #fcall and change #send be for public calls only. But he was pretty much shouted down b/c it would break old code.

In any case, it't not a big deal. #send is intended to meta-programming and should be used knowledgeably.

Taryn East said...

Thanks for that. It's good to know where it all ended up. So I guess that for standard usage you'd use "public_send". I think I'd have preferred Matz's original idea even if it was a pain to have to add the word "public_" to every instance in the codebase (search and replace isn't *that* hard).

I still think it kinda weird to have encapsulation... and then the ability to break it anyway. it just feels kinda weird.

But then I guess you can't have to great a security on private methods when you could just reopen the class and redeclare the private method as public so an easy way to access them makes sense just from a convenience perspective.

Ethan said...

you can also instance_eval on any object to bypass any restrictions. ruby does not make it difficult to get around that sort of thing.

I had heard that #send would be renamed to #send!, and #send would act like #public_send does now, but I guess that never came to be, as #send! doesn't appear to exist. that seemed like a sensible approach to me. ah well.