273

How come this approach of creating a private class method works:

class Person def self.get_name persons_name end class << self private def persons_name "Sam" end end end puts "Hey, " + Person.get_name puts "Hey, " + Person.persons_name #=> raises "private method `persons_name' called for Person:Class (NoMethodError)" 

But this does not:

class Person def self.get_name persons_name end private def self.persons_name "Sam" end end puts "Hey, " + Person.get_name puts "Hey, " + Person.persons_name 
1

7 Answers 7

327

private doesn't seem to work if you are defining a method on an explicit object (in your case self). You can use private_class_method to define class methods as private (or like you described).

class Person def self.get_name persons_name end def self.persons_name "Sam" end private_class_method :persons_name end puts "Hey, " + Person.get_name puts "Hey, " + Person.persons_name 

Alternatively (in ruby 2.1+), since a method definition returns a symbol of the method name, you can also use this as follows:

class Person def self.get_name persons_name end private_class_method def self.persons_name "Sam" end end puts "Hey, " + Person.get_name puts "Hey, " + Person.persons_name 
Sign up to request clarification or add additional context in comments.

Comments

116

ExiRe wrote:

Such behavior of ruby is really frustrating. I mean if you move to private section self.method then it is NOT private. But if you move it to class << self then it suddenly works. It is just disgusting.

Confusing it probably is, frustrating it may well be, but disgusting it is definitely not.

It makes perfect sense once you understand Ruby's object model and the corresponding method lookup flow, especially when taking into consideration that private is NOT an access/visibility modifier, but actually a method call (with the class as its recipient) as discussed here... there's no such thing as "a private section" in Ruby.

To define private instance methods, you call private on the instance's class to set the default visibility for subsequently defined methods to private... and hence it makes perfect sense to define private class methods by calling private on the class's class, ie. its metaclass.

Other mainstream, self-proclaimed OO languages may give you a less confusing syntax, but you definitely trade that off against a confusing and less consistent (inconsistent?) object model without the power of Ruby's metaprogramming facilities.

6 Comments

So if I understand correctly, ruby itself has no access modifier keywords(public, private, and protected) but rather has access modifier methods(public,private,protected)? Is this something that should be brought up on the ruby bug tracker for Matz to implement proper keyword access modifiers or is this expected behaviour?
@Edward It's designed that way junichiito.blogspot.co.uk/2012/03/…. Why "proper" ?
Following on from this, instead of doing private_class_method :method_name you could do private_class_method def method_name....
send(private_method) is also accessible outside the object.
@bjt38 Just for clarity, it would be private_class_method def self.method_name
|
102

By default all class methods are public. To make them private you can use Module#private_class_method like @tjwallace wrote or define them differently, as you did:

class << self private def method_name ... end end 

class << self opens up self's singleton class, so that methods can be redefined for the current self object. This is used to define class/module ("static") method. Only there, defining private methods really gives you private class methods.

Comments

21

Just for the completeness, we can also avoid declaring private_class_method in a separate line. I personally don't like this usage but good to know that it exists.

private_class_method def self.method_name .... end 

Comments

7

Instance methods are defined inside a class definition block. Class methods are defined as singleton methods on the singleton class of a class, also informally known as the "metaclass" or "eigenclass". private is not a keyword, but a method (Module#private).

This is a call to method self#private/A#private which "toggles" private access on for all forthcoming instance method definitions until toggled otherwise:

class A private def instance_method_1; end def instance_method_2; end # .. and so forth end 

As noted earlier, class methods are really singleton methods defined on the singleton class.

def A.class_method; end 

Or using a special syntax to open the definition body of the anonymous, singleton class of A:

class << A def class_method; end end 

The receiver of the "message private" - self - inside class A is the class object A. self inside the class << A block is another object, the singleton class.

The following example is in reality calling two different methods called private, using two different recipients or targets for the call. In the first part, we define a private instance method ("on class A"), in the latter we define a private class method (is in fact a singleton method on the singleton class object of A).

class A # self is A and private call "A.private()" private def instance_method; end class << self # self is A's singleton class and private call "A.singleton_class.private()" private def class_method; end end end 

Now, rewrite this example a bit:

class A private def self.class_method; end end 

Can you see the mistake [that Ruby language designers] made? You toggle on private access for all forthcoming instance methods of A, but proceed to declare a singleton method on a different class, the singleton class.

Comments

6

I too, find Ruby (or at least my knowledge of it) short of the mark in this area. For instance the following does what I want but is clumsy,

class Frob attr_reader :val1, :val2 Tolerance = 2 * Float::EPSILON def initialize(val1, val2) @val2 = val1 @val2 = val2 ... end # Stuff that's likely to change and I don't want part # of a public API. Furthermore, the method is operating # solely upon 'reference' and 'under_test' and will be flagged as having # low cohesion by quality metrics unless made a class method. def self.compare(reference, under_test) # special floating point comparison (reference - under_test).abs <= Tolerance end private_class_method :compare def ==(arg) self.class.send(:compare, val1, arg.val1) && self.class.send(:compare, val2, arg.val2) && ... end end 

My problems with the code above is that the Ruby syntax requirements and my code quality metrics conspire to made for cumbersome code. To have the code both work as I want and to quiet the metrics, I must make compare() a class method. Since I don't want it to be part of the class' public API, I need it to be private, yet 'private' by itself does not work. Instead I am force to use 'private_class_method' or some such work-around. This, in turn, forces the use of 'self.class.send(:compare...' for each variable I test in '==()'. Now that's a bit unwieldy.

1 Comment

The fact that you need to use send has nothing to do with the "how" you mark class methods private. Private methods can not be called from the "outside".
-11

Ruby seems to provide a poor solution. To explain, start with a simple C++ example that shows access to private class methods:

#include <iostream> class C { public: void instance_method(void) { std::cout << "instance method\n"; class_method(); // !!! LOOK !!! no 'send' required. We can access it // because 'private' allows access within the class } private: void static class_method(void) { std::cout << "class method\n"; } }; int main() { C c; c.instance_method(); // works // C::class_method() does not compile - it's properly private return 0; } 

Running the above

 % ./a.out instance method class method 

Now Ruby does not seem to provide the equivalent. Ruby's rules, I think, are that private methods must not be accessed with a receiver. That is,

inst.pvt_method # FAILS pvt_method # WORKS only within the class (good) 

That's OK for private instance methods, but causes problems with private class methods.

I would like Ruby to function this way:

class C def instance_method STDOUT << "instance method\n" # Simple access to the private class method would be nice: class_method # DOES NOT WORK. RUBY WON'T FIND THE METHOD C.class_method # DOES NOT WORK. RUBY WON'T ALLOW IT # ONLY THIS WORKS. While I am happy such capability exists I think # the way 'send' should be used is when the coder knows he/she is # doing a no-no. The semantic load on the coder for this is also # remarkably clumsy for an elegant language like ruby. self.class.send(:class_method) end private_class_method def self.class_method() STDOUT << "class method\n"; end end 

But, alas, the above does not work. Does someone know a better way?

When I see 'send' prior to a method, it's a clear sign the code is violating the intent of the API's designer, but in this case the design is specifically to have an instance method of the class call the private class method.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.