基于 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 # => 'John'
或者,如果您的上下文需要更内联的调用结构:
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
# Note the omission of "v.extend(HashExtension)" vs. the mix-in variation.
对其他答案的评论表达了保留类类型的愿望。这个解决方案可以满足这一点。
people = { person1: { created_at: Time.now } }
people.with_very_indifferent_access!
people.person1.created_at.class # => Time
无论您选择哪种解决方案,我都建议使用此哈希进行测试:
people = { person1: { display: { first: 'John' } }, person2: { display: { last: 'Jingleheimer' } } }