2016-07-20 11 views
0

Hier ist mein Problem. Ich mag Andrea Pavoni 's Möglichkeit, einen verschachtelten Hash zu verwenden, um eine Klasse zu initialisieren.Ruby: initialisiert eine Ruby-Klasse mit einem verschachtelten Hash und einigen vordefinierten Standardwerten

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 

Aber ich kann nicht einen Weg umfasst einen Hash (in der Klasse) mit spezifischen Standardwerten finden, so dass das Verhalten wäre wie folgt:

Original-Verhalten ohne Standard (mit oben code):

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} 

Mit folgendem default_h innerhalb der Klasse definiert:

default_h = {a: {dc: 2}, dd: {de: 4}} 

input_hash und default_h wie folgt fusionieren

{:a=>{:dc=>2, :b=>1}, :dd=>{:de=>4}} 

Das Verhalten mit Standard-Hash (tatsächlich deep_merge für verschachtelte Hash verwendet wird) sein sollte:

new_object = DeepStruct.new hash 
new_object.a.b # => 1 
new_object.a.dc # => 2 
new_object.a.to_h # => {:dc=>2, :b=>1} 

ich keinen Weg finden können um dieses Verhalten innerhalb der Klasse zu implementieren. Ich würde wirklich jede Hilfe in dieser Angelegenheit schätzen.

Edit: versuchen nun Davids Code in einer Klasse zu verwenden:

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> 

Natürlich, ich habe einen Weg gefunden auf einfache Art und Weise die verschachtelten Hash-Elemente aus dem openstruct Objekt abzurufen. Mein Ziel ist es, eine Klasse zu erstellen, die mit einem standardmäßigen (verschachtelten) Hash in der Klasse und einem (verschachtelten) Eingabehash initialisiert wird. Außerdem möchte ich in der Lage sein, Methoden hinzuzufügen, die den Hash innerhalb der Klasse verarbeiten würden. Ich bin noch nicht da.

Auf der anderen Seite konnte ich nur das fusionierte Hash verwenden und diese mit etwas umständlichen Notationen wenn auch funktionieren würden:

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 

Ich werde die letzte Version als meine Lösung betrachten, es sei denn jemand den Code macht mit OpenStruct Arbeit, die ich bevorzugen würde.

Antwort

1

Hier ist ein Code, der das tut, was Sie wollen, außer dass ich die Idee, OpenStruct abzuleiten, verwarf, weil ich nicht sicher war, ob es eine gute Idee war. Außerdem habe ich deep_merge selbst implementiert, weil es ziemlich einfach war, aber Sie könnten versuchen, die Version von ActiveSupport zu verwenden, wenn Sie wollten.

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 
+0

Danke, das funktioniert, aber ich glaube, ich brauche eine Klasse, die den Standard-Hash enthält, und wird mit dem zusammengeführten Hash (Standard + Eingabe) initialisiert. Der Grund ist, dass ich zusätzliche Methoden hinzufügen möchte, die den Inhalt des Hashes verarbeiten. Ich arbeite mit deinem Code daran und werde es veröffentlichen, wenn ich etwas finde. Warum ist es eine schlechte Idee, OpenStruct zu untergliedern? – JMor

+0

Es sollte nicht zu schwer sein, eine Klasse zu haben, die den Standard-Hash enthält. Sie können den Code in diesem Post unverändert belassen und eine Klasse erstellen, die ihn intern verwendet. –

+0

Ich habe das getan. Aber ich kämpfe mit den Methoden dieser Klasse, die auf den zusammengeführten Hash zugreifen würde – JMor