Today I came across something very odd...to me at least. My app was behaving strangely. The user id being stored in the database wasn''t correct. No matter what user I logged in with, the id in the database would show the same number. Now I''m not a superstitous guy but the number 4 repeating over an over seemed like a bad omen. Memories of the last bug which took almost a day to find started coming back to me like a bad dream. Shrugging off the looming dread, I started hunting for all the places the user id was being set. The first pass through the app didn''t turn up anything suspicous. The second time around, I took a double take as my eyes scanned past the @session[:user] in the Account controller. I remembered reading something about the new version of the Account controller using :user instead of ''user''. GOTCHA! I''m still using the old version in this app. But still why would the database store the user id as 4? On a lark, I fire up the IRB and type ''puts nil.id'' 4 Son-of-a-Nil!!! WTF is going on here? A nil has an id? And why is it 4? My mind''s racing: Is it because Ruby is Japanese and 4 in Japanese is ''shi'' which means death and nil could be considered a synonym for death? Or have I discovered some terrible forbidden secret buried deep in the bowels of Ruby? Or... Maybe I just need to go out for a walk. Seriously though, why would the nil object have an id? and why would the id be 4? Doesn''t this have the potential of causing very insidious bugs? Imagine that you''re expecting an object and instead get a nil. And just like I did, you use the id of that object for something. Instead of throwing it''s arms up in protest, Ruby slyly returns back the number 4. I''m pretty much a newbie to both Rails and Ruby so it''s very likely I''m missing something very fundamental. I''m hoping someone more knowledgeable will enlighten me. Hammed
H M wrote:> Today I came across something very odd...to me at least. > [...] > Seriously though, why would the nil object have an id? and why would > the id be 4? > [...]Great story, Hammed! That''s troubleshooting for you. Every Ruby object has an id method. Active Record overrides it to provide the record''s primary key rather than its object id and, yes, this has high potential for error. Happily, Ruby has deprecated id in favor of object_id so the confusion will evaporate in the not-too-distant future. Active Record will have the id method all to itself again. In the meantime, know thee nil.id =4 and know it well. Best, jeremy
H M wrote:> Seriously though, why would the nil object have an id? and why would > the id be 4?All objects have object_ids. You can do [].id, 5.id, nil.id, true.id, false.id, {1 => 2}.id, Object.id and so on and they will all return something. Ruby is moving away from using .id to get the object id -- in the future this will be done by .object_id which is a good thing as .id tends to get used by libraries and applications. For now you can protect yourself against bugs involving nil instead of database records by sticking the following code into your environment.rb: def nil.id() raise(NoMethodError, "Called .id on nil.") end
> Every Ruby object has an id method. Active Record overrides it to > provide the record''s primary key rather than its object id and, yes, > this has high potential for error.Thanks for the explanation Jeremy. I''m not sure I understand completely though. I know if I run script/console, it runs in the context of the application but doesn''t irb run in it''s own context? How would IRB know about ActiveRecord. cheers Hammed
It doesn''t know about ActiveRecord. As Jeremy said, each Ruby object has an id method, AR just overrides it for all AR objects. Check out http://www.rubycentral.com/book/tut_classes.html and go to the section on overriding to_s to understand overriding methods. On 4/23/05, H M <airmalik-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > Every Ruby object has an id method. Active Record overrides it to > > provide the record''s primary key rather than its object id and, yes, > > this has high potential for error. > > Thanks for the explanation Jeremy. I''m not sure I understand > completely though. I know if I run script/console, it runs in the > context of the application but doesn''t irb run in it''s own context? > How would IRB know about ActiveRecord. > > cheers > > Hammed > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
Never mind. I understand it now :) Thanks Hammed
> For now you can protect yourself against bugs involving nil instead of > database records by sticking the following code into your environment.rb: > > def nil.id() > raise(NoMethodError, "Called .id on nil.") > endI also had problems with the nil.id == 4 issue. I worked around with only using object[''id''] instead of object.id for AR-objects all the time. This way I automatically get a NoMethodError when unintentionally using nil instead of a valid AR-object.
Florian Groß wrote:> H M wrote: > >> Seriously though, why would the nil object have an id? and why would >> the id be 4? > > > All objects have object_ids. You can do [].id, 5.id, nil.id, true.id, > false.id, {1 => 2}.id, Object.id and so on and they will all return > something. > > Ruby is moving away from using .id to get the object id -- in the future > this will be done by .object_id which is a good thing as .id tends to > get used by libraries and applications. > > For now you can protect yourself against bugs involving nil instead of > database records by sticking the following code into your environment.rb: > > def nil.id() > raise(NoMethodError, "Called .id on nil.") > endOr shorter: class NilClass; undef id; end