Private vs. protected question

From a book I’m reading:

class Person
def initialize(age)
@age = age
end

def age
@age
end

def age_difference_with(other_person)
(self.age - other_person.age).abs
end

protected :age
end

fred = Person.new(34)
chris = Person.new(25)
puts chris.age_difference_with(fred)
puts chris.age

results in:

9
:20: protected method ‘age’ called for #<Person:0x1e5f28 @age=25>
(NoMethodError)

The first ‘puts’ line printed ‘9’ on the screen, while the second
resulted
in error. This is because, like ‘private’, I cannot use a specific
receiver to call a protected method, correct?

Now I’m going to replace ‘protected’ with ‘private’ (and omit the last
line (puts chris.age) ). This would result in:


H:/Ruby/Practice/six.rb:9:in age_difference_with': private methodage’
called for
#<Person:0x19ccf68 @age=25> (NoMethodError)from
H:/Ruby/Practice/six.rb:16:in `’

Explanation from the book:
" if
age were made private, the preceding example would fail because
other_person.age would
be invalid. That’s because private makes methods accessible only by
methods of a specific
object."

From the statement, “…because private makes methods accessible only by
methods of a specific
object.”, I’m assuming that it also means ‘accessible only by methods of
a single and CURRENT object’, and that current object is ‘chris’, not
‘fred’, and that is why it resulted in error.

Am I wrong??
Help please! Thank you!!

Kaye Ng wrote in post #991911:

Explanation from the book:
" if
age were made private, the preceding example would fail because
other_person.age would
be invalid. That’s because private makes methods accessible only by
methods of a specific
object."

From the statement, “…because private makes methods accessible only by
methods of a specific
object.”, I’m assuming that it also means ‘accessible only by methods of
a single and CURRENT object’, and that current object is ‘chris’, not
‘fred’, and that is why it resulted in error.

Am I wrong??
Help please! Thank you!!

That explanation is about as clear as mud. The rule for private methods
is simple: you cannot call private methods with an explicit receiver.
Here is an example:

class Dog
def bark
puts ‘woof’
end
end

dog = Dog.new
dog.bark

–output:–
woof

In the last line, dog is the ‘receiver’. If bark() is a private method,
you can’t call it with a receiver, so that line would produce an error:

class Dog
private
def bark
puts ‘woof’
end

end

dog = Dog.new
dog.bark

–output:–
prog.rb:10:in <main>': private methodbark’ called for
#Dog:0xa1acde0 (NoMethodError)

The implications of the private method rule are a little bit tougher to
understand. How can you call bark() on an object if you are not allowed
to specify an object as the receiver? Well, in ruby if you call a
method without a receiver, then something called ‘self’ becomes the
receiver by default.

Therefore, you must understand what object is equal to self in various
parts of your code:

puts self #main

class Dog
puts self #Dog

def bark
puts self #Dog:0x8e05c10,
puts ‘woof’
end

end

dog = Dog.new
puts dog #Dog:0x8e05c10

dog.bark #woof

ruby’s notation for a dog object is the name of the class plus an id
number:

#Dog:0x8e05c10

Note what self is inside the bark method: it’s the same object that
called the method(compare the id numbers), which is dog. That means
you can call private instance methods from inside public instance
methods:

class Dog
def do_stuff
bark
end

private
def bark
puts ‘woof’
end

end

dog = Dog.new
dog.do_stuff

–output:–
woof

The code works like this: dog calls do_stuff, so inside do_stuff self
is equal to dog. Because the private method bark() is called without a
receiver, the implicit receiver is self, which in this case is equal to
dog. And that is how you call a private method on an object like dog.

The protected method rule states that you can use a receiver to call the
method–so long as whatever object is self at that time is also an
object of the same class as the receiver (or a parent or subclass).

7stud – wrote in post #991945:

The protected method rule states that you can use a receiver to call the
method–so long as whatever object is self at that time is also an
object of the same class as the receiver (or a parent or subclass).

I think we need to scratch ‘(or parent…’ off the list:

class Animal
def meth(obj)
obj.bark #self=anim whose class is Animal,
end #and Animal is a parent class of obj=dog
end

class Dog < Animal
protected
def bark
puts ‘woof’
end
end

dog = Dog.new
anim = Animal.new
anim.meth(dog)

–output:–
prog.rb:3:in meth': protected methodbark’ called for #Dog:0x9f8948c
(NoMethodError)
from prog.rb:22:in `’

Also if the protected rule was: you can use an object to call a
protected method–so long as self (at the instance the method is called)
is equal to a member of a parent class of the object–then you could do
this:

class Dog
protected
def bark
puts ‘woof’
end
end

dog = Dog.new
puts self
puts self.class
puts dog.bark

–output:–
main
Object
prog.rb:10:in <main>': protected methodbark’ called for
#Dog:0x9154d6c (NoMethodError)

Note that when dog.bark is called, self=main which is an instance of
Object, and Object is a parent class of every class–yet ruby won’t let
you do that.

In case all that was too confusing, to sum up:

private:
You can call private methods from inside public methods.

protected:
Protected methods act mostly like private methods, but in some cases you
are allowed to specify a receiver; those cases occur when self is equal
to a member of the same class as the receiver or a subclass of the
receiver. Below is a situtation where you need to specify a receiver
for a method that you want to be private otherwise:

class Dog
def initialize(lbs)
@weight = lbs
end

def equal?(other_obj)
#inside here self=spot
get_weight == other_obj.get_weight
end

protected
def get_weight
@weight
end

end

spot = Dog.new(10)
rex = Dog.new(10)

if spot.equal?(rex)
puts ‘yes’
end

–output:–
yes