How to create a private class method?

后端 未结 8 1313
北荒
北荒 2020-12-02 04:43

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

class Person

  def self.get_name
    persons_name
  end

  class << self

    private         


        
相关标签:
8条回答
  • 2020-12-02 04:51

    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
    
    0 讨论(0)
  • 2020-12-02 04:52

    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.

    0 讨论(0)
  • 2020-12-02 04:53

    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
    
    0 讨论(0)
  • 2020-12-02 04:55

    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.

    0 讨论(0)
  • 2020-12-02 04:59

    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.

    0 讨论(0)
  • 2020-12-02 05:02

    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.

    0 讨论(0)
提交回复
热议问题