2014-04-20 8 views
5

Questo è un modello inizializzatore comune:Collegamento per l'impostazione di initialize() args agli attributi?

def initialize(title, val, type) 
    @title, @val, @type = title, val, type 
end 

C'è una scorciatoia che equivale a "prendere ogni argomento, creare un attributo con lo stesso nome, e impostare l'attributo uguale al valore dell'argomento"?

Sto cercando una soluzione senza gemme.

+0

Un bel thread su quello qui. http://stackoverflow.com/questions/2680523/dry-ruby-initialization-with-hash-argument – Jazzepi

+0

@Jazzepi, si tratta di un filo di interessante, ma si occupa solo con argomenti hash. –

risposta

2

Ecco come è possibile farlo. Verrà creata una variabile di istanza e un accessore di lettura/scrittura associato per ciascuno dei parametri di initialize, con la variabile con lo stesso nome, preceduta da @ e ad ogni variabile di istanza verrà assegnato il valore del parametro associato.

Codice

class MyClass 
    def initialize(< arbitrary parameters >) 
    self.class.params.each { |v| 
     instance_variable_set("@#{v}", instance_eval("#{v}")) } 

    < other code > 
    end 

    @params = instance_method(:initialize).parameters.map(&:last) 
    @params.each { |p| instance_eval("attr_accessor :#{p}") } 

    class << self 
    attr_reader :params 
    end 

    < other code >  
end 

Esempio

class MyClass 
    def initialize(a, b, c) 
    self.class.params.each { |v| 
     instance_variable_set("@#{v}", instance_eval("#{v}")) } 
    end 

    @params = instance_method(:initialize).parameters.map(&:last) 
    @params.each { |p| instance_eval("attr_accessor :#{p}") } 

    class << self 
    attr_reader :params 
    end 
end 

MyClass.methods(false) 
    #=> [:params] 
MyClass.instance_methods(false) 
    #=> [:a, :a=, :b, :b=, :c, :c=] 

m = MyClass.new(1,2,3) 
m.a #=> 1 
m.b #=> 2 
m.C#=> 3 
m.a = 4 
m.a #=> 4 

Spiegazione

Quando la classe MyClass viene analizzato, l'istanza della classe variabile @params viene assegnato un array i cui elementi sono i parametri di initialize. Ciò è possibile perché il metodo initialize è stato creato quando viene analizzato il codice che inizia con @params = ....

Il metodo Method#parameters viene utilizzato per ottenere i parametri di initialize. Per l'esempio precedente,

instance_method(:initialize).parameters 
    #=> [[:req, :a], [:req, :b], [:req, :c]] 

così

@params = instance_method(:initialize).parameters.map(&:last) 
    #=> [:a, :b, :c] 

Abbiamo poi creiamo le funzioni di accesso di lettura/scrittura:

@params.each { |p| instance_eval("attr_accessor :#{p}") } 

e una funzione di accesso di lettura per @params, per l'utilizzo da parte initialize:

class << self 
    attr_reader :params 
end 

Quando viene creata un'istanza my_class di MyClass, i valori dei parametri passati a MyClass.new vengono passati a initialize. initialize quindi esegue il ciclo della variabile di istanza di classe @params e imposta il valore di ogni variabile di istanza.In questo esempio,

MyClass.new(1,2,3) 

invoca initialize(a,b,c) dove

a => 1 
b => 2 
c => 3 

Abbiamo:

params = self.class.params 
    #=> [:a, :b, :c] 

params.each { |v| instance_variable_set("@#{v}", instance_eval("#{v}")) } 

Per il primo elemento di params (:a), questo è:

instance_variable_set("@a", instance_eval(a) } 

che è:

instance_variable_set("@a", 1 } 

causando @a da assegnare 1.

Nota la funzione di accesso per @params non è essenziale:

class MyClass 
    def initialize(a, b, c) 
    self.class.instance_variable_get(:@params).each { |v| 
     instance_variable_set("@#{v}", instance_eval("#{v}")) } 
    end 

    @params = instance_method(:initialize).parameters.map(&:last) 
    @params.each { |p| instance_eval("attr_accessor :#{p}") } 
end 
3

È possibile utilizzare Struct come detto sopra o farlo in modo dinamico come:

class MyClass 

    def initialize input 
    input.each do |k,v| 
     instance_variable_set("@#{k}", v) unless v.nil? 
    end 
    end 

end 

Comunque Se è il mio codice sarò vai usando Struct.

4

si perde la funzione per controllare contro argomentazioni sbagliate, ma può fare questo:

def initialize(*args) 
    @title, @val, @type = args 
end 

Ma se si sta facendo più volte questo, allora il codice non è giusto. Si dovrebbe meglio riprogettare l'API di prendere argomenti con nome:

def initialize(title:, val:, type:) 
    ... 
end 

WhateverClass.new(title: "foo", val: "bar", type: "baz") 
+0

semplicemente semplice e migliore –

1

mi piace usare solo un hash args e inviare ogni attributo per l'istanza. Tuttavia questo non creerà gli accessor per questi attributi, quindi dovrai aggiungerli se lo desideri.

def initialize(args) 
    args.each do |method, value| 
    self.send("#{method}=", value) 
    end 
end 
Problemi correlati