Forum: Rails France Génération dynamique de méthodes

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Julien B. (Guest)
on 2007-02-12 17:45
(Received via mailing list)
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
Luc H. (Guest)
on 2007-02-12 18:06
(Received via mailing list)
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.
Frédéric Logier (Guest)
on 2007-02-12 18:21
(Received via mailing list)
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) {}
Julien B. (Guest)
on 2007-02-12 18:25
(Received via mailing list)
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.
Julien B. (Guest)
on 2007-02-12 18:28
(Received via mailing list)
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.
Jean-François Trân (Guest)
on 2007-02-12 18:36
(Received via mailing list)
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.
Jean-François Trân (Guest)
on 2007-02-12 18:57
(Received via mailing list)
> > > 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.
Julien B. (Guest)
on 2007-02-12 18:59
(Received via mailing list)
Jean-François Trân wrote:
>  def initialize( values = {} )
>    end
>  end
> end
>
>    -- Jean-François.
>
>
ah je crois que j'ai ma solution :) j'aurais dû penser au bloc statique...

merci beaucoup,

Julien
Julien B. (Guest)
on 2007-02-12 19:02
(Received via mailing list)
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 :)
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.
Jean-François Trân (Guest)
on 2007-02-12 19:18
(Received via mailing list)
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 :)

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.
This topic is locked and can not be replied to.