2010-05-08 7 views
0

Alors, je voudrais pouvoir faire un appelarguments en méthodes d'instance en rubis

x = MyClass.new('good morning', 'good afternoon', 'good evening', 'good night', 
      ['hello', 'goodbye']) 

qui ajouterait des méthodes à la classe dont les valeurs sont les valeurs des arguments. Alors maintenant:

p x.methods #> [m_greeting, a_greeting, e_greeting, n_greeting, 
         r_greeting, ...] 

Et

p x.m_greeting #> "good morning" 
p x.r_greeting #> ['hello', 'goodbye'] 

Je me rends compte que ce genre de quelles variables par exemple, sont à faire (et que si je voulais immuable je pouvais les rendre constantes congelés) mais, pour des raisons au-delà de mon contrôle, j'ai besoin de faire des méthodes à la place.

Merci!

BTW: J'ai essayé

def initialize(*args) 
    i = 0 
    %w[m_a, m_b, m_c].each do |a| 
    self.class.send(:define_method, a.to_s, Proc.new { args[i] }) 
    i+=1 
    end 
end 

Mais qui a fini par donner à chaque méthode la valeur du dernier argument.

Répondre

1

Votre dernière boucle enverrait le dernier argument de redéfinir la méthode pour chacun de vos m_a, m_b, m_c Essayez boucle sur les args et l'envoi à la méthode indexée.

par exemple.

def initialize(*args) 
    methods = %w[m_a m_b m_c] 
    args.each_with_index {|item,index| 
    self.class.send(:define_method, methods[index], lambda { item }) 
    } 
end 

each_with_index vient du module Enumerable: http://ruby-doc.org/core/classes/Enumerable.html#M003137

2

Je suppose que cela résout le problème:

def initialize(*args) 
    @args = args 
    %w[m_a m_b m_c].each_with_index do |a, i| 
    eval "def #{a}; @args[#{i}]; end" 
    end 
end 
+0

ah posté pendant que je tapais; \ –

2

Vous pouvez faire ce que vous voulez, comme ceci:

class Foo 

    def initialize(*args) 
    methods = %w[m_greeting a_greeting e_greeting n_greeting r_greeting] 
    raise ArgumentError unless args.size == methods.size 
    args.zip(methods).each do |arg, method| 
     self.class.instance_eval do 
     define_method method do 
      arg 
     end 
     end 
    end 
    end 

end 

foo = Foo.new(1, 2, 3, 4, 5) 
p foo.m_greeting # => 1 
p foo.a_greeting # => 2 
p foo.e_greeting # => 3 
p foo.n_greeting # => 4 
p foo.r_greeting # => 5 

Mais cela ne peut être le droïde que vous cherchez: Plus que quelques arguments positionnels peuvent rendre le code difficile à lire. Vous pourriez envisager d'utiliser OpenStruct. Vous devez écrire presque pas de code, et les appels du constructeur sera plus facile à lire:

require 'ostruct' 

class Foo < OpenStruct 
end 

foo = Foo.new(:m_greeting=>1, 
       :a_greeting=>2, 
       :e_greeting=>3, 
       :n_greeting=>4, 
       :r_greeting=>5) 
p foo.m_greeting # => 1 
p foo.a_greeting # => 2 
p foo.e_greeting # => 3 
p foo.n_greeting # => 4 
p foo.r_greeting # => 5 

Ne pas transpirer mutabilité. Si vous ressentez le besoin d'écrire du code pour vous protéger des erreurs, pensez plutôt à écrire des tests unitaires. Ensuite, le code peut être libéré avec divers contrôles et protections.