Nơi xác định các loại lỗi tùy chỉnh trong Ruby và / hoặc Rails?


149

Có cách thực hành tốt nhất để xác định các loại lỗi tùy chỉnh trong thư viện Ruby (gem) hoặc ứng dụng Ruby on Rails không? Đặc biệt:

  1. Họ thuộc về cấu trúc nào trong dự án? Một tệp riêng biệt, được gắn với định nghĩa mô-đun / lớp có liên quan, ở một nơi khác?
  2. Có bất kỳ công ước đó thành lập khi đến và khi không để tạo ra một loại lỗi mới?

Các thư viện khác nhau có cách làm việc khác nhau và tôi không nhận thấy bất kỳ mô hình thực tế nào. Một số thư viện luôn sử dụng các loại lỗi tùy chỉnh trong khi những thư viện khác hoàn toàn không sử dụng chúng; một số có tất cả các lỗi mở rộng StandardError trong khi một số khác có cấu trúc phân cấp lồng nhau; một số chỉ là định nghĩa lớp trống, một số khác có đủ loại mánh khóe thông minh.

Ồ, và chỉ vì tôi cảm thấy việc gọi những "loại lỗi" này là mơ hồ, ý tôi là thế này:

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

Câu trả lời:


219

Dành cho đá quý

Tôi đã thấy nhiều lần bạn xác định ngoại lệ theo cách này:

gem_dir / lib / gem_name / exceptions.rb

và được định nghĩa là:

module GemName

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

end

một ví dụ về điều này sẽ là một cái gì đó như thế này trong httparty

Dành cho Ruby trên đường ray

Đặt chúng trong thư mục lib / của bạn dưới một tệp có tên exceptions.rb, trông sẽ giống như thế này:

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

và bạn sẽ sử dụng nó như thế này:

raise Exceptions::InvalidUsername

Đối với đá quý, có vẻ như bạn cũng phải bao gồm tệp ngoại lệ. Xem ví dụ này, một lần nữa từ httparty: github.com/jnunemaker/httparty/blob/iêu
Jason Swett

37
Tại sao không gian tên chúng vào Exceptionsmô-đun?
ABMagil

13
Tôi nghĩ rằng đó /libcó thể không phải là nơi cho lỗi. Chúng rất đặc trưng cho ứng dụng và tôi có ấn tượng rằng mã mà tôi đang đặt /libcó nghĩa là mã có thể được sử dụng lại trong các ứng dụng khác.
wuliwong

1
Các hướng dẫn của Ruby on Rails không hiệu quả với tôi - có một số bước bổ sung cần thiết để thực sự tải tệp mới này trong trường hợp điển hình không?
Meekohi

1
@ABMagil dường như tôi phải Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define itlựa chọn khác là một lớp cho mỗi ngoại lệ tôi nghĩ
ryan2johnson9

25

Tôi nghĩ rằng để có các tệp nguồn gắn kết trong dự án của bạn, bạn nên xác định các lỗi trong lớp có thể ném chúng và không ở đâu khác.

Một số quyền thừa kế có thể hữu ích - không gian tên rất tốt trong việc giữ các chuỗi dư thừa khỏi tên loại - nhưng đó là vấn đề của hương vị - không cần phải quá nhiệt tình với điều kiện bạn phải có ít nhất một loại ngoại lệ tùy chỉnh trong ứng dụng mà bạn sử dụng trong suốt để phân biệt giữa các trường hợp ngoại lệ 'cố ý' và 'tình cờ'.


8
Trong khi về lý thuyết bạn đúng, điều gì xảy ra khi cùng một lỗi có thể được đưa ra bởi các lớp khác nhau trong các tình huống hoàn toàn khác nhau?
Alain

1
@Alain Tại sao không xác định các lỗi được sử dụng bởi nhiều hơn một lớp trong mô-đun Ngoại lệ / Lỗi, nhưng để lại tất cả các lỗi khác được xác định trong lớp duy nhất sử dụng chúng?
Scott W

@ScottW, trong trường hợp đó, chúng tôi dựa vào nhà phát triển để nhớ kiểm tra.
Josh Saint Jacque

22

trong đường ray, bạn có thể tạo app/errorsthư mục

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

khởi động lại spring / server và nó sẽ nhận nó


Làm thế nào tôi nên nêu ra những ngoại lệ này?
Nikhil Wagh

@NikhilWagh raise FooError, "Example message..."hoặcraise FooError.new("Example message...")
schpet

13

Đây là một câu hỏi cũ, nhưng tôi muốn chia sẻ cách tôi xử lý các lỗi tùy chỉnh trong Rails, bao gồm đính kèm thông báo lỗi, kiểm tra và cách xử lý vấn đề này với ActiveRecordcác mô hình.

Tạo lỗi tùy chỉnh

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

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

Kiểm tra (nhỏ nhất)

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

Với ActiveRecord

Tôi nghĩ rằng đáng lưu ý rằng nếu làm việc với một ActiveRecordmô hình, một mẫu phổ biến là thêm một lỗi vào mô hình như được mô tả dưới đây, để các xác nhận của bạn sẽ thất bại:

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

Khi xác thực được chạy, phương thức này sẽ đưa vào ActiveRecord::RecordInvalidlớp lỗi của ActiveRecord và sẽ khiến xác nhận không thành công.

Hi vọng điêu nay co ich!


9

Để đảm bảo rằng tự động tải hoạt động như mong đợi trong Rails 4.1.10 cho nhiều lớp lỗi tùy chỉnh, bạn sẽ muốn chỉ định các tệp riêng biệt cho mỗi lớp. Điều này sẽ làm việc trong sự phát triển với tải lại động của nó.

Đây là cách tôi thiết lập lỗi trong một dự án gần đây:

Trong lib/app_name/error/base.rb

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

và trong các lỗi tùy chỉnh tiếp theo, như trong lib/app_name/error/bad_stuff.rb

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

Sau đó, bạn có thể gọi các lỗi của bạn thông qua:

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

Và nếu bạn không muốn có một tệp riêng cho mỗi lỗi mới, chỉ cần đặt tất cả chúng vàolib/app_name/error.rb
jlhonora

Bắt một uninitialized constant MyController::AppName. Tôi đang gọi tăng trong bộ điều khiển của mình
Nikhil Wagh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.