Private methods not so private?

F

Frank Meyer

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
 
A

Alex Young

Frank said:
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?
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.
 
B

Ben Bleything

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?

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 ;)

Ben
 
D

Daniel Berger

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. :)

Regards,

Dan
 
S

Skye Shaw!@#$

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.
 
D

Dan Zwell

Ben said:
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 ;)

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
 
J

Jørgen P. Tjernø

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

Daniel said:
[ .. snip .. ]
The advantage of allowing send to access private methods is that it
allows you to test private methods. :)
Isn't that a Bad Practice (tm)? 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. ;-)

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-----
 
D

dohzya

Le jeudi 02 août 2007 à 20:28 +0900, "Jørgen P. Tjernø" a écrit :
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Daniel said:
[ .. snip .. ]
The advantage of allowing send to access private methods is that it
allows you to test private methods. :)
Isn't that a Bad Practice (tm)? 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. ;-)

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-----

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.
 
D

dblack

--1926193751-1278936812-1186055005=:5495
Content-Type: MULTIPART/MIXED; BOUNDARY="1926193751-1278936812-1186055005=:5495"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

--1926193751-1278936812-1186055005=:5495
Content-Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed
Content-Transfer-Encoding: QUOTED-PRINTABLE

Hi --

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

Daniel said:
[ .. snip .. ]
The advantage of allowing send to access private methods is that it
allows you to test private methods. :)
Isn't that a Bad Practice (tm)? 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

--=20
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
--1926193751-1278936812-1186055005=:5495--
--1926193751-1278936812-1186055005=:5495--
 
B

Ben Bleything

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.

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

Ben
 
F

Frank Meyer

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
 
D

David Chelimsky

Hi --

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

Daniel said:
[ .. snip .. ]
The advantage of allowing send to access private methods is that it
allows you to test private methods. :)
Isn't that a Bad Practice (tm)? Private methods are mostly meant for th= e
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?
 
B

Ben Bleything

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
 
D

dblack

--1926193751-659028526-1186083000=:15917
Content-Type: MULTIPART/MIXED; BOUNDARY="1926193751-659028526-1186083000=:15917"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

--1926193751-659028526-1186083000=:15917
Content-Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed
Content-Transfer-Encoding: QUOTED-PRINTABLE

Hi --

Hi --

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

Daniel Berger wrote:
[ .. snip .. ]
The advantage of allowing send to access private methods is that it
allows you to test private methods. :)
Isn't that a Bad Practice (tm)? Private methods are mostly meant for th= e
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?

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

--=20
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
--1926193751-659028526-1186083000=:15917--
--1926193751-659028526-1186083000=:15917--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top