रूबी कस्टम त्रुटि कक्षाएं: संदेश विशेषता का उत्तराधिकार


95

मुझे कस्टम अपवाद कक्षाओं के बारे में अधिक जानकारी नहीं मिल सकती है।

मुझे क्या पता

आप अपने कस्टम त्रुटि वर्ग की घोषणा कर सकते हैं और इसे इनहेरिट StandardErrorकर सकते हैं, इसलिए इसे rescued:

class MyCustomError < StandardError
end

यह आपको इसका उपयोग करने की अनुमति देता है:

raise MyCustomError, "A message"

और बाद में, बचाते समय उस संदेश को प्राप्त करें

rescue MyCustomError => e
  puts e.message # => "A message"

जो मैं नहीं जानता

मैं अपने अपवाद को कुछ कस्टम फ़ील्ड देना चाहता हूं, लेकिन मैं messageअभिभावक वर्ग से विशेषता प्राप्त करना चाहता हूं । मुझे इस विषय पर पढ़ने में पता चला कि @messageयह अपवाद वर्ग का एक उदाहरण चर नहीं है, इसलिए मुझे चिंता है कि मेरी विरासत काम नहीं करेगी।

क्या कोई मुझे इसके बारे में अधिक जानकारी दे सकता है? मैं एक objectविशेषता के साथ एक कस्टम त्रुटि वर्ग कैसे लागू करूंगा ? निम्नलिखित सही है:

class MyCustomError < StandardError
  attr_reader :object
  def initialize(message, object)
    super(message)
    @object = object
  end
end

और तब:

raise MyCustomError.new(anObject), "A message"

लेना:

rescue MyCustomError => e
  puts e.message # => "A message"
  puts e.object # => anObject

क्या यह काम करेगा, और अगर यह करता है, तो क्या यह सही तरीका है?


3
नहीं है rescue Exception => e। यह डिफ़ॉल्ट से व्यापक है, rescue => eजो StandardErrorCtrl + C सहित सभी चीज़ों से फैलता है और पकड़ता है। मैं करता हूँ rescue MyCustomError => e
रियान टेलर

1
@RyanTaylor मैंने अधिक उचित दृष्टिकोण के लिए अपने प्रश्न को संपादित किया।
23

जवाबों:


121

raise पहले से ही संदेश सेट करता है, ताकि आपको इसे निर्माता को पास न करना पड़े:

class MyCustomError < StandardError
  attr_reader :object

  def initialize(object)
    @object = object
  end
end

begin
  raise MyCustomError.new("an object"), "a message"
rescue MyCustomError => e
  puts e.message # => "a message"
  puts e.object # => "an object"
end

मैंने जगह ले ली rescue Exceptionहै rescue MyCustomError, देखें कि रूबी में 'रेस्क्यू एक्सेप्शन => ई` को खराब स्टाइल क्यों दिया गया है?


मैं आपका उत्तर स्वीकार करूंगा क्योंकि आपने मुझे पूरा वाक्य-विन्यास दिखाया था। धन्यवाद!
मारियोडीस

1
यहाँ हम कर रहे हैं rescue Exception, लेकिन क्यों नहीं rescue MyCustomError?
Dfr

FYI करें, यदि पहला तर्क, वस्तु, एक विकल्प है और इसके raise MyCustomError, "a message"बिना new, "एक संदेश" सेट नहीं किया जाएगा।
हिरोशी

क्या हमारे कस्टम अपवाद वर्ग में किसी तरह से उठाया गया संदेश प्राप्त करने का एक तरीका है?
साइबरमेव

@CyberMew आपका क्या मतलब है? आप क्या करना चाहते हैं?
स्टीफन

10

यह देखते हुए कि रूबी कोर दस्तावेज Exceptionकिस से, अन्य सभी त्रुटियों के वारिस हैं, के बारे में बताता है#message

अपवाद के परिणाम को वापस लौटाता है। आम तौर पर यह अपवाद का संदेश या नाम देता है। एक to_str पद्धति की आपूर्ति करके, अपवाद का उपयोग करने के लिए सहमत हो रहे हैं जहां स्ट्रिंग्स की अपेक्षा की जाती है।

http://ruby-doc.org/core-1.9.3/Exception.html#method-i-message

