Gdzie zdefiniować niestandardowe typy błędów w Rubim i / lub Railsach?


149

Czy istnieje najlepsza praktyka definiowania niestandardowych typów błędów w bibliotece Ruby (gem) lub aplikacji Ruby on Rails? Konkretnie:

  1. Gdzie strukturalnie przynależą do projektu? Oddzielny plik, wstawiony z odpowiednią definicją modułu / klasy, gdzieś indziej?
  2. Czy są jakieś konwencje, które tworzą kiedy do kiedy nie tworzyć nowy rodzaj błędu?

Różne biblioteki mają różne sposoby działania i nie zauważyłem żadnych prawdziwych wzorców. Niektóre biblioteki zawsze używają niestandardowych typów błędów, podczas gdy inne w ogóle ich nie używają; niektóre mają wszystkie błędy rozszerzające StandardError, podczas gdy inne mają zagnieżdżone hierarchie; niektóre są po prostu pustymi definicjami klas, inne mają różne sprytne sztuczki.

Aha, i tylko dlatego, że mam ochotę nazywać te „typy błędów”, jest to trochę niejednoznaczne, mam na myśli to:

class AuthenticationError < StandardError; end
class InvalidUsername < AuthenticationError; end

Odpowiedzi:


219

Za klejnoty

Wiele razy widziałem, że wyjątki definiuje się w ten sposób:

katalog_gemów / lib / nazwa_gematu / exceptions.rb

i zdefiniowane jako:

module GemName

  class AuthenticationError < StandardError; end
  class InvalidUsername < AuthenticationError; end

end

przykładem może być coś takiego w httparty

Dla Ruby on Rails

Umieść je w swoim folderze lib / w pliku o nazwie exceptions.rb, który wyglądałby mniej więcej tak:

module Exceptions
  class AuthenticationError < StandardError; end
  class InvalidUsername < AuthenticationError; end
end

i użyłbyś tego w ten sposób:

raise Exceptions::InvalidUsername

W przypadku klejnotów może być konieczne dołączenie również pliku wyjątku. Zobacz ten przykład ponownie z httparty: github.com/jnunemaker/httparty/blob/…
Jason Swett

37
Po co umieszczać je w przestrzeni nazw w Exceptionsmodule?
ABMagil

13
Myślę, że to /libmoże nie być miejsce na błędy. Są one bardzo specyficzne dla aplikacji i mam wrażenie, że kod, który wprowadzam, /libma być kodem, który można ponownie wykorzystać w innych aplikacjach.
wuliwong

1
Instrukcje Ruby on Rails nie działały u mnie - czy w typowym przypadku potrzebny jest jakiś dodatkowy krok, aby załadować ten nowy plik?
Meekohi,

1
@ABMagil wydaje mi się, że w przeciwnym razie Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define itinną opcją byłaby jedna klasa na wyjątek, jak sądzę
ryan2johnson9

25

Myślę, że aby mieć spójne pliki źródłowe w swoim projekcie, należy zdefiniować błędy w klasie, do której mogą je wrzucić i nigdzie indziej.

Pomocna może być pewna hierarchia - przestrzenie nazw są dobre w utrzymywaniu nadmiarowych ciągów znaków z dala od nazw typów - ale to bardziej kwestia gustu - nie ma potrzeby przesadzania, pod warunkiem, że w aplikacji masz co najmniej jeden niestandardowy typ wyjątku, którego używasz do rozróżnienia między „zamierzonymi” a „przypadkowymi” wyjątkami.


8
Chociaż teoretycznie masz rację, co się dzieje, gdy ten sam błąd może zostać podniesiony przez różne klasy w zupełnie różnych sytuacjach?
Alain

1
@Alain Dlaczego nie zdefiniować tych błędów używanych przez więcej niż jedną klasę w module Exceptions / Errors, ale pozostawić wszystkie inne zdefiniowane w jednej klasie, która ich używa?
Scott W.

@ScottW, w takim przypadku polegamy na tym, że programista pamięta o sprawdzeniu.
Josh Saint Jacque

22

w railsach możesz stworzyć app/errorskatalog

# app/errors/foo_error.rb
class FooError < StandardError; end

zrestartuj spring / server i powinien go odebrać


Jak mam zgłosić te wyjątki?
Nikhil Wagh

@NikhilWagh albo raise FooError, "Example message..."alboraise FooError.new("Example message...")
schpet

13

To jest stare pytanie, ale chciałem podzielić się tym, jak radzę sobie z niestandardowymi błędami w Railsach, włączając w to dołączanie komunikatów o błędach, testowanie i jak sobie z tym radzić ActiveRecord modelach.

Tworzenie błędu niestandardowego

class MyClass
  # create a custome error
  class MissingRequirement < StandardError; end

  def my_instance_method
    raise MyClass::MissingRequirement, "My error msg" unless true   
  end
end

Testowanie (minitest)

test "should raise MissingRequirement if ____ is missing"
  # should raise an error
  error = assert_raises(MyClass::MissingRequirement) {
    MyClass.new.my_instance_method
  }

  assert error.message = "My error msg"
end

Z ActiveRecord

Myślę, że warto zauważyć, że podczas pracy z ActiveRecordmodelem popularnym wzorcem jest dodanie błędu do modelu, jak opisano poniżej, aby Twoje walidacje nie powiodły się:

def MyModel < ActiveRecord::Base
  validate :code_does_not_contain_hyphens

  def code_does_not_contain_hyphens
    errors.add(:code, "cannot contain hyphens") if code.include?("-")
  end
end

Po uruchomieniu walidacji ta metoda będzie połączona z ActiveRecord ActiveRecord::RecordInvalid klasę błędów i spowoduje niepowodzenie walidacji.

Mam nadzieję że to pomoże!


9

Aby upewnić się, że automatyczne ładowanie działa zgodnie z oczekiwaniami w Railsach 4.1.10 dla wielu niestandardowych klas błędów, będziesz chciał określić oddzielne pliki dla każdego. Powinno to działać w fazie rozwoju z dynamicznym ponownym ładowaniem.

Oto jak konfiguruję błędy w ostatnim projekcie:

W lib/app_name/error/base.rb

module AppName
    module Error
        class Base < StandardError; end
    end
end

iw kolejnych błędach niestandardowych, takich jak w lib/app_name/error/bad_stuff.rb

module AppName
    module Error
        class BadStuff < ::AppName::Error::Base; end
    end
end

Powinieneś wtedy móc wywołać swoje błędy za pośrednictwem:

 raise AppName::Error::BadStuff.new("Bad stuff just happened")

A jeśli nie chcesz osobnego pliku dla każdego nowego błędu, po prostu umieść je wszystkielib/app_name/error.rb
jlhonora

Zdobycie uninitialized constant MyController::AppName. Wzywam podbicie na moim kontrolerze
Nikhil Wagh
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.