Inspect violates encapsulation?

I have defined a class MyClass with an instance variable x. Have
declared:

attr_reader :x
private :x

Suppose anObject is an object of the class MyClass.
The statement

anObject.inspect —> #<MyClass:oxa10280 @x=100>

while

anObject.x —> compile error, as expected

My question is: what is the point of declaring x to be private if it’s
value can be accessed via the inspect method outside the class? Doesn’t
that violate encapsulation?

Thanks in advance!

Jay S. wrote in post #1009858:

My question is: what is the point of declaring x to be private if it’s
value can be accessed via the inspect method outside the class? Doesn’t
that violate encapsulation?

(1) @x is an instance variable; x is a method. They are two completely
different things. Declaring x to be private makes no difference to the
visibility of @x.

(2) Ruby is completely dynamic and you can always get around any access
controls.

anobject.send(:x) # call a private method
anobject.instance_variable_get(:@x) # look at value
anobject.instance_eval { @x } # another way

Note that @x is directly accessible by anyone, and not just via the
‘inspect’ method. Hence encapsulation is an entirely advisory concept in
Ruby.

(3) ‘inspect’ is a debugging tool. Pragmatically, it’s useful to be able
to see what’s different between two instances of an object. I find it
annoying that repr() in python doesn’t do this.

(4) At the end of the day, you can always override ‘inspect’ for any
particular class if you don’t like the default behaviour.

class Foo
def inspect
“Naff off!”
end
end

HTH,

Brian.

I think my first statement threw you off by my neglecting to include the
“@”. Here is bare-bones program. Note the @aVariable and
@anotherVariable are instance variables, NOT methods. The question is:
what is the point of declaring @aVariable private (in terms of
encapsulation) if it can be accessed via the inspect method?

class MyClass
attr_reader :aVariable, :anotherVariable

private :aVariable

def initialize(aVariable, anotherVariable)
@aVariable = aVariable
@anotherVariable = anotherVariable
end

end

anObject = MyClass.new(10,‘A string’)
puts anObject.inspect() # reveals values of both instance
puts anObject.anotherVariable #–> this is OK
puts anObject.aVariable #–> no good
private

Jay S. wrote in post #1009987:

The question is:
what is the point of declaring @aVariable private (in terms of
encapsulation) if it can be accessed via the inspect method?

Because all ruby instance variables are private anyway, you
can’t declare them to be private.

private :aVariable

A variable called aVariable is not the same as a variable called
@aVariable.

You need to do some more reading about what the private() method does
because you aren’t even close. But even if you eventually learn what
the private() method does, it won’t change the fact that it is possible
to violate encapsulation in ruby. People who like that state of affairs
will argue that a programmer can either respect the encapsulation that
ruby provides, or if they want to they can take the steps necessary to
violate it, which makes
things more flexible for them. They will argue that absolute privacy is
a hinderance to their programming feng shui, and that they should be
able to violate encapsulation if they decide that course of action is in
their best
interests.

Well, I was hoping for an explanation, not a putdown because I am a
newbie. (Is this the wrong forum?) Below is a class def directly
excerpted directly from Ruby Visual Quickstart. My understanding is that
:balance is a symbol for the instance variable @balance. Could someone
explain what the statement

protected :balance

is doing then? Since :balance isn’t a method in the class CreditCard2, I
was under the impression that it was a symbol for @balance, and
therefore the statement was declaring the instance variable @balance to
be protected. Evidently my understanding is faulty. Could someone clear
up my confusion please?

Thanks again.

class CreditCard2

PI = 3.141; # a class constant

Class instance variable:

@total_limit = 0

attr_reader :balance, :limit
protected :balance
def initialize(limit, bal = 0)
@limit = limit
@balance = bal
end

Returns the total limit: a class method

def self.total_limit
@total_limit
end

Adds to the total limit: a class method

def self.new(limit, bal = 0)
@total_limit += limit
super # allows objects to include instance variables @limit and
@balance
end

def charge(amt)
debit(amt)
end
def payment(amt)
credit(amt)
end
def available
@limit - @balance
end
def >(rh)
return nil unless rh.respond_to?(‘balance’)
@balance > rh.balance
end
private
def debit(amt)
@balance += amt
end
def credit(amt)
@balance -= amt
end
end # End of CreditCard2 class.

