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
on 2012-08-18 17:10
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).
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
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).
on 2012-08-18 19:18
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.
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.
on 2012-08-20 15:45
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
Log in with Google account | Log in with Yahoo account
No account? Register here.