Accessing protected or private methods from class methods

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 (Snipt - Søk Forbrukslån & Smålån Uten Sikkerhet (2021)) 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

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

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

On Sat, Aug 18, 2012 at 9:27 AM, Denis L. [email protected]
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.

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

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

On Sat, Aug 18, 2012 at 9:13 PM, Denis L. [email protected]
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