Jay S. wrote in post #1010005:

Apparently

attr_reader :balance, :limit
protected :balance

is referring to the getter method for the instance variable @balance so
that

protected :balance

is declaring the getter method to be private. Is this the correct
interpretation? (The book I referred to is unclear about this point.)

Yes and no. It declares a method named ‘balance’ to be protected.
‘protected’ is not the same as ‘private’. And the protected() method
knows nothing about attr_reader() or what it is doing.

Don’t get hung up on protected() and private(). You will do fine if you
put them aside and consider them to be an intermediate to advanced
topic. Perhaps the most important lesson is that you can’t call a
private method explicitly like this:

obj.private_meth()

A private method cannot be called with a “receiver”; it has to be called
like this:

private_meth()

The invisible receiver is whatever object self is equal to.

Thanks for your patience! I meant “protected” not “private”. That
attr_reader and “protected” are independent is an interesting point that
I need to ponder. The 2 reference books that I use (the other is The
Book of Ruby) do not clarify this (at least to me). I am still slightly
confused about part of the rest of your response. I gather that the
method “balance” declared in the attr_reader and in the protected
statement are one and the same: the getter method for the @balance
instance variable. Correct?

Apparently

attr_reader :balance, :limit
protected :balance

is referring to the getter method for the instance variable @balance so
that

protected :balance

is declaring the getter method to be private. Is this the correct
interpretation? (The book I referred to is unclear about this point.)

Jay S. wrote in post #1010009:

Thanks for your patience! I meant “protected” not “private”. That
attr_reader and “protected” are independent is an interesting point that
I need to ponder. The 2 reference books that I use (the other is The
Book of Ruby) do not clarify this (at least to me).

Yes. attr_reader :balance is shorthand for writing:

def balance
@balance #same as ‘return @balance
end

And attr_accessor :balance is shorthand for writing:

def balance
@balance #same as ‘return @balance
end

def balance=(val)
@balance = val
end

Sometimes, I think it is a waste of time for ruby books to introduce the
attr_accessor family of methods because they mostly confuse. It would
be better for beginners to have to write out their setter and getter
methods by hand. But then consider if you wanted getter and setter
methods for 30 different instance variables? Only after having written
those all out by hand would you see the benefit of being able to write
this instead:

attr_accessor :a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k …

I am still slightly
confused about part of the rest of your response. I gather that the
method “balance” declared in the attr_reader and in the protected
statement are one and the same: the getter method for the @balance
instance variable. Correct?

Yes, but attr_reader and protected() are not some kind of siamese
pairing. Each one is independent of the other, and they do different
things. For instance, protected() is not shorthand for anything.

Then to
understand how private and protected methods are different from regular
methods, you have to learn more about ‘self’. For instance, when you
write this:

def greet
puts ‘hello’
end

greet

–output:–
hello

That method actually becomes a private method. See how you call it
without a receiver? Private methods cannot be called with a receiver,
and since all methods have to have a receiver (in ruby speak: an
object which receives the :greet message), self becomes the invisible
receiver. self is a ruby variable name that ruby assigns objects to
depending on where you are in your code. The self variable is
constantly changing, and if you call a method without a receiver,
whatever object self is equal to at that moment becomes the receiver.

The ‘receiver’ language is ruby speak. You can translate the above to
mean: "all methods must be called by an object. If I don’t specify an
object when calling a method, then ruby automatically calls the method
with self. Same thing.

Excellent. I have a much better understanding now. I want to get hold of
a copy of Mats’ book which I expect will have more precise language. My
problem (and I doubt I am alone as an independent learner) is that the
“beginner’s books” focus on usage without much meat in terms of the how
and the why - quite unlike your clear explanations and comments. Thanks
again for your help.

Jay S. wrote in post #1009858:

Doesn’t that violate encapsulation?

Ruby is a hacker’s language, deliberately different to the likes of C++.

It is a little strong to say it ‘violates’ encapsulation but certainly
it takes a cavalier attitude to it.