Di mana menentukan jenis kesalahan khusus di Ruby dan / atau Rails?

149

Apakah ada praktik terbaik untuk menentukan jenis kesalahan khusus di aplikasi Ruby library (gem) atau Ruby on Rails? Secara khusus:

  1. Di mana mereka berada secara struktural dalam proyek? File terpisah, diuraikan dengan definisi modul / kelas yang relevan, di tempat lain?
  2. Apakah ada konvensi yang menetapkan saat ke dan kapan untuk tidak membuat kesalahan tipe baru?

Perpustakaan yang berbeda memiliki cara yang berbeda dalam melakukan sesuatu, dan saya belum melihat adanya pola yang nyata. Beberapa perpustakaan selalu menggunakan jenis kesalahan khusus sementara yang lain tidak menggunakannya sama sekali; beberapa memiliki semua kesalahan memperluas StandardError sementara yang lain memiliki hierarki bersarang; beberapa hanya definisi kelas kosong, yang lain memiliki segala macam trik pintar.

Oh, dan hanya karena saya merasa ingin memanggil "tipe kesalahan" ini agak ambigu, yang saya maksud adalah ini:

class AuthenticationError < StandardError; end
class InvalidUsername < AuthenticationError; end
intinya
sumber

Jawaban:

219

Untuk Permata

Saya telah melihat beberapa kali Anda mendefinisikan pengecualian dengan cara ini:

gem_dir / lib / gem_name / exceptionions.rb

dan didefinisikan sebagai:

module GemName

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

end

contohnya akan seperti ini di httparty

Untuk Ruby on Rails

Tempatkan mereka di lib / folder Anda di bawah file yang disebut exception.rb, yang akan terlihat seperti ini:

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

dan Anda akan menggunakannya seperti ini:

raise Exceptions::InvalidUsername
Mike Lewis
sumber
Untuk permata, sepertinya Anda mungkin juga harus menyertakan file pengecualian. Lihat contoh ini, lagi dari httparty: github.com/jnunemaker/httparty/blob/…
Jason Swett
37
Mengapa namespace mereka ke dalam Exceptionsmodul?
ABMagil
13
Saya akan berpikir itu /libmungkin bukan tempat untuk kesalahan. Mereka sangat spesifik aplikasi dan saya mendapat kesan bahwa kode yang saya masukkan /libdimaksudkan untuk menjadi kode yang dapat digunakan kembali dalam aplikasi lain.
wuliwong
1
Instruksi Ruby on Rails tidak berfungsi untuk saya - adakah langkah tambahan yang diperlukan untuk memuat file baru ini dalam kasus tipikal?
Meekohi
1
@ABMagil tampaknya saya harus sebaliknya Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define itpilihan lain akan menjadi satu kelas per pengecualian saya pikir
ryan2johnson9
25

Saya pikir untuk memiliki file sumber yang kohesif dalam proyek Anda, Anda harus mendefinisikan kesalahan di kelas yang dapat membuangnya dan tidak ke tempat lain.

Beberapa heirarki dapat membantu - ruang nama bagus dalam menjaga string yang berlebihan dari nama tipe - tetapi itu lebih merupakan masalah selera - tidak perlu berlebihan, asalkan Anda memiliki setidaknya satu jenis pengecualian khusus dalam aplikasi yang Anda gunakan untuk membedakan antara kasus pengecualian 'disengaja' dan 'tidak disengaja'.

Dean Radcliffe
sumber
8
Meskipun secara teori Anda benar, apa yang terjadi ketika kesalahan yang sama dapat diangkat oleh berbagai kelas dalam situasi yang sama sekali berbeda?
Alain
1
@Alain Mengapa tidak mendefinisikan kesalahan-kesalahan yang digunakan oleh lebih dari satu kelas dalam modul Pengecualian / Kesalahan, tetapi membiarkan semua yang lain didefinisikan dalam kelas tunggal yang menggunakannya?
Scott W
@ScottW, Dalam hal ini, kami mengandalkan pengembang untuk mengingat untuk memeriksa.
Josh Saint Jacque
22

di rel Anda bisa membuat app/errorsdirektori

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

restart spring / server dan itu harus mengambilnya

schpet
sumber
Bagaimana saya harus mengajukan pengecualian ini?
Nikhil Wagh
@NikhilWagh baik raise FooError, "Example message..."atauraise FooError.new("Example message...")
schpet
13

Ini adalah pertanyaan lama, tetapi saya ingin berbagi bagaimana saya menangani kesalahan khusus di Rails, termasuk melampirkan pesan kesalahan, pengujian, dan bagaimana cara mengatasinya dengan ActiveRecordmodel.

Membuat Kesalahan Khusus

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

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

Pengujian (terkecil)

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

Dengan ActiveRecord

Saya pikir perlu dicatat bahwa jika bekerja dengan ActiveRecordmodel, pola populer adalah menambahkan kesalahan pada model seperti yang dijelaskan di bawah ini, sehingga validasi Anda akan gagal:

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

Ketika validasi dijalankan, metode ini akan membonceng kembali ke ActiveRecord::RecordInvalidkelas kesalahan ActiveRecord dan akan menyebabkan validasi gagal.

Semoga ini membantu!

Mat
sumber
9

Untuk memastikan bahwa autoloading berfungsi seperti yang diharapkan di Rails 4.1.10 untuk beberapa kelas kesalahan khusus, Anda ingin menentukan file yang terpisah untuk masing-masingnya. Ini harus bekerja dalam pengembangan dengan reload dinamis.

Ini adalah cara saya mengatur kesalahan dalam proyek terbaru:

Di lib/app_name/error/base.rb

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

dan kesalahan khusus berikutnya, seperti pada lib/app_name/error/bad_stuff.rb

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

Anda kemudian dapat memanggil kesalahan Anda melalui:

 raise AppName::Error::BadStuff.new("Bad stuff just happened")
spyle
sumber
Dan jika Anda tidak ingin file terpisah untuk setiap kesalahan baru, cukup masukkan semuanyalib/app_name/error.rb
jlhonora
Mendapatkan uninitialized constant MyController::AppName. Saya menelepon kenaikan di controller saya
Nikhil Wagh