Are there any good reasons for 'private' to work like Ruby does?

It took me a while to figure out how private methods work in Ruby, and it really seems very inconvenient to me. Does anyone know if there are good reasons for private methods to be processed as they are? Are these just historical reasons? Or the reasons for the implementation? Or are there good logical reasons (i.e., semantic)?

For example:

class Person private attr_reader :weight end class Spy < Person private attr_accessor :code public def test code #(1) OK: you can call a private method in self Spy.new.code #(2) ERROR: cannot call a private method on any other object self.code #(3) ERROR!!! cannot call a private method explicitly on 'self' code="xyz" #(4) Ok, it runs, but it actually creates a local variable!!! self.code="z" #(5) OK! This is the only case where explicit 'self' is ok weight #(6) OK! You can call a private method defined in a base class end end 
  • Ruby's behavior on lines (1), (2) and (5) seems reasonable.
  • The fact that (6) is in order is a bit strange, especially from Java and C ++. Good reason for this?
  • I really don't understand why (3) fails! Explanation, anyone?
  • The problem in line (4) looks like an ambiguity in the grammar that has nothing to do with 'private'.

Any ideas?

+25
private ruby language-design access-specifier
Oct 14 '09 at 9:45
source share
1 answer

You may find it helpful to read the ruby โ€‹โ€‹definition of the public, private, and protected. (Go to access control)

Ruby private is similar to secure Java. There is no Ruby equivalent for Java. EDIT:. This solution now provides a method for falsifying the Java ideal of the private in Ruby objects.

Private is defined as methods / variables that can name implicitly. This is why statements 2 and 3 fail. In other words, private limits use methods / variables for the context of the class or subclass in which they are defined. Inheritance passes private methods to subclasses and therefore can be accessed with an implicit self. (Explains why instruction 6 works.)

I think you are looking for something closer to protected. Which behaves similarly to Java accessories that are not provided with visibility (for example, public, private, protected). Changing the personal in Spy to protect all 6 of your applications. Protected methods can be called by any instance of the defining class or their subclasses. Explicitly or implicitly called self are valid statements for protected methods if the calling object is either a class of the object that answers the call or inherits from it.

 class Person private attr_reader :weight end class Spy < Person protected attr_accessor :code public def test code #(1) OK: you can call a private method in self Spy.new.code #(2) OK: Calling protected method on another instance from same class family or a descendant. self.code #(3) OK: Calling protected method on with explicit self is allowed with protected code="xyz" #(4) Ok, it runs, but it actually creates a local variable!!! self.code="z" #(5) OK! This is the only case where explicit 'self' is ok weight #(6) OK! You can call a private method defined in a base class end end s = Spy.new s.test # succeeds s.code #(7) Error: Calling protected method outside of the class or its descendants. 

As for statement 4. You are right in considering this to avoid ambiguity. It rather protects the potential harm of a ruby โ€‹โ€‹dynamic character. This ensures that you cannot override accessors by opening the class again later. A situation that may arise, for example, by eval'ing corrupted code.

I can only guess about his design decisions that led to this behavior. For the most part, I feel that it comes down to the dynamic nature of the language.

PS If you really want to give things, the definition of java is private. Available only to the class in which it is defined, not even subclasses. You can add the self.inherited method to your classes to remove references to the methods you want to restrict access to.

Make the weight attribute inaccessible from subclasses:

 class Person private attr_reader :weight def initialize @weight = 5 end def self.inherited(subclass) subclass.send :undef_method, :weight end end class Spy < Person private attr_accessor :code public def test weight end end Person.new.send(:weight) # => 5 Spy.new.send(:weight) #=> Unhelpful undefined method error 

It might make sense to replace the undef_method call with something like this:

  def self.inherited(subclass) subclass.class_eval %{ def weight raise "Private method called from subclass. Access Denied" end } end 

This provides a much more efficient bug and the same functionality.

Submission is necessary to call private methods for other classes. It is used only to prove that everything actually works.

Which in retrospect makes private and protected worthless. If you are really serious about protecting your methods, you will have to redefine sending to block them. The following code does this based on the private_methods object:

 def send_that_blocks_private_methods(method, *args) if private_methods.include?(method.to_s) raise "Private method #{method} cannot called be called with send." else send_that_allows_private_methods(method, *args) end end alias_method :send_that_allows_private_methods, :send alias_method :send, :send_that_blocks_private_methods private :send_that_allows_private_methods 

You can specify the class_variable private_methods for which you want to block access, instead of denying access to all private methods. You can also make send private, but there is legal use of the send call from outside the object.

+30
Oct 14 '09 at 11:10
source share
โ€” -



All Articles