4 ms·
I’ve only glanced quickly at the article, but I think the author’s understanding of private in Ruby is wrong. class Parent attr_accessor :x pri
by stouset 3y ago
I’ve only glanced quickly at the article, but I think the author’s understanding of private in Ruby is wrong.
class Parent
attr_accessor :x
private :x
def initialize(x)
self.x = x
# └── (1) allowed
end
def ==(other)
self.x == other.x
# │ └── (2) not allowed
# └── (1) allowed
end
end
This is not doing what the author thinks it is doing. Or, it is, but I don’t think the authors understand why. `private :x` only marks the x method as private, but `x=` is a different method.
They would see different behavior with
private :x
private :x=
In general, public, private, and protected do the following. Public methods can be called with any receiver: explicit (x.foo()), self (self.foo()), or implicit (foo()). Protected methods can be invoked on self or with an implicit receiver. Private methods can only be invoked on an implicit receiver. Note that this has nothing to do with the class hierarchy. A private method can be called from a subclass as long as it has an implicit receiver.
The difference between protected and private is not really practically useful, so I personally use them to signal different levels of “private-ness”. I use protected methods to implement a class’ “internal API”, which is the core thing the public API is abstracting and exposing. I use private methods for implementing ancillary details that aren’t really core to what the class does but handle some one-off unrelated data manipulation or munging. But that’s just me.
I’ll also admit that something may have changed with Ruby’s rules around these keywords around 3.0 that I missed, so if that’s the case I apologize.
- jez 3y agoSorry, that’s simply a typo translating from Java to Ruby. I believe the point that section is trying to make still holds after editing it to say “private :x, :x=” > Private methods can only be invoked on an implicit receiver. This changed in Ruby 2.7. You can now include the self keyword, which is important because it allows calling methods that share names with Ruby keywords. “begin” is a keyword but “self.begin” is a call to a method named begin. So if that method happens to be private, you can call it now in Ruby 2.7 without needing “send(:begin)” Other than the typo (thanks! I’ll fix that when I’m back at my computer), I think that the rest of the post underscores what you’ve already pointed out about how private works, modulo the misunderstanding about the changes in Ruby 2.7
- stouset 3y agoAha! Glad I caveated things by saying I may have missed a change in semantics. I’m surprised they did that in a minor release and not a major one. So, then, is there no actual difference between private and protected now?
- jez 3y agoThe difference between private and protected is a bit subtle so I’ll defer to the Ruby FAQ: https://www.ruby-lang.org/en/documentation/faq/7/ https://www.ruby-lang.org/en/documentation/faq/7/ (On this page there’s a section called “What’s the difference between private and protected?” which explains it.) It’s interesting in its own right, and it’s in my backlog to blog about because I don’t like any of the existing resources contrasting the two.
- tsimionescu 3y agoFrom the examples in this FAQ, it seems like protected works a bit more like Java's private - you can use it only inside the class, but for any object of that class, not just self, right? (with the exception that it applies to subclasses) So Ruby's private only allows you to interact with other methods on the current object. Protected allows objects of the same class to send each other the message, but not other classes. Would this be a fair summary?
- arthurcolle 3y agoRuby private literally does nothing. It's a marker with some side effects, but isn't actually protecting anything. You can trivially do obj.send(:private_method) for any method, private or not. It's been about a year since I last looked at Ruby code (a true fave, holds it down, easy to love), but I'm pretty sure that's the idea. In Ruby, the private keyword is more about controlling the visibility of methods rather than providing strict access control. It's a part of Ruby's philosophy of "convention over enforcement." While it does mark methods as private, it doesn't prevent you from calling them using .send. This is a design choice in Ruby to prioritize flexibility and programmer discretion. Here's a bit more detail on how it works: Method Visibility: When you declare a method as private, you're essentially saying that it should only be used within the class where it's defined (or its subclasses). This is a way to indicate the intended usage of a method. No Strict Access Control: Unlike some other languages, Ruby doesn't enforce strict access control. It trusts the developer to follow conventions. However, this doesn't mean you should casually break encapsulation. It's a matter of design and discipline. send and public_send: The send method allows you to invoke any method, including private ones, on an object. It's a powerful feature that can be handy in some situations but should be used with caution. There's also public_send, which respects the visibility rules and won't invoke private methods. Here's an example: class MyClass def public_method puts "This is a public method" end private def private_method puts "This is a private method" end end obj = MyClass.new obj.public_method # works, prints the string obj.private_method # fails, no method irb(main):014:0> obj = MyClass.new => #<MyClass:0x00000001368fd6d0> irb(main):015:0> obj.public_method This is a public method => nil irb(main):016:0> obj.private_method Traceback (most recent call last): 4: from /usr/bin/irb:23:in `<main>' 3: from /usr/bin/irb:23:in `load' 2: from /Library/Ruby/Gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>' 1: from (irb):16 NoMethodError (private method `private_method' called for #<MyClass:0x00000001368fd6d0>) Did you mean? private_methods irb(main):017:0> obj.send(:private_method) This is a private method => nil irb(main):018:0>
- byroot 3y ago> Protected methods can be invoked on self or with an implicit receiver. No, protected method can be called on self or from other instances of the same class. Typically used for implementing `==` methods. > Private methods can only be invoked on an implicit receiver. That used to be true, but as others mentioned is no longer true as of Ruby 2.7.