创建用于引发特定于类的错误的模块

时间:2013-12-19 06:22:43

标签: ruby-on-rails ruby ruby-on-rails-3 metaprogramming

在我的rails项目中,我经常在我的类和模型中使用这种行为:

class Whatever

  class WhateverError < StandardError; end

  def initialize(params={})
    raise WhateverError.new("Bad params: #{params}") if condition
    # actual class code to follow
  end
end

麻烦的是,这是非常重复和相当冗长的。如果我能在每次需要引发特定于类的错误时执行此操作,我会喜欢它:

class ErrorRaiser
  include ClassErrors
  def initialize(params={})
    error("Bad params: #{params}") if condition
    error if other_condition # has default message
    # actual class code to follow
  end

  def self.class_method
    error if third_condition # class method, behaves identically
  end
end

我在创建这样一个模块时遇到了很大的麻烦。我悲伤的早期尝试往往看起来像下面这样,但我很困惑模块范围内的可用内容,如何动态创建类(在方法中?)或者我是否有直截了当的access to the "calling" class at所有

我的基本要求是error既是类方法又是实例方法,它是调用它的类的“命名空间”,并且它有一个默认消息。有什么想法/帮助吗?这甚至可能吗?

module ClassErrorable 

  # This and the "extend" bit (theoretically) allow error to be a class method as well
  module ClassMethods
    def self.error(string=nil)
      ClassErrorable.new(string).error
    end
  end

  def self.included(base)
    set_error_class(base)
    base.extend ClassMethods
  end

  def self.set_error_class(base)
    # I'm shaky on the scoping. Do I refer to this with @ in a class method
    # but @@ in an instance method? Should I define it here with @ then?
    @@error_class = "##{base.class}Error".constantize
  end

  def self.actual_error
    # This obviously doesn't work, and in fact,
    # it raises a syntax error. How can I make my 
    # constant a class inheriting from StandardError?
    @@actual_error = @@error_class < StandardError; end 
  end

  def initialize(string)
    @string = string || "There's been an error!"
  end

  def error(string=nil)
    raise @@actual_error.new(string)
  end

end

1 个答案:

答案 0 :(得分:1)

这样的事情(用纯Ruby编写;可以重构使用一些特定于Rails的特性,如.constantize):

module ClassErrorable 
  module ClassMethods
    def error(message = nil)
      klass = Object::const_get(exception_class_name)
      raise klass.new(message || "There's been an error!")
    end

    def exception_class_name
      name + 'Error'  
    end
  end

  def self.included(base)
    base.extend ClassMethods
    Object::const_set(base.exception_class_name, Class.new(Exception))
  end

  def error(message = nil)
    self.class.error(message)
  end
end