Private setters can be called by self, why not getters?

class Counter
def initialize
self.count = 0
end

def increment1
self.count = count + 1
end

def increment2
self.count = self.count + 1
end

def increment3
self.count += 1
end

private

attr_accessor :count
end

counter = Counter.new
counter.increment1 rescue $!.message # => 1
counter.increment2 rescue $!.message # => “private method count' called for #<Counter:0x007f8c998c7c60 @count=1>" counter.increment3 rescue $!.message # => "private methodcount’
called
for #<Counter:0x007f8c998c7c60 @count=1>”
(counter.count = 0) rescue $!.message # => “private method `count=’
called
for #<Counter:0x007f8c998c7c60 @count=1>”

So in Counter#increment1, we can see that we can invoke Counter#count=,
even though it’s private. But in increment2, we try self.count, which
raises a NoMethodError, because we’re calling a private method with an
explicit receiver. But isn’t that what we’re doing when we say
self.count = ... ? Somehow it works for self, but we can see in the last example
that
we can’t do this from outside.

So why can we invoke private setters with self as the explicit receiver,
but not private getters? This comes up primarily because what we really
want to do is increment3, but this whole time I’ve thought it was the
setter that was causing the issue, realized last night it was the getter
(b/c I was going to recommend making these protected instead of private,
in
order to avoid this issue).

-Josh

On Sun, 31 Mar 2013 15:51:31 +0200, Josh C. [email protected]
wrote:

So why can we invoke private setters with self as the explicit receiver,

We not only can - we must. In your example, count = 0 would set the
local variable count, not call the count= method.

Whether this is good language design is anyone’s guess. This conflict
itself is a pretty natural consequence of implicit self., though.

Personally I tend to avoid public/protected/private altogether and use
the convention of prefixing private methods with _.

You have to define setters and getters in ruby more carefully, when you
intend to use them as private methods! You have to separate what is
getter and what is setter, especially, when one and the same symbol can
be used for both operations simultaneously as is the case in combined
assignment operators such as +=, -=, *=, /= …

Here is how you manage the getters and setters when setter but not
the getter should be private:


#!/usr/bin/env ruby
class Counter
attr_reader :count
attr_writer :count
private :count=

def initialize; self.count = 0; end
def show_count; puts "Count=#{count}"; end
def increment1; self.count = count + 1; end
def increment2; self.count = self.count + 1; end
def increment3; self.count += 1; rescue; end

end

o = Counter.new
o.show_count
o.increment1
o.show_count #=> Count=0
o.increment2
o.show_count #=> Count=1
o.increment3
o.show_count #=> Count=2

puts “Explicit get: Count=#{o.count}” #=> Explicit get: Count=2
o.count=1234 #=> ERROR

Indeed, if you do not care or are ignorant of oo, you can resort to
devices like the underscore as suggested above.

Regards, igor

On Sun, Mar 31, 2013 at 12:20 PM, Josh C. [email protected]
wrote:

I rarely want to expose either. In my code example, why can I call
self.count= but not self.count ?

It’s a hack, because setters require you to use “self.count=” syntax,
whereas you can just call a getter with “count”

Since there’s no way to call a private setter except “self.count=”, Ruby
permits “self” as a receiver for this even if it’s a private method.

Clearly the more consistent thing to do would be for private methods to
always permit “self” as a receiver. It’s a bit odd Ruby doesn’t allow
this,
IMO

On Sun, Mar 31, 2013 at 1:19 PM, Igor P. [email protected]
wrote:


def increment3; self.count += 1; rescue; end

puts “Explicit get: Count=#{o.count}” #=> Explicit get: Count=2
o.count=1234

Indeed, if you do not care or are ignorant of oo, you can resort to
devices like the underscore as suggested above.

I rarely want to expose either. In my code example, why can I call
self.count= but not self.count ?

Josh C. wrote in post #1103882:

I rarely want to expose either. In my code example, why can I call
self.count= but not self.count ?

As Tony said, it’s a hack! Private methods (except attribute_writers) in
Ruby can not be called on explicit receivers! That’s how private is
defined in Ruby, namely, no calls on explicuit receivers. Likwise the
combined assignment operators will not work, if you define accesors as
private. Sorry, I failed to show that clearly in my example segregating
the private setter and public getter above :frowning:

Cheers, igor

Josh C. wrote in post #1103861:

(b/c I was going to recommend making these protected instead of private,
in order to avoid this issue).

-Josh

You are right, making these protected instead of private seems to be the
best solution, unless you need to shield access to your getters and
setters from subclasses too. Taking into account, that instance
variables in Ruby are by design public, all this accessor encapsulation
is rather ‘non-ruby-ish’, nevertheless, your suggestion deserves to be
noticed!

The following code works exactly as expected, and you can even use only
the simplest form of accessor declaration {{ attr_accessor :count }} in
the private section, without bothering to separate the getter from the
setter method.


