Де визначити власні типи помилок у Ruby та / або Rails?


149

Чи є найкраща практика визначення власних типів помилок у бібліотеці Ruby (gem) або Ruby on Rails? Конкретно:

  1. Де вони структурно належать до проекту? Окремий файл, позначений відповідним визначенням модуля / класу, десь ще?
  2. Чи існує якесь - або угода , які встановлюють , коли до і коли НЕ створити новий тип помилки?

У різних бібліотеках є різні способи роботи, і я не помітив реальних зразків. Деякі бібліотеки завжди використовують спеціальні типи помилок, а інші взагалі не використовують їх; деякі мають усі помилки, що розширюють StandardError, а інші мають вкладені ієрархії; деякі - лише порожні визначення класу, інші - всілякі хитрі хитрощі.

О, і тільки тому, що я відчуваю, що називати ці "типи помилок" - це щось неоднозначно, я маю на увазі це:

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

Відповіді:


219

Для самоцвітів

Я не раз бачив, що ви визначаєте винятки таким чином:

gem_dir / lib / gem_name / izuzetitions.rb

і визначається як:

module GemName

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

end

прикладом цього може бути щось подібне в httparty

Для Ruby on Rails

Помістіть їх у свою папку / папку під файл під назвою exceptions.rb, який виглядатиме приблизно так:

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

і ти би використовував це так:

raise Exceptions::InvalidUsername

Для дорогоцінних каменів, схоже, вам доведеться також включити файл виключень. Дивіться цей приклад ще раз з httparty: github.com/jnunemaker/httparty/blob/…
Jason Swett

37
Навіщо простір імен їх в Exceptionsмодулі?
ABMagil

13
Я думаю, що /libдля помилок може бути не місце. Вони дуже специфічні для додатків, і я відчуваю враження коду, який я ввожу/lib , мається на увазі як код, який можна повторно використовувати в інших програмах.
wuliwong

1
Інструкції Ruby on Rails для мене не спрацювали - чи потрібен якийсь додатковий крок, щоб фактично завантажити цей новий файл у типовому випадку?
Meekohi

1
@ABMagil, мабуть, я б інакше Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define itіншим варіантом був би один клас за виняток, я думаю
ryan2johnson9

25

Я думаю, що для того, щоб у вашому проекті були згуртовані вихідні файли, ви повинні визначити помилки в класі, в якому вони можуть кидати їх і ніде більше.

Деякі герархії можуть бути корисними - простори імен добре допомагають утримувати зайві рядки від імен типів - але це вже більше питання смаку - не потрібно перебирати за борт, якщо у вашому додатку є принаймні один спеціальний тип винятку, який ви використовуєте для диференціації між "навмисними" та "випадковими" винятками.


8
Хоча в теорії ви маєте рацію, що відбувається, коли однакові помилки можуть виникати різними класами в абсолютно різних ситуаціях?
Ален

1
@Alain Чому б не визначити ті помилки, які використовуються більш ніж одним класом у модулі Виключення / Помилки, а залишити всі інші визначені в одному класі, який їх використовує?
Скотт W

@ScottW, У такому випадку ми покладаємось на розробника, який не забуде перевірити.
Джош Сент-Жак

22

в рейки можна скласти app/errorsкаталог

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

перезапустіть весну / сервер, і він повинен підібрати його


Як я повинен підняти ці винятки?
Нікіл Ваг

@NikhilWagh або raise FooError, "Example message..."абоraise FooError.new("Example message...")
schpet

13

Це старе питання, але я хотів поділитися тим, як я обробляю власні помилки в Rails, включаючи приєднання повідомлень про помилки, тестування та як впоратися з цим ActiveRecordмоделями.

Створення користувацької помилки

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

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

Тестування (мінімум)

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

З ActiveRecord

Я думаю, що варто зауважити, що при роботі з ActiveRecordмоделлю популярним зразком є ​​додавання помилки до моделі, як описано нижче, щоб ваші перевірки не вдалися:

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

Після запуску перевірок цей метод повернеться до ActiveRecord::RecordInvalidкласу помилок ActiveRecord і призведе до відмови перевірок.

Сподіваюся, це допомагає!


9

Щоб автоматично завантажувати роботу, як очікувалося в Rails 4.1.10 для декількох спеціальних класів помилок, вам потрібно вказати окремі файли для кожного. Це має працювати в розробці з її динамічним перезавантаженням.

Ось як я налаштовую помилки в нещодавньому проекті:

В lib/app_name/error/base.rb

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

і в наступних користувацьких помилках, як у lib/app_name/error/bad_stuff.rb

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

Тоді ви зможете викликати свої помилки за допомогою:

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

І якщо ви не хочете окремого файлу для кожної нової помилки, просто lib/app_name/error.rb
введіть

Отримання uninitialized constant MyController::AppName. Я закликаю підняти в своєму контролері
Nikhil Wagh
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.