Programing

Ruby 사용자 정의 오류 클래스 : 메시지 속성 상속

lottogame 2020. 9. 1. 07:54
반응형

Ruby 사용자 정의 오류 클래스 : 메시지 속성 상속


사용자 지정 예외 클래스에 대한 정보를 많이 찾을 수없는 것 같습니다.

내가 아는 것

사용자 정의 오류 클래스를 선언하고에서 상속 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

작동 할 것인가, 작동한다면 이것이 올바른 일을하는 방법인가?


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, 참조 는`구조 예외 => e` 루비에 나쁜 스타일 왜? .


Exception다른 모든 오류가 상속 하는의 루비 코어 문서 에서 다음과 같이 설명합니다.#message

예외를 호출 한 결과를 반환합니다. 일반적으로 이것은 예외의 메시지 또는 이름을 반환합니다. to_str 메소드를 제공하면 예외가 문자열이 예상되는 곳에 사용되는 데 동의합니다.

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

재정의 to_s/ to_str또는 이니셜 라이저를 선택합니다. 다음은 외부 서비스가 어떤 작업을 수행하지 못했을 때 대부분 사람이 읽을 수있는 방식으로 알고 싶은 예입니다.

참고 : 아래의 두 번째 전략 demodualize은 약간 복잡 할 수 있으므로 예외에서 수행하는 것이 현명하지 않을 수있는 과 같은 rails pretty string 메서드를 사용합니다 . 필요한 경우 메서드 서명에 더 많은 인수를 추가 할 수도 있습니다.

#to_str이 아닌 #to_s 전략을 재정의하면 다르게 작동합니다.

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, underscorehumanize 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

데모 도구

This is a demo to show rescuing and messaging of the above implementation. The class raising the exceptions is a fake API to Cloudinary. Just dump one of the above strategies into your rails console, followed by this.

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

Your idea is right, but the way you call it is wrong. It should be

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

I wanted to do something similar. I wanted to pass an object to #new and have the message set based on some processing of the passed object. The following works.

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

Note that if you don't declare attr_accessor :message then it will not work. Addressing the OP's issue, you could also pass the message as an additional argument and store anything you like. The crucial part appears to be overriding #message.

참고URL : https://stackoverflow.com/questions/16106645/ruby-custom-error-classes-inheritance-of-the-message-attribute

반응형