मैं पुनर्निर्धारण to_s/ to_strया आरंभीकरण का विकल्प चुनूंगा । यहां एक उदाहरण है जहां हम जानना चाहते हैं, ज्यादातर मानव पठनीय तरीके से, जब कोई बाहरी सेवा कुछ करने में विफल रही है।

नोट: नीचे दी गई दूसरी रणनीति में बहुत सुंदर तरीके जैसे रेल का उपयोग किया गया है demodualize, जो थोड़ा जटिल हो सकता है और इसलिए संभावित रूप से एक अपवाद में ऐसा करने के लिए नासमझ है। आप विधि हस्ताक्षर के लिए और भी तर्क जोड़ सकते हैं, आपको चाहिए।

#To_s की रणनीति #to_str को ओवरराइड करने से , यह अलग तरह से काम करता है

module ExternalService

  class FailedCRUDError < ::StandardError
    def to_s
      'failed to crud with external service'
    end
  end

  class FailedToCreateError < FailedCRUDError; end
  class FailedToReadError < FailedCRUDError; end
  class FailedToUpdateError < FailedCRUDError; end
  class FailedToDeleteError < FailedCRUDError; end
end

कंसोल आउटपुट

begin; raise ExternalService::FailedToCreateError; rescue => e; e.message; end
# => "failed to crud with external service"

begin; raise ExternalService::FailedToCreateError, 'custom message'; rescue => e; e.message; end
# => "failed to crud with external service"

begin; raise ExternalService::FailedToCreateError.new('custom message'); rescue => e; e.message; end
# => "failed to crud with external service"

raise ExternalService::FailedToCreateError
# ExternalService::FailedToCreateError: failed to crud with external service

ओवरराइडिंग #initialize रणनीति

यह वह रणनीति है जिसे मैंने रेल में उपयोग किए जाने वाले कार्यान्वयन के सबसे करीब रखा है। जैसा कि ऊपर बताया है, यह का उपयोग करता है demodualize, underscoreऔर humanize ActiveSupportतरीकों। लेकिन इसे आसानी से हटाया जा सकता था, जैसा कि पिछली रणनीति में था।

module ExternalService
  class FailedCRUDError < ::StandardError
    def initialize(service_model=nil)
      super("#{self.class.name.demodulize.underscore.humanize} using #{service_model.class}")
    end
  end

  class FailedToCreateError < FailedCRUDError; end
  class FailedToReadError < FailedCRUDError; end
  class FailedToUpdateError < FailedCRUDError; end
  class FailedToDeleteError < FailedCRUDError; end
end

कंसोल आउटपुट

begin; raise ExternalService::FailedToCreateError; rescue => e; e.message; end
# => "Failed to create error using NilClass"

begin; raise ExternalService::FailedToCreateError, Object.new; rescue => e; e.message; end
# => "Failed to create error using Object"

begin; raise ExternalService::FailedToCreateError.new(Object.new); rescue => e; e.message; end
# => "Failed to create error using Object"

raise ExternalService::FailedCRUDError
# ExternalService::FailedCRUDError: Failed crud error using NilClass

raise ExternalService::FailedCRUDError.new(Object.new)
# RuntimeError: ExternalService::FailedCRUDError using Object

डेमो उपकरण

यह उपरोक्त कार्यान्वयन के बचाव और संदेश दिखाने के लिए एक डेमो है। अपवाद बढ़ाने वाला वर्ग क्लाउडिनरी के लिए एक नकली एपीआई है। बस उपरोक्त रणनीतियों में से एक को अपने रेल कंसोल में डंप करें, इसके बाद।

require 'rails' # only needed for second strategy 

module ExternalService
  class FailedCRUDError < ::StandardError
    def initialize(service_model=nil)
      @service_model = service_model
      super("#{self.class.name.demodulize.underscore.humanize} using #{@service_model.class}")
    end
  end

  class FailedToCreateError < FailedCRUDError; end
  class FailedToReadError < FailedCRUDError; end
  class FailedToUpdateError < FailedCRUDError; end
  class FailedToDeleteError < FailedCRUDError; end
end

