Wyjątek

Witam,

Mam tablicę id klientów: [1,3,5,7]

Dla każdego elementu tablicy wywołuję w pętli DoSomothing.new(id)

Jeśli nie ma klienta o konkretnym id rzucany jest wyjątek i aplikacja się zatrzymuje.
Jak pomimo wyjątku przejść do przetwarzania następnego klienta?

Jeśli nie ma klienta o id 3, przechodzi do id 5.
Wiem, że można zrobić odwrotnie. Najpierw sprawdzać czy dany klient istnieje a potem jeśli istnieje coś z nim zrobić.

Ale chodzi mi konkretnie o eleganckie obsłużenie błędu.
Przegląda doc na temat exception, rescue itp ale nie bardzo nadal wiem jak to zrobić.

Pozdrawiam

array.each do |item| begin DoSth.new(item) rescue #error occured end end
Ale ładniej będzie w drugą stronę, jak pisałeś.

array.each do |id| begin DoSth.new(id) rescue ActiveRecord::RecordNotFound #record not found - doing nothing:P end end
W ten sposób obsłużysz tylko ten wyjątek, o który Ci chodzi - czyli brak id w bazie.

mi osobiście podoba sie styl

def metoda tu sie cos robi .... rescue wyjatek ..... end
ale nie wiem czy w tym przykładzie podpasuje

albo:

array.each do |id| DoSth.new(id) if Client.exists?(id) end
lub

array.select {|id| Client.exists?(id)}.each do |id| DoSth.new(id) end

Generalnie nie zaleca się używania wyjątków do kontroli przepływu programu. Wyjątki powinny być czymś nieoczekiwanym. Jeżeli oczekujesz sytuacji w której klienci nie istnieją należy użyć alternatywnych metod, typu przedstawionych przez slawosza.

Jeżeli nie interesują cię brakujace idki możesz zrobić po prostu Client.all(:conditions => {:id => array})

Generalnie każde stwierdzenie generalizujące jest głupie :wink:
Walidacje w railsach najładniej się koduje właśnie za pomocą rzucających wyjątek metod AR.

Ja stosujuję >generalnie< jedynie żeby złagodzić przekaz. Mogę oczywiście inaczej. np.
Osoby stosujące Wyjątki do kontroli przepływu w językach wysokiego poziomu(za wyjątkiem pythona) powinny być siłowo reedukowane tak by ujrzały światło.
Lepiej? (dla niekumatych oczywiście żartuję)

A na serio powodów jest kilka:

  • try/catch jest 10 razy wolniejsze od if-a
  • try/catch przewija stos i zmienia zakres - w efekcie im większy program tym więcej pamięci try/catch zajmuje szczególnie w ciasnych pętlach.
  • wyjątki są nazwane tak z konkretnych powodów - powinny być uzywane jedynie do obsługi sytuacji wyjątkowych.
  • If/else jest o wiele spójniejsze w małych blokach kodu, a przy większych obsługa wyjątku jest zwykle z dala od kodu rzucającego wyjatek, dosyć konkretnie wytrącając z rytmu przy czytaniu kodu.
  • Walidacje z tego co pamiętam robi się na zasadzie errors.add i większość źródeł w tym domyślny railsyw scaffold używają wzorca:
 if record.valid? then ble ble else bla bla

więc bez wyjątków.

PS. Benchmark if vs. exceptions ładnie pokazuje różnicę 10x.

irb(main):010:0> a, b = 0, 0; c, d = 0, 0; Benchmark.bm{|r| r.report("if"){ 100000.times{ rand() < 0.5 ? b+=1 : a+=1 }}; r.report("rescue"){ 100000.times{ (raise(RuntimeError, "Zly los") if rand() >= 0.5; c+=1) rescue d+=1 } } }; p [a,b,c,d] user system total real if 0.220000 0.000000 0.220000 ( 0.273861) rescue 2.130000 0.040000 2.170000 ( 2.365966) [49990, 50010, 49998, 50002] => nil


@Tomash: gratulacje, wygrałeś kolejny internet :wink:

[code=ruby]array_of_ids = [1, 3, 5, 7]

clients = array_of_ids.map {|id| Client.find(id) rescue nil}.compact[/code]
A tak w ogóle, najlepiej zrobić to chyba tak:

[code=ruby]array_of_ids = [1, 3, 5, 7]

Client.where(id: array_of_ids) #=> pobierze tylko istniejące[/code]