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 --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Richard wrote:> def spy > @secret > end > attr_reader :spy > > puts "spy reveals \"secret\" = " + s.spy.inspect.to_s > #=> nilThe problem looks like its with attr_reader. attr_reader creates that accesses an instance variable. The instance variable and the method have the same name. So basically, what you are doing is this: #the method you wrote def spy @secret end #what attr_reader does for you def spy @spy end Now you have 2 methods named "spy". When the second method called "spy" is declared, the first one is overriden, it''s gone. And since @spy has not been set to anything it just returns nil. So get rid of: attr_reader :spy and it should work fine. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
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 On Feb 11, 7:14 pm, Alex Wayne <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Richard wrote: > > def spy > > @secret > > end > > attr_reader :spy > > > puts "spy reveals \"secret\" = " + s.spy.inspect.to_s > > #=> nil > > The problem looks like its with attr_reader. attr_reader creates that > accesses an instance variable. The instance variable and the method > have the same name. So basically, what you are doing is this: > > #the method you wrote > def spy > @secret > end > > #what attr_reader does for you > def spy > @spy > end > > Now you have 2 methods named "spy". When the second method called "spy" > is declared, the first one is overriden, it''s gone. And since @spy has > not been set to anything it just returns nil. > > So get rid of: > > attr_reader :spy > > and it should work fine. > > -- > Posted viahttp://www.ruby-forum.com/.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
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 :-) I mention this primarily for the benefit of anybody else who might read this thread. Best wishes, Richard On Feb 11, 7:14 pm, Alex Wayne <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Richard wrote: > > def spy > > @secret > > end > > attr_reader :spy > > > puts "spy reveals \"secret\" = " + s.spy.inspect.to_s > > #=> nil > > The problem looks like its with attr_reader. attr_reader creates that > accesses an instance variable. The instance variable and the method > have the same name. So basically, what you are doing is this: > > #the method you wrote > def spy > @secret > end > > #what attr_reader does for you > def spy > @spy > end > > Now you have 2 methods named "spy". When the second method called "spy" > is declared, the first one is overriden, it''s gone. And since @spy has > not been set to anything it just returns nil. > > So get rid of: > > attr_reader :spy > > and it should work fine. > > -- > Posted viahttp://www.ruby-forum.com/.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
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 On Feb 11, 7:14 pm, Alex Wayne <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Richard wrote: > > def spy > > @secret > > end > > attr_reader :spy > > > puts "spy reveals \"secret\" = " + s.spy.inspect.to_s > > #=> nil > > The problem looks like its with attr_reader. attr_reader creates that > accesses an instance variable. The instance variable and the method > have the same name. So basically, what you are doing is this: > > #the method you wrote > def spy > @secret > end > > #what attr_reader does for you > def spy > @spy > end > > Now you have 2 methods named "spy". When the second method called "spy" > is declared, the first one is overriden, it''s gone. And since @spy has > not been set to anything it just returns nil. > > So get rid of: > > attr_reader :spy > > and it should work fine. > > -- > Posted viahttp://www.ruby-forum.com/.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Richard wrote:> 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?].I assume you mean: attr_reader :theNum> 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; orattr_reader reads only, not creates.> (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.You''re getting hung up on who creates what and has access to who and it''s much simpler than that. Take this class: class Foo attr_reader :bar def initialize(value) @bar = value end end foo = Foo.new(''abc'') foo.bar #=> "abc" When you call "Foo.new(''abc'')" you are invoking the initialize method, this sets the @bar variable. When you call "bar" you just retrieve whatever the valid of @bar is at that moment. And attr_reader gives you an instance method, meaning it''s only on instances of class Foo created with: Foo.new(''abc''). And in this example you cannot instantiate the Foo class without running the initialize method that sets up @bar. So the "bar" method doesn''t acknowledge or restrict anything. It simply fetches whats in @bar whether it''s there or not. As a result it doesn''t care what else is going in your class at all. you can have a class like this and it will work fine, except always return nil. class Bar attr_reader :foo end bar = Bar.new bar.foo #=> nil The final thing to keep in mind is that instance variable like @bar are the only kind of variable that will never give you undefined or uninitialized errors. So if it''s not initialized it will just give you nil, unlike class of or local variables that will raise an exception: irb(main):001:0> foo NameError: undefined local variable or method `foo'' for main:Object from (irb):1 from :0 irb(main):002:0> @foo => nil irb(main):003:0> @@foo NameError: uninitialized class variable @@foo in Object from (irb):3 from :0 One last example: class Bam attr_reader :foo def set_foo @foo = ''abc123'' end end bam = Bam.new bam.foo #=> nil bam.set_foo bam.foo #=> "abc123" I think all that answers your question. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
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 :theNumYes, I omitted the colon :-( You''ve got eagle-eyes :-)> 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 On Feb 12, 12:32 pm, Alex Wayne <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Richard wrote: > > 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?]. > > I assume you mean: > > attr_reader :theNum > > > 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 > > attr_reader reads only, not creates. > > > (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. > > You''re getting hung up on who creates what and has access to who and > it''s much simpler than that. Take this class: > > class Foo > attr_reader :bar > > def initialize(value) > @bar = value > end > end > > foo = Foo.new(''abc'') > foo.bar #=> "abc" > > When you call "Foo.new(''abc'')" you are invoking the initialize method, > this sets the @bar variable. When you call "bar" you just retrieve > whatever the valid of @bar is at that moment. > > And attr_reader gives you an instance method, meaning it''s only on > instances of class Foo created with: Foo.new(''abc''). And in this > example you cannot instantiate the Foo class without running the > initialize method that sets up @bar. > > So the "bar" method doesn''t acknowledge or restrict anything. It simply > fetches whats in @bar whether it''s there or not. As a result it doesn''t > care what else is going in your class at all. you can have a class like > this and it will work fine, except always return nil. > > class Bar > attr_reader :foo > end > > bar = Bar.new > bar.foo #=> nil > > The final thing to keep in mind is that instance variable like @bar are > the only kind of variable that will never give you undefined or > uninitialized errors. So if it''s not initialized it will just give you > nil, unlike class of or local variables that will raise an exception: > > irb(main):001:0> foo > NameError: undefined local variable or method `foo'' for main:Object > from (irb):1 > from :0 > irb(main):002:0> @foo > => nil > irb(main):003:0> @@foo > NameError: uninitialized class variable @@foo in Object > from (irb):3 > from :0 > > One last example: > > class Bam > attr_reader :foo > > def set_foo > @foo = ''abc123'' > end > end > > bam = Bam.new > bam.foo #=> nil > bam.set_foo > bam.foo #=> "abc123" > > I think all that answers your question. > > -- > Posted viahttp://www.ruby-forum.com/.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Richard wrote:> In short, I signed onto your ideas, and now proclaim them as mine, > too :-).As long as we are being thorough, one last example that proves nil and undefined are not the same thing when it comes to instance variables. class Foo attr_reader :bar def set_bar(value) @bar = value end def bar_exists? defined? @bar end end foo = Foo.new foo.bar #=> nil foo.bar_exists? #=> nil foo.set_bar("abc") foo.bar #=> "abc" foo.bar_exists? #=> "instance-variable" foo.set_bar(nil) foo.bar #=> nil foo.bar_exists? #=> "instance-variable" This way you can tell if variable has been not been initialized, or if has been initialized and was specifically set to nil. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
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 ifhas been initialized and was specifically set to nil. That''s excellent!! Thank you very much for educating me in Ruby. Yours very truly, Richard On Feb 12, 8:20 pm, Alex Wayne <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Richard wrote: > > In short, I signed onto your ideas, and now proclaim them as mine, > > too :-). > > As long as we are being thorough, one last example that proves nil and > undefined are not the same thing when it comes to instance variables. > > class Foo > attr_reader :bar > > def set_bar(value) > @bar = value > end > > def bar_exists? > defined? @bar > end > end > > foo = Foo.new > > foo.bar #=> nil > foo.bar_exists? #=> nil > foo.set_bar("abc") > > foo.bar #=> "abc" > foo.bar_exists? #=> "instance-variable" > > foo.set_bar(nil) > foo.bar #=> nil > foo.bar_exists? #=> "instance-variable" > > This way you can tell if variable has been not been initialized, or if > has been initialized and was specifically set to nil. > > -- > Posted viahttp://www.ruby-forum.com/.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---