Calling protected class method from instance method in Ruby

前端 未结 3 1073
粉色の甜心
粉色の甜心 2021-01-28 03:35

I\'ve been having this bothering recurring theme; let\'s just say, I have a class which defines an instance method and a protected class method. The instance method must call th

3条回答
  •  轮回少年
    2021-01-28 03:43

    Let's consider a private class method (since protected class methods don't make sense).

    We know it's possible for an instance to call a private method on itself, as long as it isn't using an explicit receiver (self.call_something_private). It seems you also expect that an instance can call a private class method on its own class, but that is not the case.

    Let's look at a way to do this without using send.

    The private and protected macros only affect instance methods of the current scope, not class methods. Here are three ways to rewrite your original code:

    class Bang
      def instance_bang
        self.class.class_bang
      end
    
      # declare method visibility after
      def self.class_bang
        puts "bang"
      end
      private_class_method :class_bang
    
      # inline
      private_class_method def self.class_bang
        puts "bang"
      end
    
      # class scope
      class << self
        # the private macro works here because we're inside the class scope
        private
    
        def class_bang
          puts "bang"
        end
      end
    end
    

    So now we want to expose an interface on the class to call class_bang, but only if it's called by an instance of Bang.

    class Bang
      def instance_bang
        self.class.invoke_class_bang(self)
      end
    
      class << self
        private
    
        def class_bang
          puts "bang"
        end
    
        public
    
        # we ask the receiver to pass itself as an argument ...
        def invoke_class_bang(receiver)
          # ... so that we can check whether it's
          class_bang if receiver.is_a?(Bang)
        end
      end
    end
    

    That's not a very nice looking solution though. Here's a sneakier way:

    class Bang
      def initialize
        def self.instance_bang() self.class.method(:class_bang).call end
      end
    
      class << self
        private
        def class_bang
          puts "bang"
        end
      end
    end
    

提交回复
热议问题