问题 访问OpenStruct中不存在的属性时引发异常


我试图弄清楚如何使它成为一个OpenStruct的子类(或任何类的东西)或哈希,如果我试图访问一个尚未设置的属性,将引发自定义异常。我无法得到 define_method 和 method_missing 要做到这一点,我就不知道如何在Ruby中完成它。

这是一个例子:

class Request < OpenStruct...

request = Request.new

begin
  request.non_existent_attr
rescue CustomError...

我可以想象它必须是这样的:

class Hash
  # if trying to access key:
  # 1) key exists, return key
  # 2) key doesn't exist, raise exception
end

编辑: 存在的属性不应引发异常。我正在寻找的功能是,我可以自由访问属性,如果它不存在我的自定义异常将被引发。


11085
2018-06-03 20:22


起源



答案:


当您设置新成员时,OpenStruct在对象上定义单例访问器方法,因此您可以使用 respond_to? 查看该成员是否有效。事实上,您可能只是捕获任何未使用method_missing定义的方法并抛出错误,除非它是一个setter方法名称,在这种情况下,您将它传递给super。

class WhinyOpenStruct < OpenStruct
  def method_missing(meth, *args)
    raise NoMemberError, "no #{meth} member set yet" unless meth.to_s.end_with?('=')
    super
  end
end

7
2018-06-03 21:01





如果您需要严格的哈希,只需:

class StrictHash < Hash
  alias [] fetch
end

它按预期工作:

hash = StrictHash[foo: "bar"]

hash[:foo]
# => "bar"

hash[:qux]
# stricthash.rb:7:in `fetch': key not found: :qux (KeyError)
#         from stricthash.rb:7:in `<main>'

6
2018-06-16 14:59



我所寻找的是“点访问”,而不是哈希访问。 +1虽然优雅。 - Seralize


答案:


当您设置新成员时,OpenStruct在对象上定义单例访问器方法,因此您可以使用 respond_to? 查看该成员是否有效。事实上,您可能只是捕获任何未使用method_missing定义的方法并抛出错误,除非它是一个setter方法名称,在这种情况下,您将它传递给super。

class WhinyOpenStruct < OpenStruct
  def method_missing(meth, *args)
    raise NoMemberError, "no #{meth} member set yet" unless meth.to_s.end_with?('=')
    super
  end
end

7
2018-06-03 21:01





如果您需要严格的哈希,只需:

class StrictHash < Hash
  alias [] fetch
end

它按预期工作:

hash = StrictHash[foo: "bar"]

hash[:foo]
# => "bar"

hash[:qux]
# stricthash.rb:7:in `fetch': key not found: :qux (KeyError)
#         from stricthash.rb:7:in `<main>'

6
2018-06-16 14:59



我所寻找的是“点访问”,而不是哈希访问。 +1虽然优雅。 - Seralize


我喜欢用的东西

hash = { a: 2, b: 3 }

Struct.new(*hash.keys).new(*hash.values).freeze

获得一个会引发的不可变对象 NoMethodError 如果调用了意外的方法


2
2018-03-23 12:52





在红宝石中,无论何时写作 object.horray 消息 horray 被发送到对象 object,这将返回一个值。每一个 horray 是一条消息。如果对象不响应此消息,则无法区分对象没有具有此名称的属性,或者它没有具有此名称的方法。

因此,除非你假设没有方法可以输入拼写错误,否则你不可能做你想做的事情。


1
2018-06-03 20:55



如果在OpenStruct上执行此操作,除初始化之外,您将无法再创建新成员。 - dbenhur
@dbenhur你是对的,ty,删除那个例子 - fotanus


这是残酷的,但你可以覆盖 new_ostruct_member 生成错误的方法:

require 'ostruct'

class CustomError < StandardError; end
os  = OpenStruct.new({:a=>1, :b=>1})
def os.new_ostruct_member(name) #just wrecking a single instance
  raise CustomError, "non-existing key #{name} called"
end

p os.a=3
p os.c=4 #=>non-existing key c called (CustomError)

0
2018-06-03 21:27



@Serialize想要在读取不存在的属性时引发错误。 - Gabe Kopley


我选择了这个完全符合我需求的解决方案:

class Request < Hash
  class RequestError < StandardError; end
  class MissingAttributeError < RequestError; end

  def initialize(hash)
    hash.each do |key, value|
      self[key] = value
    end
  end

  def [](key)
    unless self.include?(key)
      raise MissingAttributeError.new("Attribute '#{key}' not found in request")
    end

    super
  end
end

0
2018-06-04 16:39



你可以使用 fetch 方法而不是重写 [] 这里。 ruby-doc.org/core-1.9.3/Hash.html#method-i-fetch - Philippe Creux