# Stub service representing 3rd party cloud storage
class Cloudinary

  def initialize(*error_args)
    @error_args = error_args.flatten
  end

  def create_read_update_or_delete
    begin
      try_and_fail
    rescue ExternalService::FailedCRUDError => e
      e.message
    end
  end

  private def try_and_fail
    raise *@error_args
  end
end

errors_map = [
  # Without an arg
  ExternalService::FailedCRUDError,
  ExternalService::FailedToCreateError,
  ExternalService::FailedToReadError,
  ExternalService::FailedToUpdateError,
  ExternalService::FailedToDeleteError,
  # Instantiated without an arg
  ExternalService::FailedCRUDError.new,
  ExternalService::FailedToCreateError.new,
  ExternalService::FailedToReadError.new,
  ExternalService::FailedToUpdateError.new,
  ExternalService::FailedToDeleteError.new,
  # With an arg
  [ExternalService::FailedCRUDError, Object.new],
  [ExternalService::FailedToCreateError, Object.new],
  [ExternalService::FailedToReadError, Object.new],
  [ExternalService::FailedToUpdateError, Object.new],
  [ExternalService::FailedToDeleteError, Object.new],
  # Instantiated with an arg
  ExternalService::FailedCRUDError.new(Object.new),
  ExternalService::FailedToCreateError.new(Object.new),
  ExternalService::FailedToReadError.new(Object.new),
  ExternalService::FailedToUpdateError.new(Object.new),
  ExternalService::FailedToDeleteError.new(Object.new),
].inject({}) do |errors, args|
  begin 
    errors.merge!( args => Cloudinary.new(args).create_read_update_or_delete)
  rescue => e
    binding.pry
  end
end

if defined?(pp) || require('pp')
  pp errors_map
else
  errors_map.each{ |set| puts set.inspect }
end

6

आपका विचार सही है, लेकिन जिस तरह से आप इसे कहते हैं वह गलत है। यह होना चाहिए

raise MyCustomError.new(an_object, "A message")

ठीक है, मैंने सोचा कि आपके द्वारा दिया गया संदेश raiseकीवर्ड या कुछ और के लिए एक दूसरा पैरामीटर था ।
मारियोड्स

आपने initializeदो तर्क लेने के लिए फिर से परिभाषित किया। newको तर्क देता है initialize
आरा

या, आप कोष्ठकों को छोड़ सकते हैं।
आरा

मैं उस बिट को समझता हूं, लेकिन मैंने अपने प्रश्न में जिस विषय को जोड़ा है उसका पोस्टर इस तरह से है raise(BillRowError.new(:roamingcalls, @index), "Roaming Calls field missing"):। इसलिए वह raiseदो मापदंडों के साथ कहता है : एक नई BillRowErrorवस्तु, और उसका संदेश। मैं सिंटैक्स से भ्रमित हूं ... अन्य ट्यूटोरियल पर मैं हमेशा इसे इस तरह से देखता हूं:raise Error, message
मारियोडीएस

1
समस्या यह नहीं है कि आप कितने तर्क से गुजरते हैं raise; यह बहुत अधिक लचीला है। समस्या यह है कि आपने initializeदो तर्क देने के लिए परिभाषित किया और केवल एक दिया। अपने उदाहरण में देखें। BillRowError.new(:roamingcalls, @index)दो तर्क दिए गए हैं।
आरा

4

मैं भी कुछ ऐसा ही करना चाहता था। मैं #new को ऑब्जेक्ट पास करना चाहता था और पास की गई वस्तु के कुछ प्रोसेसिंग के आधार पर मैसेज सेट करना चाहता हूं। निम्नलिखित कार्य करता है।

class FooError < StandardError
  attr_accessor :message # this is critical!
  def initialize(stuff)
    @message = stuff.reverse
  end
end

begin
  raise FooError.new("!dlroW olleH")
rescue FooError => e
  puts e.message #=> Hello World!
end

ध्यान दें कि यदि आप घोषणा नहीं करते हैं attr_accessor :messageतो यह काम नहीं करेगा। ओपी के मुद्दे को संबोधित करते हुए, आप संदेश को एक अतिरिक्त तर्क के रूप में भी पारित कर सकते हैं और अपनी पसंद के अनुसार कुछ भी संग्रहीत कर सकते हैं। यह महत्वपूर्ण हिस्सा #message ओवरराइडिंग प्रतीत होता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.