#!/usr/bin/env ruby
class Counter
def initialize; self.count = 0; end
def show_count; puts “Count=#{count}”; end
def increment1; self.count = count + 1; end
def increment2; self.count = self.count + 1; end
def increment3; self.count += 1; end
protected
attr_accessor :count
end

o = Counter.new
o.show_count #=> Count=0
o.increment1
o.show_count #=> Count=1
o.increment2
o.show_count #=> Count=2
o.increment3
o.show_count #=> Count=3

puts “Explicit get: Count=#{o.count}” #=> fails with exception
o.count=1234 #=> would properly fail too

Very good, Josh!
Regards, igor

On Mon, Apr 1, 2013 at 1:16 AM, Igor P. [email protected]
wrote:

Josh C. wrote in post #1103861:

(b/c I was going to recommend making these protected instead of private,
in order to avoid this issue).

-Josh

You are right, making these protected instead of private seems to be the
best solution, unless you need to shield access to your getters and
setters from subclasses too.

You can’t hide methods from subclasses.

Super = Class.new { private; attr_accessor :a }
Sub = Class.new Super
Sub.private_instance_methods.grep /^a=?$/ # => [:a=, :a]
Sub.instance_method(:a).owner # => Super

Taking into account, that instance
variables in Ruby are by design public, all this accessor encapsulation
is rather ‘non-ruby-ish’, nevertheless, your suggestion deserves to be
noticed!

It’s not clear to me what you mean about variables being public.

I rarely worry about my Ruby being non-ruby-ish. In this particular
case, I
want to do this because I internally want to implement my public methods
by
using private data. I know I could use instance variables, but I
frequently
change variable names, move things around, change implementations, etc.
If
I use an ivar, I start handing nil around, or setting the wrong
variable.
If I use a method, I immediately get a NoMethodError. As the Pragmatic
Programmers say, “Crash Early”. Furthermore, if I use a method, I can
encapsulate access from even my own object, which would allow for
swapping
implementations out (e.g. switching from attr_accessor to a Struct,
which
would require updating all access points if they used ivars). I know
most
Rubyists aren’t as bothered by things like this, and maybe they’re
right,
but when I disregard these concerns, I don’t feel free, I feel cavalier.

-Josh

Josh C. wrote in post #1103951:

You can’t hide methods from subclasses.

I, know you cannot hide methods from subclasses in Ruby. On my quick
initial reading of your first post in this thread, I had an impression
you wanted to do so, as it looked, by imposing your own, BTW, rather
dubious convention, to use {{self}} on all instance methods defined in a
class in which they were defined. Insisting to use {{self}}, would
indeed, prevent a subclass to access a private method of a superclass,
while calling protected or public methods in superclasses this way,
would not!


class A;def who; base;end; private;def base; puts “Class A”;end; end
class B<A;def who; self.base;end; def base;puts “B:overriden”;end; end
class C<A;def who; self.base;end; end
a = A.new
a.who #=> Class A
b = B.new
b.who #=> B:overriden
c = C.new
c.who #=> Exception NoMethodError

Hence, there is a way, to shield access to inherited methods, providing
you obey your self-imposed convention.

It’s not clear to me what you mean about variables being public.

You have access to any instance variable in any class in the inheritance
hierarchy, as long as you do not shield a variable by defining it with
the same name as one in a superclass:


class A; def initialize; @av=“A”;end; private; attr_accessor :av; end
class B<A; def private_instvar_of_super; puts “@av=#@av”; end; end
b = B.new
b.private_instvar_of_super #=> @av=A

But, you seem to know this, as you obviously know for the benefits of
using accessors, only for some reason, you would not see this
unrestricted instance variable visibility and access as public?

I rarely worry about my Ruby being non-ruby-ish. …

I never said that Ruby is non-ruby-ish! Non-ruby-ish is perhaps
someone’s programming style, and certainly, trying to make instance
variables (or as, I am sure, you’d correct me, access to them) private
in Ruby, qualifies for such a characterization.

Ruby allows you to be as free or as strict as you wish! It does not help
you too much to enforce your whims, or your own programming conventions,
though. There is absolutely nothing preventing you to accomplish the
required flexibility “to implement your public methods by using private
data” or “frequently change variable names, move things around, change
implementations, etc.” In fact I applaud you to find a way to turn all
accessor methods to behave almost as if they were private, by using
protected instead.

Also, converting classes and structures back and forth is highly
unreliable practice in Ruby, particularly because of the inconsistencies
when using accessor methods, which may fail to work in structures. If
you wish to enjoy Ruby oo paradigm, you better think twice if you really
want to use structs. But, I guess, you’d have to be the “Rubyist”, to
know this?

I do not think, your presenting yourself as a cavalier, who believes he
is entrapped in some fictitious Ruby constraints, is an accurate
description. I wish one day you’d realize, Ruby is cavalier to you
instead.

All the best, igor

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs