2016-05-05 19 views
2

Wie kann ich add_entry Methode durch etwas sinnvoller ersetzen?Ruby dynamisch Methoden und Variablen erstellen

class MyStorageClass 

    def add_entry key, value 
     eval "(@#{key} ||= []) << value; def #{key}; @#{key}; end" 
    end 

end 

So dann kann ich Wert wie folgt abrufen:

def get_entry key 
    begin 
     self.send key.to_sym 
    rescue NoMethodError 
     nil 
    end 
end 

Antwort

6

Statt einer Instanzvariable pro Schlüssel, die etwas unnötig sperrigen Code benötigt, warum nicht nur ein Hash wie unten. Auch können define_method und define_singleton_method Ihr Freund sein, um schlechtes schlechtes eval s zu vermeiden.

class MyStorageClass 
    def initialize 
    @data = {} 
    end 

    def add_entry(key, value) 
    (@data[key] ||= []) << value 
    define_singleton_method(key){ @data[key] } 
    end 

    def get_entry(key) 
    @data.key?(key) or raise NoMethodError 
    @data[key] 
    end 
end 

Möglicherweise möchten Sie überprüfen, ob Sie nicht eine vordefinierte Methode überschrieben erste ([email protected]?(key) && self.respond_to?(key) an der Spitze des add_entry Methode tun würde), aber das ist für einen anderen Gespräch. Könnte schlecht sein, wenn jemand versucht hat, einen Schlüssel namens inspect, class, oder, oh, get_entry, zum Beispiel, hinzuzufügen!

+0

Sie könnten auch init @ data mit Hash.new {[]}, so dass Sie brauchen nicht auf die, || = Teil in add_entry Verfahren – karina

+1

Gefahr ! Es müsste sein: Hash.new {| h, k | h [k] = []} 'oder alle neuen Schlüssel erhalten Werte, die mit den anderen verknüpft sind. Das Objekt [], das Sie übergeben haben, ist ein einzelnes Objekt im Speicher, auf das von allen Schlüsseln verwiesen wird, und der Operator '<<', den wir verwenden, ändert dieses Objekt an Ort und Stelle. Versuch es! 'h = Hash.new ([]); h [: x] << 1; h [: y] 'gibt dir' [1] '. Normalerweise wende ich mich gegen den Blockinitialisierer für Hash, es sei denn, es ist vorteilhafter als das Speichern eines '|| =' –

+0

Ich habe die geschweiften Klammern verwendet. – karina

0

Ich bin nicht sicher, was Sie nennen „vernünftiger“, aber hier ist die Vorlage ohne eval s mit zu beginnen:

self.class.send :attr_reader, key \ 
    unless self.class.instance_methods.include?(key) 
:
def add_entry key, value 
    # define instance variable unless it is already defined 
    instance_variable_set :"@#{key}", [] \ 
    unless instance_variable_defined? :"@#{key}" 
    # add value to the array 
    instance_variable_set :"@#{key}", instance_variable_get(:"@#{key}") + value 
    # define getter 
    self.class.send :define_method key { instance_variable_get :"@#{key}" } \ 
    unless self.class.instance_methods.include?(key) 
end 

Der Getter kann in lesbarer Weise definiert werden,

0

dies erreicht werden kann instance_variable_set und attr_accessor mit:

class MyStorageClass 
    def add_entry(key, value) 
    if respond_to?(key) 
     key << value 
    else 
     instance_variable_set("@#{key}", [value]) 
     self.class.send(:attr_accessor, key) 
    end 
    end 
end 

jedoch wie andere vorgeschlagen haben, ein sauberer Ansatz ist, einfach ein Hash zu verwenden, anstatt für jede Variable eine neue Instanz Methode definieren.

1

IMO das ist eine wirklich schlechte Idee. Mach das nicht! Sie werden Komplexität mit sehr geringem Nutzen hinzufügen.

Ich empfehle stattdessen eine OpenStruct. Dies sind großartige Objekte - Sie können Getter und Setter nach Belieben aufrufen, ohne die Attribute im Voraus festzulegen. Vielleicht ein wenig ineffizient, aber das ist normalerweise egal.

Ein Nebeneffekt von OpenStruct ist, dass Sie Ihre Attribute in logische Mengen gruppieren können, z. connection_options, formatting_options usw. Hier ist ein Beispielskript veranschaulichen:

#!/usr/bin/env ruby 

require 'ostruct' 

class MyClass 

    attr_reader :config_options # only if you want to expose this 

    def initialize 
    @config_options = OpenStruct.new 
    end 

    def do_something 
    config_options.color = 'yellow' 
    config_options.size = 'medium' 
    end 

    def to_s 
    config_options.to_h.to_s 
    end 
end 

my_class = MyClass.new 
my_class.do_something 
puts my_class # outputs: {:color=>"yellow", :size=>"medium"} 
+0

['Hashie :: Mash'] (https://github.com/intridea/hashie#mash)? – mudasobwa

+0

@mudasobwa Kontext/Ausarbeitung, bitte? –

+0

Ich wollte nur einen Link auf die große Bibliothek fallen lassen, das ist [IMHO] auf alle Fälle ein besseres Äquivalent von 'OpenStruct'. – mudasobwa

Verwandte Themen