J'ai créé dynamiquement une variable d'instance dans ma classe:
class Mine
attr_accessor :some_var
def intialize
@some_var = true
end
def my_number num
self.instance_variable_set "@my_#{num}", num
end
end
Comment créer @my_#{num}
maintenant en tant que valeur attr?
par exemple. Je veux pouvoir faire ceci:
dude = Mine.new
dude.my_number 1
dude.my_1
=> 1
cette réponse ne pollue pas l'espace de classe, par exemple .. si je fais mine.my_number 4
, alors les autres instances de Mine
n'obtiendront pas la méthode my_4
.. cela se produit car nous utilisons la classe singleton de l'objet à la place de la classe.
class Mine
def my_number num
singleton_class.class_eval { attr_accessor "my_#{num}" }
send("my_#{num}=", num)
end
end
a = Mine.new
b = Mine.new
a.my_number 10 #=> 10
a.my_10 #=> 10
b.my_10 #=> NoMethodError
Ceci peut être accompli en utilisant __send__
. Ici:
class Mine
attr_accessor :some_var
def intialize
@some_var = true
end
def my_number num
self.class.__send__(:attr_accessor, "my_#{num}")
self.__send__("my_#{num}=", num)
end
end
dude = Mine.new
dude.my_number 1
puts dude.my_1
=> 1
Facile. Vous pouvez définir dynamiquement le lecteur d'attributs dans la méthode my_number:
def my_number num
self.instance_variable_set "@my_#{num}", num
self.class.class_eval do
define_method("my_#{num}") { num }
end
end
voir si cela fonctionne pour vous
Il y a un problème avec les deux méthodes ici ... si une variable d'instance est définie dans une instance, son accesseur sera disponible pour toutes les instances, car vous définissez des méthodes sur self.class
plutôt que sur vous-même.
dude = Mine.new
dude.my_number 1
puts dude.my_1
dudette = Mine.new
dudette.my_1 = 2 # works, but probably shouldn't
dudette.my_number 2
dude.my_2 = 3 # works, but probably shouldn't
Ce que vous voulez probablement faire est de ne modifier que l'instance qui contient la variable d'instance:
class Mine
# ...
def my_number num
class << self
attr_accessor "my_#{num}"
end
self.send("my_#{num}=", num)
end
end
De cette manière, les variables d'instance obtiennent uniquement des accesseurs sur les objets pour lesquels elles ont été créées. Je ne me suis pas non plus préoccupé d'instance_variable_set, car si vous définissez un accesseur, je pense qu'il est préférable de le réutiliser. Mais c'est un appel de style. La grosse affaire ici appelle class << self
au lieu de self.class
.
Vous voudrez peut-être utiliser OpenStruct:
require "ostruct"
class Mine < OpenStruct
end
dude = Mine.new
dude.my_number = 1
dude.my_number # => 1
Je ne sais pas pourquoi vous voudriez que dude.my_1
renvoie 1 - n'est-ce pas vous rendre ce que vous avez déjà?
Encore une autre solution à ajouter à la pile, define_singleton_method
:
class Mine
def my_number num
define_singleton_method("num_#{num}") { num }
end
end
Un effet secondaire de toutes ces solutions est que si vous l'appelez plusieurs fois avec des numéros différents, vous vous retrouvez avec un tas de méthodes sur votre objet:
dude = Mine.new
dude.my_number 1
dude.my_number 5
dude.my_1
=> 1
dude.my_5
=> 5
Nous pouvons résoudre ce problème en supprimant l'ancienne méthode:
class Mine
def my_number num
old_num = @num
if @num
# need to use `old_num` local variable
# instance var scope is different inside `class_eval`
singleton_class.class_eval { remove_method("num_#{old_num}") }
end
@num = num
define_singleton_method("num_#{num}") { @num }
end
end
fil plus ancien, mais je l’ai trouvé utile merci. Voici la réponse du code Dorkus Prime, mais prenant aussi des variables d'instance de name\values dans un hachage
@cookies = browser.cookies.to_a
@cookies.each do |cookie|
self.class.__send__(:attr_accessor, "#{cookie[:name]}")
self.__send__("#{cookie[:name]}=",cookie[:value])
end