print “read manual” while each %bug;

taak.

trafił mnie dziś bardzo “fajny" błąd.

w sofcie który pisałem, piszę i się zajmuję mam taki kawałek kodu:

for my $object ( @objects ) {
next unless $self->validate_object( $object );
$self->save_object_to_database( $object );
}

kod ten pobiera z podanej listy obiekty (tak naprawdę to nie obiekty tylko struktury (hasze haszy). potem waliduje zawartość i jeśli walidacja się udała – wpisuje do bazy.

trywiał.

jednym z elementów walidacji jest podmiana pewnych wartości na wartości słownikowe.

powiedzmy, że mamy w “obiekcie" element “region" i ma on wartość “WARSZAWA". w odpowiednim słowniku sprawdzam czy wartość WARSZAWA jest dopuszczalna. jak nie – walidacja się nie udała. jak tak, zamiast stringu “WARSZAWA" wstawiam numeryczny identyfikator z bazy. np. 15.

kod który to robi:

sub validate_object {
my $self = shift;
my $object = shift;
while (my ($param, $dictionary) = each %{ $self->validation_rules }) {
next unless defined $object->{ $param };
my $object_value = $object->{ $param };
if ( $self->dictionaries->{ $dictionary }->{ $object_value } ) {
$object->{ $param } = $self->dictionaries->{ $dictionary }->{ $object_value };
next;
}
$self->log("error at validation ...");
return;
}
return 1;
}

może nie jest to najbardziej czytelne, ale po kolei:

hash $self->validation_rules ma pary klucz/wartość, gdzie klucz jest nazwą klucza (elementu) z obiektu, a wartość jest nazwą słownika którym mamy dany element walidować.

przykładowo hash ten może zawierać:

"region" => 'REGION_LIST'

reguł jest standardowo około 10.

słowniki są zwracane z metody $self->dictionaries(). zwracana struktura to hashref, mający jako klucz nazwę słownika, a jako wartość – hashref z parami – tekstowa wartość => numeryczny identyfikator.

przykładowo:

{
'REGION_LIST' => { 'WARSZAWA' => 1, 'KRAKÓW' => 2, ...},
'CATEGORIES_LIST' => { 'MOTO' => 15, 'AGD' => 21, ... },
...
}

proste.

czy widzicie błąd w kodzie funkcji validate_object() ?

nie?

podpowiem. objaw który do mnie trafił, to to, że metoda save_object_to_database() zwracała bład sql'a, który mówił, że wartość ‘WARSZAWA' nie jest prawidłowa dla pola numerycznego.

nadal nie wiecie?

otóż metoda each(). zapamiętuje ona w haszu ostatnio zwrócony element. tak aby przy następnym wywołaniu zwrócić kolejny.

co się więc stanie gdy któryś z obiektów sie nie zwaliduje?

załóżmy, że mamy 10 reguł. od 1 do 10. przy obiekcie “a" zwalidowały się reguły 1, 2, 3, 4, a przy regule 5 pojawił się błąd. został zalogowany ($self->log), metoda validate_object się skończyła pustym returnem. więc w głównej metodzie został pobrany kolejny obiekt – “b".

przy walidowaniu obiektu “b", wywołujemy each(), który zwraca którą regułę? 6! potem 7, 8, 9, 10 i na tym skończy. czyli reguły 1-5 w ogóle nie są sprawdzone. i nawet jeśli obiekt jest poprawny – wartości odpowiednich pól nie zostają zamienione na id'y. i stąd błąd przy insercie.

czemu o tym piszę?

dwa powody.

po pierwsze: może się to komuś przyda.

po drugie: może to spowoduje, że o tym nie zapomnę. i następnym razem zamiast bawić się each() użyję po prostu keys.

2 thoughts on “print “read manual” while each %bug;”

Comments are closed.