Witaj na blogu prowadzonym przez Sebana. Spisuję tutaj swoje uwagi na różny temat. Przeważają tematy związane z Internetem, popieranymi przeze mnie rozwiązaniami dotyczącymi wykorzystania komputerów, oraz kilka innych. Przeczytasz tu również recenzje książek IT.
Whiny nils w Rails
09 kwietnia 2008 | Klucze:
Ogólne,
programowanie,
rails,
ruby,
Techblog
3 komentarze. trackback
Czy zdarzyło wam się szukać błędu bardzo, bardzo długo, a na końcu krzyknąć ,,Eureka!''? Mi się w tym tygodniu zdarzyło. Dwa dni szukałem błędu w aplikacji, którą piszę w pracy. Sytuacja była o tyle dziwna, że testy wcale nie powodowały powstania tego błędu ...
Błąd pojawiał się w takiej linii:
item.unit_id = Unit.find_by_name(some_name).id rescue Unit.create!(:name => some_name)Abstrahując już od innych rzeczy w tym kodzie, szczególną uwagę należy zwrócić na rescue. W środowiskach development i test ten kod działał poprawnie. Po uruchomieniu aplikacji na środowisku ,,wstępnie produkcyjnym'' przestawał działać, pięknie rzucając wyjątkiem z poziomu MySQL o błędnym indeksie. Dopisałem sporo testów, ale to ciągle działa. Przejrzałem logi z maszyny produkcyjnej. Napisałem test w identycznymi parametrami jak request wyzwalający błąd, a to mi działa. Porównałem dla pewności jeszcze wersje gemów, nawet MySQL (tak wiem chore, ale tonący brzytwy się chwyta). Dziś rano okazało się, że wszystkiemu winne są whiny nils no i ja po części również. Poniżej przykład działania tej opcji konfigurującej środowisko.
# config.whiny_nils = true >> Unit.find_by_name("bad name") => nil >> Unit.find_by_name("bad name").id RuntimeError: Called id for nil, which would mistakenly be 4 >> Unit.find_by_name("bad name").id rescue "tworzę nowy" => "tworzę nowy"
# config.whiny_nils = false >> Unit.find_by_name("bad name") => nil >> Unit.find_by_name("bad name").id (irb):2: warning: Object#id will be deprecated; use Object#object_id => 4 >> Unit.find_by_name("bad name").id rescue "tworzę nowy?" (irb):3: warning: Object#id will be deprecated; use Object#object_idCzujecie różnicę? W drugim przypadku aplikacja nie miała nawet okazji stworzyć brakującego rekordu w tabeli Units bo nigdy nie mogło tam dojść do wyjątku, tylko ostrzeżenie (które nota bene też nie pojawiało się w logu :(). Jeśli jeszcze nie to spróbujcie kilka razy w konsoli irb nil.id lub nil.object_id. Wynik łatwy do przewidzenia ;-).
Co w tym wszystkim mnie zastanawia najbardziej? Czy domyślna konfiguracja środowisk development i test narzekająca whiny_nils na nil.id jest prawidłowa? Niby zachowanie prawidłowe bo Rails krzyczy do programisty o złym użyciu metody id, ale z drugiej strony ciekaw jestem czy wielu przechwytuje ten wyjątek i w jakiś sposób go obsługuje, nawet nieświadomie. I czemu w środowisku production ta opcja jest wyłączona? A dla Was, które ustawienie wydaje się ciekawe? Wolicie wyjątek czy ostrzeżenie?
KOMENTARZE
11 kwietnia 2008 | Radarek |
I tak i nie. Zauważ, że metodę „id” posiada każdy obiekt w Rubym (Object#id), dlatego ciężko żeby przy whiny_nils=false railsy wchodziły Ci w drogę gdy wywołujesz metodę „id” na nilu (to też przecież obiekt). W Rubym 1.9 nie będzie już tej metody (zostaje tylko „object_id”), więc prawdopodobnie wyłapałbyś ten błąd.
Ostrzeżenie (w 2 przypadku) nie mogło się pojawić w logach bo pochodzi od interpretera Rubiego a nie od samych Railsów. Pojawiło się prawdopodobnie na konsoli serwera lub w jego logach.
11 kwietnia 2008 | Radarek |
Jeszcze gwoli ścisłości:
$ irb
>> nil.id
(irb):1: warning: Object#id will be deprecated; use Object#object_id
=> 4
12 kwietnia 2008 | Seban |
Radarku wiem o Object#id i Object#object_id. Największe moje zdziwienie wywołała różnica pomiędzy konfiguracjami środowisk development, test, a production.
