Forum: Ruby accessing protected or private methods from class methods

Posted by Denis Leclair (dleclair)
on 2012-08-18 17:10
There is all kinds of information out there on Ruby method visibility
rules. After doing a lot of reading I think I have pretty good handle on
how the rules work but what I'm still struggling with is the philosophy.

Let me clarify where I'm having difficulty with a specific example...

There are times when I want to setup a class interface in such a way
that the normal instantiation method (new() method in most languages)
cannot
be used and to force the caller to use a class (a.k.a. static) factory
method so that certain decisions can be made prior to object creation.

After a lot of trial and error, and reading on the subject, I've reached
the conclusion that this pattern cannot be done cleanly in Ruby. The
fundamental problem seems to be that as soon as the new() method is made
private then not only is it restricted from the
public API, but it's also blocked from being called from the class' own
factory method. The only way I can see to circumvent the restriction is
to use the ugly .send() method to bypass visibility rules.

The code snippet (http://snipt.org/vAk4) shows how I've implemented this
in Ruby, but again, it just seems wrong that I have to bypass the
visibility rules using the .send() method.

Can anybody clarify the rationale for the restrictions placed on class
methods and/or if there is a better way to implement this common pattern
in Ruby?

Thanks,
Denis
Posted by Jan E. (jacques1)
on 2012-08-18 17:38
Hi,

What does this have to do with the new method? Your problem is obviously 
not with calling the private new method but with calling the (protected) 
instance method "configure".

And that's actually a problem. In Ruby there's no special connection 
between a class and its instances. So a class cannot directly access 
private instance methods (or vice versa).
Posted by Denis Leclair (dleclair)
on 2012-08-18 18:27
Hi Jan,

You are quite right - my initial post was confusing in that regard. The 
real problem is with the .configure() method but my question still 
stands.

In this particular example, I have a .configure() method which is only 
intended to be run at object creation time in order to set up some of 
the internal state of the object. Ideally, I would prefer to to setup 
the interface so that it's not callable by any external code. It seems 
like the only way to do this is to declare the method private and then 
have my class method bypass the restriction using .send(). Is there no 
cleaner way to do this?

This factory method is just one example. There are many other cases 
where it would make good sense to have a class method that can modify 
the internal state of an object by calling into protected or private 
methods on an object that it has a handle to. I just don't see a nice 
way of doing it with Ruby.

Thanks again,
Denis
Posted by Jan E. (jacques1)
on 2012-08-18 19:16
In your case the code from the configure method should be in the
initialize method. This will solve your problem and is also the right
way to do it. Setting up an object is what the initialize method is for.
It shouldn't depend on some other method being called (this will make
the code rather obscure and fragile).

Apart from that, I don't see any clean solution to the general problem.
Like I said: There's no special connection between a class and an
instance of the class. You can either make a method either visible to
all classes or none at all.

So I guess you'll have to stick with the "send" workaround (which I 
don't find
too bad).
Posted by Christopher Dicely (Guest)
on 2012-08-18 19:18
(Received via mailing list)
On Sat, Aug 18, 2012 at 9:27 AM, Denis Leclair <lists@ruby-forum.com> 
wrote:
> like the only way to do this is to declare the method private and then
> have my class method bypass the restriction using .send(). Is there no
> cleaner way to do this?
>
> This factory method is just one example. There are many other cases
> where it would make good sense to have a class method that can modify
> the internal state of an object by calling into protected or private
> methods on an object that it has a handle to. I just don't see a nice
> way of doing it with Ruby.

In a sense what you are asking for is self-contradictory in Ruby -- a 
class is
an object (an instance of class Class), and its a different object than 
its own
instances. So, when you take a method out of the public interface to 
hide it
from "external" code, you are hiding it from class methods because they 
are
external code as much as any other method that isn't part of the 
instance is.

So you can't have a method that is part of an instance's public 
interface as
far as methods on the object that is the instance's class are concerned, 
but
not part of the public interface of the instance as far as methods on 
other
external objects are concerned.

It sounds like what you really want is:
A privileged interface object (this is the role you seem to be using the 
class
in, but there is no real reason this has to be a class and some OO 
design
reasons why you may not want it to be), and
A class whose instances provide a public interface that is used by the
privileged interface object but not by most of the consumers that 
interact
with the privileged interface object, and
A class whose instance each wrap an instance of the class described
previously, that provides a narrower public interface -- instances of 
this
class are returned by the privileged interface object to consumer code.
Posted by Denis Leclair (dleclair)
on 2012-08-18 21:13
Christopher Dicely wrote in post #1072759:
> In a sense what you are asking for is self-contradictory in Ruby -- a
> class is
> an object (an instance of class Class), and its a different object than
> its own
> instances. So, when you take a method out of the public interface to
> hide it
> from "external" code, you are hiding it from class methods because they
> are
> external code as much as any other method that isn't part of the
> instance is.

I disagree that this is self-contradictory in Ruby. I understand that 
they've gone with the "a class is an object" model and that certainly 
brings a lot of nice characteristics to the language. But that, in and 
of itself, does not require that class methods be treated as "external 
code" from the perspective of the class object.

There is still a very strong association between the class method and 
the class object that it is bound to so there's no technical reason why 
the class method couldn't be given more access privileges than other 
non-associated code. It must have been a deliberate decision to set it 
up this way and I'm trying to get an appreciation for why that decision 
would have been made.

Then again, maybe there is a technical reason why those privileges 
couldn't be given and I'm just not seeing it.
Posted by Robert Klemme (robert_k78)
on 2012-08-20 15:45
(Received via mailing list)
On Sat, Aug 18, 2012 at 9:13 PM, Denis Leclair <lists@ruby-forum.com> 
wrote:
>> instance is.
>
> I disagree that this is self-contradictory in Ruby. I understand that
> they've gone with the "a class is an object" model and that certainly
> brings a lot of nice characteristics to the language. But that, in and
> of itself, does not require that class methods be treated as "external
> code" from the perspective of the class object.

It (i.e. the class) is external from the perspective of the instance.
The instance should be responsible for maintaining its own affairs.

> There is still a very strong association between the class method and
> the class object that it is bound to so there's no technical reason why
> the class method couldn't be given more access privileges than other
> non-associated code. It must have been a deliberate decision to set it
> up this way and I'm trying to get an appreciation for why that decision
> would have been made.

Even if it is technically feasible it will add additional complexity
and it may make things actually unnecessary complex - especially if
you start thinking about how super and sub classes should be handled
in this case.  Current checks are simpler because "self" is the only
instance which is allowed to invoke protected methods and private
methods must only be invoked without explicit receiver.

> Then again, maybe there is a technical reason why those privileges
> couldn't be given and I'm just not seeing it.

Since I am not Matz I cannot answer this definitively.  My assumption
of possible reasons

 - encapsulation at the object level, since class != any of its
instances it is external to the object and giving it privileged access
will break that rule (you then could also ask for instance.@foo = 123,
i.e. easy access to instance variables
 - added complexity (cost in development, maintenance, runtime 
performance)
 - there is a solution (use #send)
 - too low benefit given items above

I think Matz just did not want to encourage people writing code
manipulating an instance in other classes.

Kind regards

robert
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.