Accessing private member by subclassing

Hi All,

SCENARIO: I took an example from Ruby Cookbook which had a private method 'secret'. 'secret' returned the value of @secret initialized to a random value.

I created a subclass whose initializer called super and accessed secret to save its returned-value in a instance variable, which I then displayed. It matched the value I dumped when the parent class was initialized. Success!

However, I tried a alternative approach, which failed --- which is my reason for posting this.

I created a public method 'spy' in the parent class and returned the value @secret, just like the private method 'secret' did. I then invoked spy from a subclass instance, but it returned nil.

QUESTION: Why does subclass.spy return nil?

Code follows.

Thanks in Advance, Richard

=========== SecretNumber.rb ==================== # Source: Ruby Cookbook, p. 371-373

class SecretNum   def initialize     @secret=rand(20)     puts "@secret <= #{@secret}"   end

  def hint     puts "The number is #{"not " if secret<10}greater than 10"   end

  private   def secret     @secret   end

  public   def spy     @secret   end   attr_reader :spy end

=========== SecretNumber_AccessInSubclass.rb =================== # SecretNumber_AccessInSubclass.rb

require 'SecretNumber.rb'

class NotSoSecretNum < SecretNum   def initialize     super     @theNum = secret   end

  attr_reader :theNum

end

s = NotSoSecretNum.new puts "theNum reveals \"secret\" = " + s.theNum.to_s puts "secret reveals \"secret\" = " + "\"s.secret\" not accessible" puts "spy reveals \"secret\" = " + s.spy.inspect.to_s

========= Results ============== @secret <= 3 theNum reveals "secret" = 3 secret reveals "secret" = "s.secret" not accessible spy reveals "secret" = nil

Hi Alex,

Fantastic reply!

You not only told me what action to take to get my intended result, but more importantly WHY my code failed. I never really gave much thought to what attr_reader REALLY meant.

Thank you very much.

Best wishes, Richard

Hi again Alex,

After thinking further about my mistaken use of attr_reader, I wondered why I had never learned the details about it before. So I perused Hal Fulton's "The Ruby Way, Second Edition" and found, on page 391, precisely the explanation you gave me. Clearly, I have not done my homework :slight_smile:

I mention this primarily for the benefit of anybody else who might read this thread.

Best wishes, Richard

Hi Alex,

Just one more question, if I may:

Hal Fulton say "attr_reader theNum" creates theNum [an instance method, right?] and @theNum [an instance variable, right?].

However, the initialize method creates and initializes @theNum.

So, which of the following occurs?

(1) the initialize method creates/initializes @theNum first and attr_reader acknowledges @theNum's existence and restricts its creation to theNum method; or

(2) the initialize method gets scanned but not executed, so attr_reader does both creations and then the initialize method initializes the existing @theNum instance variable.

The closest thing I've found on this issue is "compile" in Pickaxe 2nd Edition, which applied to Regexp's, not compiling/interpreting Ruby source code.

Thanks in advance for any documentation you can point me to or any ideas you have on this question.

Yours truly, Richard

Hi Alex,

I responded earlier to your last post, but my response got lost in cyber-space. Here is a new response, which hopefully is better written than the lost one.

I assume you mean:

   attr_reader :theNum

Yes, I omitted the colon :frowning: You've got eagle-eyes :slight_smile:

I think all that answers your question.

It sure does. Thanks a lot. I really like to nail down these kinds of details.

I put together my list of examples based on the your ideas, which I provided below. They satisfy me that:

1) "attr_reader :foo" is equivalent to "def foo; @foo; end" (as you said originally)

I think examples 2a and b (and 4, 5 and 6, as well) demonstrate that.

2) "@foo" yields nil when it's undefined. Occam's razor suggests that a method returning @foo's value: a) returns nil when @foo is undefined (as you also said); rather than b) creates @foo with an initial nil value when @foo is undefined, and then returns @foo's nil value.

In short, I signed onto your ideas, and now proclaim them as mine, too :-).

Best wishes, Richard

===== My humble examples ==== class FooBar1 end

class FooBar2a   attr_reader :foo end

class FooBar2b   def foo     @foo   end end

class FooBar3   attr_reader :foo

  def set_foo(parm)     @foo = parm   end end

class FooBar4   def set_foo(parm)     @foo = parm   end

  attr_reader :foo end

class FooBar5   def set_foo(parm)     @foo = parm   end

  def foo     @foo   end end

class FooBar6   def set_foo(parm)     @foo = parm   end

  def foo; @foo; end end

fb1 = FooBar1.new # puts fb1.foo # ==> undefined method `foo' for #<FooBar1: ...

fb2a = FooBar2a.new puts "fb2a.foo = " + fb2a.foo.inspect # ==> nil

fb2b = FooBar2b.new puts "fb2b.foo = " + fb2b.foo.inspect # ==> nil

fb3 = FooBar3.new puts "fb3.foo = " + fb3.foo.inspect # ==> nil fb3.set_foo "some-value" puts "fb3.foo = " + fb3.foo # ==> some-value

fb4 = FooBar4.new fb4.set_foo "another-value" puts "fb4.foo = " + fb4.foo # ==> another-value

fb5 = FooBar5.new fb5.set_foo "a third value" puts "fb5.foo = " + fb5.foo # ==> a third value

fb6 = FooBar6.new fb6.set_foo "a fourth value" puts "fb6.foo = " + fb6.foo # ==> a fourth value

Hi Alex,

I got distracted by our snow-storm for a few days.

This way you can tell if variable has been not been initialized, or if

has been initialized and was specifically set to nil.

That's excellent!!

Thank you very much for educating me in Ruby.

Yours very truly, Richard