Private methods not so private?


#1

Hello,
I’m using Ruby 1.8.2 and I’m reading the book “Programming Ruby 2nd
edition”. In this book they say, that Ruby implements private methods by
not allowing another receiver than “self”. After reading this, I tried
the following:

class Test
private
def print_hello
puts “Hello everyone!”
end
end

t = Test.new
t.send( “print_hello” )

This program runs just fine and prints “Hello everyone!”.

Is this behaviour to be expected?

Turing


#2

Frank M. wrote:

end
Is this behaviour to be expected?
Yes - send trumps private. Private in Ruby is more of a guideline than
a rule. The implementer is trying to tell you not to go there, but you
can get around it if you really need to.


#3

On Thu, Aug 02, 2007, Frank M. wrote:

This program runs just fine and prints “Hello everyone!”.

Is this behaviour to be expected?

Yes. send bypasses protection levels, for reasons I’m not really clear
on. If I were to hazard a guess, it would be that what you send is
eval’ed inside the context of the object, thereby making self the
receiver, but that’s a total guess.

Test.new.print_hello will cause the error you expect.

As a sidenote, upgrade your ruby :wink:

Ben


#4

On Aug 1, 4:06 pm, Alex Y. removed_email_address@domain.invalid wrote:

puts “Hello everyone!”
Yes - send trumps private. Private in Ruby is more of a guideline than
a rule.

It’s a guideline in any language.

I think people tend to think of ‘private’ as some sort of security
enforcement. It’s not. It just means, “You aren’t supposed to access
this method directly. If you go out of your way to do so, I am not
responsible for the consequences”.

The advantage of allowing send to access private methods is that it
allows you to test private methods. :slight_smile:

Regards,

Dan


#5

Ben B. wrote:

As a sidenote, upgrade your ruby :wink:

Ben

Ben,

It is always possible to run private methods, change class variables,
etc. The way to do it is to reopen the class (or add an instance method)
that calls the private method or sets/gets the variable we need. Since
it’s possible, why not make it easier? Ruby tries not to impose on what
the user can do, so we have foo.send, foo.instance_variable_set, and
foo.instance_variable_get.

Dan


#6

On Aug 1, 5:42 pm, Daniel B. removed_email_address@domain.invalid wrote:

This program runs just fine and prints “Hello everyone!”.

Is this behaviour to be expected?

Yes - send trumps private. Private in Ruby is more of a guideline than
a rule.

It’s a guideline in any language.

I think people tend to think of ‘private’ as some sort of security
enforcement. It’s not.

It is in C++ and Java, maybe even VB, if I recall… (I try not to
recall VB).
Well, maybe not a “security enforcement” but a visibility and/or
invocation restriction.

My JVM even gives me an IllegalAccessException when I try to access an
private Method at runtime via reflection.


#7

Le jeudi 02 août 2007 à 20:28 +0900, “Jørgen P. Tjernø” a écrit :

-----END PGP SIGNATURE-----

It is a root of bad practices, but it may be necessary… I think a good
principe is : use public methods only of others’s libraries, and use
instance_eval (etc) of yours when you need it, and if there is
collaboration between classes (like friendly classes of C++).
Thus you will be able to keep your model safe.


#8

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Daniel B. wrote:

[ … snip … ]
The advantage of allowing send to access private methods is that it
allows you to test private methods. :slight_smile:
Isn’t that a Bad Practice ™? Private methods are mostly meant for the
internals of a class, and a test should really only test how the class
interacts with the rest of the world - right? (i.e. public methods)

I might’ve missed the whole point, so this post contains Many
Questionmarks. :wink:

Kindest regards, Jørgen P.
Tjernø.-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGsbDiUMzc1WGo4zgRAoJ3AJ9MtInhJTZS9Uq1ABXwxxN9rwkeVACffYBG
+iCp8nwruwSH/WzNSAsbQp8=
=ndYq
-----END PGP SIGNATURE-----


#9

On Thu, Aug 02, 2007, Dan Z. wrote:

the user can do, so we have foo.send, foo.instance_variable_set, and
foo.instance_variable_get.

Right. I was just trying to answer the original question :slight_smile:

Ben


#10

Hi –

On Thu, 2 Aug 2007, “Jørgen P. Tjernø” wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Daniel B. wrote:

[ … snip … ]
The advantage of allowing send to access private methods is that it
allows you to test private methods. :slight_smile:
Isn’t that a Bad Practice ™? Private methods are mostly meant for the
internals of a class, and a test should really only test how the class
interacts with the rest of the world - right? (i.e. public methods)

No; you definitely want to test your private methods too. First of
all, if you’re writing a class, you need to test its internals;
nothing should be a “black box” for the tests. Second, private
methods are actually available to the world, even without send. They
just have to be called the right way:

puts “Hi”
class C
attr_accessor :x
end
C.class_eval { define_method “y” }

puts, attr_accessor, and define_method are all private, but you’d
certainly want to test them if you had written them.

David


#11

Hello everybody,
thanks for your explanations. And I didn’t know that ruby allows the
reading and setting (and even creation) of instance variables.

What does class_eval do?

And actually I’m using Ruby 1.8.6 I confused the version number with
those in the book.

Turing


#12

On 8/2/07, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

allows you to test private methods. :slight_smile:
Isn’t that a Bad Practice ™? Private methods are mostly meant for the
internals of a class, and a test should really only test how the class
interacts with the rest of the world - right? (i.e. public methods)

No; you definitely want to test your private methods too.

Conventional wisdom in the Agile/TDD community is that you shouldn’t
be testing private methods. The reasoning goes something like this:

  1. If you’re doing TDD, you’re sending messages to objects that other
    objects in your system will send. The corresponding methods should be
    public.

  2. In TDD, private methods appear through refactoring, and therefore
    are already tested implicitly through the tests of public methods.

  3. When you feel the need to add tests on private methods (which have
    appeared through refactoring), it should be considered a sign that a
    new object is wanting to be born and should be extracted out into a
    new class.

My sense is that some of this thinking is a product of the fact that
testing privates in Java means using reflection, resulting in
refactoring inefficiencies. In Ruby, testing privates is fairly easy
and we don’t really have the refactoring tools that the Java community
has, so refactoring in Ruby tends to be much more manual anyhow.

That said, I think the OO design questions that get raised are worthy
of exploration when you feel the need to test something private.

WDYT?


#13

I’ve written some interesting use cases concerning send here. FYI: my
understanding is that 1.9 will change the semantics of send so that it
no longer accesses private methods.

http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/644b55fdb9e346f5/98ba46415087de05#98ba46415087de05

Regards,
Dan


#14

On Fri, Aug 03, 2007, Frank M. wrote:

thanks for your explanations. And I didn’t know that ruby allows the
reading and setting (and even creation) of instance variables.

Yup. That’s what @something variables are.

What does class_eval do?

It’s like instance_eval, but evaluates in the context of a class
instead. Hark:

str = “foo”
str.instance_eval {length}
=> 3 (same as calling str.length)

str.class.class_eval {def foo; return ‘bar’; end}
str.foo
=> ‘bar’ # effectively the same as re-opening the class

I’m sure there are other subtleties involved, but that’s the top-level
view.

Ben


#15

Hi –

On Fri, 3 Aug 2007, David C. wrote:

The advantage of allowing send to access private methods is that it

  1. If you’re doing TDD, you’re sending messages to objects that other

My sense is that some of this thinking is a product of the fact that
testing privates in Java means using reflection, resulting in
refactoring inefficiencies. In Ruby, testing privates is fairly easy
and we don’t really have the refactoring tools that the Java community
has, so refactoring in Ruby tends to be much more manual anyhow.

That said, I think the OO design questions that get raised are worthy
of exploration when you feel the need to test something private.

WDYT?

My tendency is to think that the public/private distinction in Ruby
does not overlap very closely with the line between methods you’re
likely to use from outside and methods you’re not, mainly for two
reasons.

First, private instance methods of Kernel, like raise and sprintf:
these are not behind the black curtain, but are private so as to be
callable in a receiverless way. I’d expect them to be tested.

Second, quick and frequent scope and self-scope changes:

class C
define_method …
end

etc. define_method, like raise and sprintf, won’t go away because of
refactoring; it’s a private method, but part of the interface one is
expected to use (rather than simply a by-product of the implementation
of some other such method).

That said, it may be that there are private methods one wouldn’t test,
namely those that are really part of the black box, where all you
really need to know is whether the black box produces the right
answer. But I think that’s just a subset of private methods,
certainly in core/standard Ruby and quite possibly in other Ruby code.

David