基于OpenStruct的转换方法在某些情况下效果很好,但在某些情况下则不行。例如,这里的其他答案都无法正确处理这些简单的哈希:
people = { person1: { display: { first: 'John' } } }
creds = { oauth: { trust: true }, basic: { trust: false } }
下面的方法适用于这些哈希表,修改输入哈希表而不是返回一个新对象。
def add_indifferent_access!(hash)
hash.each_pair do |k, v|
hash.instance_variable_set("@#{k}", v.tap { |v| send(__method__, v) if v.is_a?(Hash) } )
hash.define_singleton_method(k, proc { hash.instance_variable_get("@#{k}") } )
end
end
那么
add_indifferent_access!(people)
people.person1.display.first
或者,如果您的上下文需要更内联的调用结构:
creds.yield_self(&method(:add_indifferent_access!)).oauth.trust # => true
或者,你可以混合使用它:
module HashExtension
def very_indifferent_access!
each_pair do |k, v|
instance_variable_set("@#{k}", v.tap { |v| v.extend(HashExtension) && v.send(__method__) if v.is_a?(Hash) } )
define_singleton_method(k, proc { self.instance_variable_get("@#{k}") } )
end
end
end
并且适用于各个哈希值:
favs = { song1: { title: 'John and Marsha', author: 'Stan Freberg' } }
favs.extend(HashExtension).very_indifferent_access!
favs.song1.title
这是一种猴子补丁哈希的变化形式,如果您选择这样做:
class Hash
def with_very_indifferent_access!
each_pair do |k, v|
instance_variable_set("@#{k}", v.tap { |v| v.send(__method__) if v.is_a?(Hash) } )
define_singleton_method(k, proc { instance_variable_get("@#{k}") } )
end
end
end
其他答案中的评论表达了保留类类型的愿望。这个解决方案考虑到了这一点。
people = { person1: { created_at: Time.now } }
people.with_very_indifferent_access!
people.person1.created_at.class
无论您选择哪种解决方案,我建议使用以下哈希进行测试:
< p > < code > people = { person1: { display: { first: 'John' } }, person2: { display: { last: 'Jingleheimer' } } }