web-dev-qa-db-fra.com

Comment définir un attr_accessor pour une variable d'instance dynamique?

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
31
eywu

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
27
Orlando

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
23
Dorkus Prime

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

10
Gerry

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.

5
carpeliam

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à?

3
Andrew Grimm

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

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