Génération dynamique de méthodes


#1

Bonjour,

Je souhaiterais utiliser la réflexivité de ruby pour créer dynamiquement
des accesseurs en lecture et écriture à partir d’un tableau constant
(qui contient à peu près une trentaine d’éléments).

%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<

class Privacy

KEYS = %w{ email address ...}

def initialize( values = {} )
[...]
    generate_methods
end

def generate_methods
    ( class << self; self; end ).class_eval {
      KEYS.each do | key |
        define_method( key ){ # |*args|
          @values[ key ]
        }
        define_method( key + '=' ){ |*args|
          @values[ key ] = args[ 0 ]
        }
      end
    }
  end

[…]

end

%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<

Ce qui m’embête c’est que cette création d’accesseurs est faite depuis
le constructeur, et donc à chaque fois qu’un objet est créé. Or je
voudrais, si cela est possible, que la génération de ces méthodes ne
soit faite qu’une seule fois et qu’elle soit valable pour toutes les
instances. Pour cela, je pensais exécuter le code ci-dessus dans un
contexte statique, via l’utilisation par exemple de
public_instance_methods. J’ai cherché dans le code de Rails pour
s’inspirer de la création dynamique des accesseurs liés aux champs en
base mais sans succés. Si quelqu’un pouvait m’aider…

Merci,

Julien


#2

On 12 févr. 07, at 16:44, Julien B. wrote:

Ce qui m’embête c’est que cette création d’accesseurs est faite depuis
le constructeur, et donc à chaque fois qu’un objet est créé. Or je
voudrais, si cela est possible, que la génération de ces méthodes ne
soit faite qu’une seule fois et qu’elle soit valable pour toutes les
instances.

Qu’est-ce que tu as contre attr_accessor ?

class Privacy
attr_accessor :email
attr_accessor :address

end

Et si tu tiens réellement à ton tableau constant:

class Privacy
KEYS = %w{ email address … }
KEYS.each do |attr|
attr_accessor attr
end
end

Non ?


Luc H.


#3

Le 12/02/07, Julien B. a écrit :

        define_method( key ){ # |*args|

public_instance_methods. J’ai cherché dans le code de Rails pour
s’inspirer de la création dynamique des accesseurs liés aux champs en
base mais sans succés. Si quelqu’un pouvait m’aider…

Si j’ai bien compris, si tu veux créer à la volée des méthodes pour
toutes
tes instances il te faut créer des méthodes de classe :

self.class.send(:define_method, :tamethode) {}


#4

Luc H. wrote:

On 12 févr. 07, at 16:44, Julien B. wrote:

Ce qui m’embête c’est que cette création d’accesseurs est faite depuis
le constructeur, et donc à chaque fois qu’un objet est créé. Or je
voudrais, si cela est possible, que la génération de ces méthodes ne
soit faite qu’une seule fois et qu’elle soit valable pour toutes les
instances.

Qu’est-ce que tu as contre attr_accessor ?
rien mais en l’occurrence l’accesseur est créé dynamiquement par rapport
à la hash @values.


#5

Frédéric Logier wrote:

base mais sans succés. Si quelqu'un pouvait m'aider...

Si j’ai bien compris, si tu veux créer à la volée des méthodes pour
toutes tes instances il te faut créer des méthodes de classe :

self.class.send(:define_method, :tamethode) {}
sauf que j’aurais voulu créer des accesseurs et donc des méthodes
d’instance. En gros modifier public_instance_methods, mais j’ai
l’impression que ça n’a pas l’air trop possible.


#6

Luc :

 attr_accessor :address

end

Non ?

Pour une raison ou pour une autre, qui lui est propre, Julien
veut passer par une variable d’instance @value qui est un
Hash ou un Hash-like.

Sinon on peut faire comme ça :

class Privacy
KEYS = %w{ email address ga bu zo meu }

def initialize( values = {} )
@values = values
end

KEYS.each do |key|
define_method(key) do
@values[key]
end

define_method("#{key}=") do |arg|
  @values[key] = arg
end

end
end

-- Jean-François.

#7

Ce qui m’embête c’est que cette création d’accesseurs est
faite depuis le constructeur, et donc à chaque fois qu’un objet
est créé. Or je voudrais, si cela est possible, que la génération
de ces méthodes ne soit faite qu’une seule fois et qu’elle soit
valable pour toutes les instances.

Sinon on peut faire comme ça : […]

Ou en plus générique comme ça :

class Class

flemme de trouver un nom

def julien(*syms)
syms.each do |sym|
class_eval(<<-EOS, FILE, LINE)
def #{sym}
@values[’#{sym}’]
end

    def #{sym}=(arg)
      @values['#{sym}'] = arg
    end
  EOS
end

end
end

class Privacy
KEYS = %w(ga bu zo meu)
julien *KEYS
julien :foo

def initialize(values = {} )
@values = values
end
end

– Jean-François.


#8

Jean-François Trân wrote:

   end

KEYS = %w(ga bu zo meu)
julien *KEYS
julien :foo

def initialize(values = {} )
@values = values
end
end

– Jean-François.

Oula, on aurait presque l’impression que tu écris dans le fichier la
définition de la classe, je vais rester sur l’autre solution je crois :slight_smile:
Peux-tu me dire quelle solution est retenue pour AR::B ? J’imagine vu
les SHOW FIELDS que le comportement n’est pas le même en dév qu’en prod.


#9

Jean-François Trân wrote:

def initialize( values = {} )
end
end
end

– Jean-François.

ah je crois que j’ai ma solution :slight_smile: j’aurais dû penser au bloc statique…

merci beaucoup,

Julien


#10

Julien :

Oula, on aurait presque l’impression que tu écris dans le
fichier la définition de la classe, je vais rester sur l’autre
solution je crois :slight_smile:

Ouais. En plus toutes les classes n’ont pas forcément
de variables d’instance @values pour leurs instances, donc…

Peux-tu me dire quelle solution est retenue pour AR::B ?

Pour les attributs (au sens AR::B, ie colonnes de la table) ?
ça passe par method_missing (*) et les méthodes readers sont
éventuellement générés automatiquement la première fois
qu’on veut lire un attribut, selon le positionnement d’une
variable de class (@@generate_read_methods). Mais pas
pour les méthodes writers.

  • : le method_missing d’instance, j’ai envie de dire.

J’imagine vu les SHOW FIELDS que le comportement
n’est pas le même en dév qu’en prod.

Quand les informations sur les colonnes sont mises à jour,
les méthodes readers sont dégagées. Et on ne les met
pas à jour en mode prod.

– Jean-François.