这是我的问题。我喜欢Andrea Pavoni允许使用嵌套哈希初始化类的方法。
require 'ostruct'
class DeepStruct < OpenStruct
def initialize(hash=nil)
@table = {}
@hash_table = {}
if hash
hash.each do |k,v|
@table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
@hash_table[k.to_sym] = v
new_ostruct_member(k)
end
end
end
def to_h
@hash_table
end
end
但是我无法找到一种方法来包含具有特定默认值的哈希(在类中),因此行为如下:
没有默认的原始行为(使用上面的代码):
input_hash = {a: {b: 1}}
new_object = DeepStruct.new hash
new_object.a # => #<DeepStruct b=1>
new_object.a.b # => 1
new_object.a.to_h # => {b: 1}
在类中定义了以下default_h:
default_h = {a: {dc: 2}, dd: {de: 4}}
input_hash和default_h应合并如下 (实际上使用deep_merge嵌套哈希)
{:a=>{:dc=>2, :b=>1}, :dd=>{:de=>4}}
默认哈希的行为应为:
new_object = DeepStruct.new hash
new_object.a.b # => 1
new_object.a.dc # => 2
new_object.a.to_h # => {:dc=>2, :b=>1}
我找不到在类中实现此行为的方法。我真的很感激这方面的任何帮助。
编辑:现在尝试在课程中使用David的代码:
class CompMedia
require 'ostruct'
attr_accessor :merged_h
def initialize(hash)
defaults = {a: {dc: 2}, dd: {de: 4}}
@merged_h = {}
deep_update(merged_h, defaults)
deep_update(merged_h, hash)
@merged_h
end
def deep_update(dest, src)
src.each do |key, value|
if value.is_a?(Hash)
dest[key] = {} if !dest[key].is_a?(Hash)
deep_update(dest[key], value)
else
dest[key] = value
end
end
end
def deep_open_struct(hash)
result = OpenStruct.new
hash.each do |key, value|
if value.is_a?(Hash)
result[key] = deep_open_struct(value)
else
result[key] = value
end
end
result
end
end # class CompMedia
input_hash = {a: {b: 1}}
cm = CompMedia.new(input_hash)
object = cm.deep_open_struct(cm.merged_h)
p object.marshal_dump # {:a=>#<OpenStruct dc=2, b=1>, :dd=>#<OpenStruct de=4>}
p object.a # <OpenStruct dc=2, b=1>
p object.a.marshal_dump # {:dc=>2, :b=>1}
p object.a.b # 1
p object.a.dc # 2
p object.dd # <OpenStruct de=4>
显然,我还没有找到一种方法来以简单的方式检索openstruct对象中的嵌套哈希元素。 我的目标是创建一个类,该类将使用类中包含的默认(嵌套)哈希和(嵌套)输入哈希进行初始化。另外,我希望能够添加处理类内哈希的方法。我还没有。
另一方面,我可以使用合并的哈希,这虽然可以使用稍微麻烦的注释:
class CompMedia
attr_accessor :merged_h
def initialize(hash)
defaults = {a: {dc: 2}, dd: {de: 4}}
@merged_h = {}
deep_update(merged_h, defaults)
deep_update(merged_h, hash)
@merged_h
end
def deep_update(dest, src)
src.each do |key, value|
if value.is_a?(Hash)
dest[key] = {} if !dest[key].is_a?(Hash)
deep_update(dest[key], value)
else
dest[key] = value
end
end
end
def multiply_by(k)
merged_h[:a][:dc] * k
end
end
input_hash = {a: {b: 1}}
cm = CompMedia.new(input_hash)
p cm.merged_h # {:a=>{:dc=>2, :b=>1}, :dd=>{:de=>4}}
p cm.merged_h[:a] # {:dc=>2, :b=>1}
p cm.merged_h[:a][:dc] # 2
p cm.merged_h[:dd] # {:de=>4}
p cm.multiply_by(10) # 20
我会将最后一个版本视为我的解决方案,除非有人可以使用OpenStruct工作,我更喜欢这个。
答案 0 :(得分:1)
这里有一些代码可以做你想要的,除了我抛弃了子类化OpenStruct的想法,因为我不确定这是不是一个好主意。另外,我自己实现了deep_merge,因为它很容易实现,但如果你愿意的话,你可以尝试使用ActiveSupport的版本。
require 'ostruct'
# Merges two hashes that could have hashes inside them. Default
# values/procs of the input hashes are ignored. The output hash will
# not contain any references to any of the input hashes, so you don't
# have to worry that mutating the output will affect the inputs.
def deep_merge(h1, h2)
result = {}
deep_update(result, h1)
deep_update(result, h2)
result
end
def deep_update(dest, src)
src.each do |key, value|
if value.is_a?(Hash)
dest[key] = {} if !dest[key].is_a?(Hash)
deep_update(dest[key], value)
else
dest[key] = value
end
end
end
def deep_open_struct(hash)
result = OpenStruct.new
hash.each do |key, value|
if value.is_a?(Hash)
result[key] = deep_open_struct(value)
else
result[key] = value
end
end
result
end
input_hash = {a: {b: 1}}
defaults = {a: {dc: 2}, dd: {de: 4}}
object = deep_open_struct(deep_merge(defaults, input_hash))
p object.a.b
p object.a.dc
p object.a.to_h