可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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:
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
回答2:
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.
回答3:
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.
回答4:
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
回答5:
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.
回答6:
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.
回答7:
As of ruby 2.3.0
class Check
def self.first_method
second_method
end
private
def self.second_method
puts "well I executed"
end
end
Check.first_method
#=> well I executed