Ruby Access Control – Are Private And Protected Methods Only A Guideline?

ProtectA few days ago Chad Fowler ran the following quick quiz on Twitter:

Ruby quiz: in 140 chrs or less, why doesn't this work: class Y; def a;
self.x end; private; def x; puts "hi" end end; Y.new.a

Here is the formatted version:

class Y
  def a
    self.x
  end
  private
  def x
    puts "hi"
  end
end
 
Y.new.a

Running this produces the following error:

a.rb:3:in `a': private method `x' called for #<Y:0x7f819a82d548> (NoMethodError)
    from a.rb:11

I wasn't immediately able to figure out what was wrong with this code and since access control is one of those fundamental things that you should know, if you want to be a decent Ruby developer, I decided to dig a little further to figure it out. Here is the scoop.

Private, Protected and Public – Ruby Method Visibility

The concept of private, protected and public methods in Ruby is somewhat different than it is in languages like Java (well, not so much public, that's very similar :)). In Java if a method is declared private, it can only be accessed from other methods in the same class. When a method is declared protected it can be accessed by other classes in the same package as well as by subclasses of its class in a different package. When a method is public it is – of course – always visible to everyone. So, in essence with Java, these keywords protect the various members from access by classes, depending on where these classes are in the inheritance/package hierarchy.

In Ruby, the inheritance hierarchy or the package/module don't really enter into the equation, it is rather all about which object is the receiver of a particular method call. When a method is declared private in Ruby, it means this method can never be called with an explicit receiver. Any time we're able to call a private method with an implicit receiver it will always succeed. This means we can call a private method from within a class it is declared in as well as all subclasses of this class e.g.

class A
  def main_method
    method1
  end
 
  private
  def method1
    puts "hello from #{self.class}"
  end
end
 
class B < A
  def main_method
    method1
  end
end
 
A.new.main_method
B.new.main_method
alan@alan-ubuntu-vm:~/tmp$ ruby a.rb
hello from A
hello from B

However, as soon as we try to use an explicit receiver, even if the receiver is "self", the method call will fail e.g.

class C < A
  def main_method
    self.method1
  end
end
 
C.new.main_method
alan@alan-ubuntu-vm:~/tmp$ ruby a.rb
a.rb:36:in `main_method': private method `method1' called for #<C:0x7f67025a0648> (NoMethodError)
    from a.rb:40

Do you recognise the error? This was the answer to the quiz that Chad ran. We were trying to call a private method with an explicit receiver and even though we were doing it from within the class where our private method was defined, it would still fail for that reason. And of course, since you can't call a private method with an explicit receiver, you can never call it from outside the class hierarchy where it was defined.

Protected methods are also a little different. You can always call a protected method with an implicit receiver, just like private, but in addition you can call a protected method with an explicit receiver as long as this receiver is self or an object of the same class as self. Let's modify our example above and make the method protected:

class A
  def main_method
    method1
  end
 
  protected
  def method1
    puts "hello from #{self.class}"
  end
end
 
class B < A
  def main_method
    method1
  end
end
 
class C < A
  def main_method
    self.method1
  end
end
alan@alan-ubuntu-vm:~/tmp$ ruby a.rb
hello from A
hello from B
hello from C

Now, the call with the implicit receiver in class B succeeds (as before) but the call with the explicit receiver in class C also succeeds (unlike when method1 was private). Furthermore, doing the following is also fine:

class D < A
  def main_method
    B.new.method1
  end
end
 
D.new.main_method
alan@alan-ubuntu-vm:~/tmp$ ruby a.rb
hello from B

Everything works because B.new is the same type of object as self and so the call to method1 succeeds. If however we make class D NOT inherit from A, we get the following:

class D
  def main_method
    B.new.method1
  end
end
 
D.new.main_method
alan@alan-ubuntu-vm:~/tmp$ ruby a.rb
a.rb:39:in `main_method': protected method `method1' called for #<B:0x7fe81d00efa8> (NoMethodError)
    from a.rb:46

In this case B.new is no longer the same type of object as self and so trying to call a protected method with B.new as the receiver – fails. So, I guess the inheritance hierarchy does actually play a role when it comes to access control in Ruby, it's just not as big a role as it is in a language like Java.

Public methods are – of course – accessible with any kind of explicit or implicit receiver from anywhere (as the name implies).

It Is All Just A Guideline Anyway

Invisible

What if we don't want to obey these access rules? For example, what if I want to test my private method? This is reasonably easy to do in Ruby, you simply need to "call" your private method using "send" (I will cover the use of "send" in more detail in a subsequent post I am planning to do, on passing methods as arguments in Ruby). For example, let's modify our original example as follows:

class A
  private
  def method1
    puts "hello from #{self.class}"
  end
end
 
A.new.send(:method1)
alan@alan-ubuntu-vm:~/tmp$ ruby a.rb
hello from A

That was just way too easy! It works fine in both Ruby 1.8 and 1.9, it was supposed to disappear in 1.9 but it hasn't yet. However even if it does, we can always simply define a public method on the object in question which delegates to the private one or redefine our private methods as public and then change them back again when we're done.

Testing private methods is often frowned upon since we're breaking encapsulation and not testing through the interface. However, I do believe that sometimes, it simply makes sense to test private method separately, especially if they are used by multiple public methods. You can then simply mock out the private method when testing the public API rather than having to indirectly test its functionality over and over again. I think it makes for a better and more terse set of tests, which I think is smarter in a TDD context.

It is worth mentioning that in the Java world, this practice (testing private methods) is frowned upon much more strongly. I believe this attitude is more to do with refactoring support than with anything else. It is possible to invoke private methods in Java from outside the class. But, aside from the fact that this is much more labour intensive than in Ruby, the fact that you have to use reflection means you give up the refactoring support that Java IDEs provide. This is a big deal in Java, since it has excellent refactoring tools (being a strongly typed language). In Ruby it is not really much of an issue since refactoring support is minimal and arguably not as necessary due to the dynamic nature of the language.

Well, I hope this post has helped to clarify how access control works in Ruby as well as giving a bit more of an insight into the inner workings of the language. Until next time.

Images by hebedesign and vaXzine

  • http://powerupdev.com Tim Case

    What about just ignoring access control in Ruby and just coding all methods as public? I know from what my elders taught me that if access control is ignored a vortex in the space time continuum will be opened and all kittens will be sucked into the center of it, and yet… I ignored access control and nothing happened…

    • http://www.skorks.com Alan Skorkin

      Hi Tim,

      That is very much a fair point. Ever since I started coding in Ruby I no longer pay too much attention to access control. I do tend to use it sometimes but it is almost for aesthetic reasons, to “clean up” and sort things out in my own head. It is nice to know that access control is there though :).

    • http://gilesbowkett.blogspot.com giles bowkett

      I ignore access control in Ruby whenever possible. I think it’s a complete waste of time. It’s like locking your door when you live next door to Doctor Manhattan from Watchmen. The next programmer can circumvent any restriction you put in place. When the other guy can walk through walls or simultaneously exist at every point in the universe, what’s the point in locking your door? I suppose the answer is obviously “to let him know you don’t want to be disturbed” but I’ve never found that persuasive in practice.

      I actually think the use of private, protected or public in Ruby is a code smell, and the absolute worst kind: it indicates that somebody is more concerned with the theory behind their code than the practice. I’ve been aggravated with people for hiding their useful methods from me for no reason many, many more times than I’ve been grateful to them for protecting me from that big bad method so I wouldn’t hurt my poor sweet widdle fingers.

      When you say “this is something people will need to use” and “this is something people win’t need to use”, you’re making up rules for any and all users of your code, which is presumptuous, arrogant, and uncivil. I shouldn’t have to suffer through some tiresome, irrelevant restrictions just because you didn’t imagine all the different things I might want to do with your code. If my particular use case is a use case you didn’t envision, then your conclusions about access control can be irrelevant.

      Sorry, bit of a rant.

      • http://gilesbowkett.blogspot.com giles bowkett

        ps, re: this sentence:

        ” In Ruby it is not really much of an issue since refactoring support is minimal and arguably not as necessary due to the dynamic nature of the language.”

        i actually think that would be a very difficult argument to make. I think refactoring is absolutely essential to writing Ruby well.

        • http://www.skorks.com Alan Skorkin

          I do agree that refactoring is very much necessary in Ruby, but it is mostly accomplished without a lot of tool support.

      • http://www.skorks.com Alan Skorkin

        This is actually a very good point and one of the things that has always annoyed me about Java. People try to lock down their classes, methods (final keyword as so forth) etc. Companies trying to create sets of coding standards designed to protect us from ourselves – highly annoying. The Ruby ecosystem is so much better in this regard.

      • http://avdi.org/devblog Avdi

        Giles, this is why in my response to the OP I said Ruby access control isn’t about hiding code from the guy across the hall. If I still thought of access control in C++/Java terms I’d probably have the same dim view of it that you have.

        Public/Private in Ruby is above all a reminder to *myself* which methods are implementation details and I can feel free to change them or remove them at any time; and which methods I expect to be more stable in existence and behavior – e.g. I’ve used them from other objects, documented them in RDoc, written a public tutorial which shows those methods being used. I like to be able to guarantee that object methods will stay at least somewhat stable (especially after release 1.0 of a library), but I don’t have time or good reason to promise that EVERY method will be stable. Public/private delineates the line between the ones I’ll make an effort to keep consistent and the ones I won’t. I really don’t have a problem with making it a little extra work to call the private methods, because that’s a reminder to the client that they are on their own.

        When appropriate I also sometimes use public/private to delineate the line between methods which always leave an object in a consistent state, and methods that might not and are intended to be used in concert with other methods in order to maintain consistency.

        • http://gilesbowkett.blogspot.com Giles Bowkett

          Hey Avdi, yeah, I know, I understood that when I wrote the comment, but that to me is like using a design flaw as a new feature. You’re using it like a form of de facto annotation. I’m not going to be strident in my dismissal, because a big source of guilt for me is not taking better care of my open source projects, and it’s possible that if I did take better care of them I’d see a lot of use for your approach to access control, but in practice I don’t see it happen often, either in code for hire or open source. I often do see situations where it would be easier to use somebody’s code if they hadn’t made a bunch of inaccurate assumptions about the code’s eventual re-use and enforced those assumptions with access control. That offends me – it violates YAGNI, and any code which violates YAGNI should not exist, by definition.

          • http://avdi.org/devblog Avdi

            I wonder if I have a different process for creating private methods than other authors? I never go through and say “hmm, this should be private, this should be public”. I just create public methods as I need them. Then when I go through and factor out some common code from some public methods I’ll stick the factored-out code in the private section by default.

  • http://avdi.org/devblog Avdi

    When you feel the temptation to test a private method, that’s your code telling you it’s time to factor that code out into a separate helper class. Longer discussion here: http://avdi.org/devblog/2008/10/21/testing-private-methods/

    • http://www.skorks.com Alan Skorkin

      You know, I used to be of the same opinion, but time and time again I run into a situation where it just doesn’t make sense to extract the private methods into a helper class and thereby make them non-private. It creates a “useless” extra class (only useful in one scenario) and exposes functionality that shouldn’t be exposed. I still think that you shouldn’t necessarily always test all your private methods (and there is certainly a case to be made for factoring our some functionality if the situation calls for it), but in certain situations it is ok.

      • http://avdi.org/devblog Avdi

        You’re exposing that functionality anyway by writing tests for it. Practically speaking, exposure is about limiting dependencies. By writing tests for it you are increasing the dependencies (things it is capable of breaking if changed) on it. Writing tests is also, for better or for worse, one way of communicating to your code readers “this is reliable and you can depend on it”. You’re effectively making it public; you might as well make it official.

        If you want to indicate visually that it’s still part of class internals, you can always make it a nested class rather than a top-level class.

        If your concern is that you’re extracting a class whose whole existence is based on a single method, consider making it a (tested) class-level method instead.

        Public/private in Ruby isn’t about hiding code from the guy across the hall. It’s about making sure the collaborations between objects are stable and well-understood. If you look at it from that perspective, there’s nothing wrong with this kind of extra “exposure”.

        • http://www.skorks.com Alan Skorkin

          I see what you’re getting at. I guess my perspective comes from the way I’ve actually been using access control in Ruby. I don’t really use it to hide functionality, but basically to organize the structure of the class to make it “cleaner”. I’d have no issue with making all private methods non-private which would make them easily testable. At the same time if I think it was called for, these methods may be extracted into a separate class as you suggest. But, since I have no issue with making the methods public, I have little issue with keeping them private for my own organizational purposes and testing them anyway. I hope this makes some sense :).

        • Timothy George Rowe

          Do you believe in checking the code coverage of your tests? Then if testing something is exposing it, you now have a dependency on every single line of your code and can’t change *anything*. Or maybe testing something doesn’t make it public, making it public makes it public.

    • Timothy George Rowe

      Oh so *so* wrong.

      Computer scientists learned over 30 years ago that interface testing was *very* weak and couldn’t cope with realistic code complexity. A level of confidence in the code that you can get easily by testing private methods could take longer than the universe is going to last if you don’t test private methods. You test private methods for exactly the same reason as you *have* private methods: to remove duplication and to reduce complexity. Removing duplication is nice, but it’s reducing complexity that’s the killer. Test private methods separately from their clients and the amount of testing you need for a level of confidence is the sum of the number of tests you need for each. Test them only from the interface and the amount of testing for the same confidence is the *product* of what you would need for each. If the private method would need 30 test cases, and the client would need 30 test cases, do you want to write 60 test cases or 900 test cases?

      Yes, it breaks the purity of TDD. But the real world isn’t pure, and sometimes we have to get a little bit dirty to do what actually works.

  • http://blog.josh-nesbitt.net Josh Nesbitt

    I see using access control as a way of defining your public API, it’s how you interact with that object that matters over “protecting” the usage of the object from others. Testing private methods is mainly a good idea, but it depends on the context. I prefer to test the interface to those private methods rather than those methods specifically (unless as you say, you are using that method in a few different cases). I guess it all depends on the complexity of the code and your confidence surrounding the brittleness of those private methods in question.

    • http://www.skorks.com Alan Skorkin

      I think this idea is very similar to what Avdi has described above. With Ruby access control is about leaving a message to yourself regarding how you want and expect the object to be used, rather than protecting methods from others. I believe this is a good way of thinking about it.

  • Justin Ko

    To me, I view protected and private methods as “helpers” to public methods.
    And yes, they shouldn’t need testing.

  • Jörg W Mittag

    Quick note: there is a special case where calling private methods with an explicit receiver is allowed:

    If the method name ends with ‘=’ (i.e. it is an attribute writer) and the explicit receiver is ‘self’, then this works. This is necessary, because setters can only be called with an explicit receiver because of the method/variable ambiguity. Otherwise it wouldn’t be possible to call private setters.

  • Colin MacKenzie

    on your last example you can also call that private method with: A.new.method(:method1).call

  • Pingback: Why does Ruby have both private and protected methods? | Ask Programming & Technology

  • Pingback: difference between private protected public in ruby | codedecoder