Private methods not so private?

Discussion in 'Ruby' started by Frank Meyer, Aug 1, 2007.

  1. Frank Meyer

    Frank Meyer Guest

    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
    --
    Posted via http://www.ruby-forum.com/.
    Frank Meyer, Aug 1, 2007
    #1
    1. Advertising

  2. Frank Meyer

    Alex Young Guest

    Frank Meyer wrote:
    > 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.

    --
    Alex
    Alex Young, Aug 1, 2007
    #2
    1. Advertising

  3. On Thu, Aug 02, 2007, Frank Meyer wrote:
    > 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
    Ben Bleything, Aug 1, 2007
    #3
  4. On Aug 1, 4:06 pm, Alex Young <> wrote:
    > Frank Meyer wrote:
    > > 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.


    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
    Daniel Berger, Aug 2, 2007
    #4
  5. On Aug 1, 5:42 pm, Daniel Berger <> wrote:
    > On Aug 1, 4:06 pm, Alex Young <> wrote:
    >
    >
    >
    > > Frank Meyer wrote:
    > > > 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.

    >
    > 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.
    Skye Shaw!@#$, Aug 2, 2007
    #5
  6. Frank Meyer

    Dan Zwell Guest

    Ben Bleything wrote:
    > On Thu, Aug 02, 2007, Frank Meyer wrote:
    >> 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
    >
    >


    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
    Dan Zwell, Aug 2, 2007
    #6
  7. -----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 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-----
    Jørgen P. Tjernø, Aug 2, 2007
    #7
  8. Frank Meyer

    dohzya Guest

    Le jeudi 02 août 2007 à 20:28 +0900, "Jørgen P. Tjernø" a écrit :
    > -----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 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.

    --
    Etienne Vallette d'Osia
    dohzya, Aug 2, 2007
    #8
  9. Frank Meyer

    Guest

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

    On Thu, 2 Aug 2007, "J=F8rgen P. Tjern=F8" wrote:

    > -----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 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--
    , Aug 2, 2007
    #9
  10. On Thu, Aug 02, 2007, Dan Zwell wrote:
    > Ben Bleything wrote:
    > >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.

    >
    > 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
    Ben Bleything, Aug 2, 2007
    #10
  11. Frank Meyer

    Frank Meyer Guest

    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
    --
    Posted via http://www.ruby-forum.com/.
    Frank Meyer, Aug 2, 2007
    #11
  12. On 8/2/07, <> wrote:
    > Hi --
    >
    > On Thu, 2 Aug 2007, "J=F8rgen P. Tjern=F8" wrote:
    >
    > > -----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?

    > 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
    >
    > --
    > * 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)
    >
    David Chelimsky, Aug 2, 2007
    #12
  13. Frank Meyer

    Dan Yoder Guest

    Dan Yoder, Aug 2, 2007
    #13
  14. On Fri, Aug 03, 2007, Frank Meyer 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
    Ben Bleything, Aug 2, 2007
    #14
  15. Frank Meyer

    Guest

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

    On Fri, 3 Aug 2007, David Chelimsky wrote:

    > On 8/2/07, <> wrote:
    >> Hi --
    >>
    >> On Thu, 2 Aug 2007, "J=F8rgen P. Tjern=F8" wrote:
    >>
    >>> -----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--
    , Aug 2, 2007
    #15
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Buster Copley
    Replies:
    5
    Views:
    556
    Gianni Mariani
    Jul 7, 2003
  2. Simon Brunning
    Replies:
    4
    Views:
    390
    Roy Smith
    Apr 19, 2005
  3. Daniel Finnie
    Replies:
    3
    Views:
    182
    Logan Capaldo
    Dec 16, 2006
  4. Kenneth McDonald
    Replies:
    5
    Views:
    312
    Kenneth McDonald
    Sep 26, 2008
  5. Robert
    Replies:
    16
    Views:
    191
Loading...

Share This Page