Self.attr et attr

Bonjour,

Lorque je fais mes test, je ne comprends pas pourquoi le code suivant
fonctionne (value vaut ok en base):

class UserNetwork
before_create :set_value

protected
    def set_value
        self.value = 'ok' unless self.value
    end

end

alors que celui-ci non (value en base vaut null) :

class UserNetwork
before_create :set_value

protected
    def set_value
       value = 'ok' unless value
    end

end

Pour moi self.value et value correspondent tous les deux aux mêmes
accesseurs, mais apparemment ça n’est pas le cas…

Merci d’avance,

Julien

Julien :

class UserNetwork
before_create :set_value

protected
    def set_value
       value = 'ok' unless value
    end

end

Parce qu’ici, value est une variable locale à #set_value.

РJean-Fran̤ois.

Jean-François wrote:

Parce qu’ici, value est une variable locale à #set_value.

РJean-Fran̤ois.

Dans ce cas, pourquoi je n’ai pas une erreur disant que value n’est pas
définie ? Dans quel cas cette règle est-elle valable ? Uniquement dans
les callbacks ?

Cela me perturbe tout ça :confused:

Merci pour ton aide,

Julien

Salut pour que ca fonctionne tu devrais faire :

def set_value
@value ||= ‘ok’
end

Mickael

2007/1/25, Jean-François [email protected]:

Julien B. a écrit :

Parce qu’ici, value est une variable locale à #set_value.

РJean-Fran̤ois.

Dans ce cas, pourquoi je n’ai pas une erreur disant que value n’est pas
définie ? Dans quel cas cette règle est-elle valable ? Uniquement dans
les callbacks ?

Ben la différence que tu as, c’est que ‘value’ est une variable. Un
callback va essayer d’appeler une méthode, et l’erreur sera produite au
moment où tu essaieras d’appeler une méthode non existante. Avec ruby
dans le cas d’une variable qui n’existe pas, il te la crée. C’est
d’ailleurs ce que tu lui demandes de faire ici. Tu lui dis : si value ne
vaut rien (ou nil, et c’est le cas puisqu’elle n’est pas définie) alors
défini value avec comme valeur ‘ok’.

Par contre tu pourrais effectivement avec des problèmes en essayant
d’utiliser une variable non initialisée si par exemple tu essayais
d’appeler une méthode dessus. Je sais pas si je suis très clair (mais
j’essaie de simplifier pourtant) :slight_smile:

Mickael G. wrote:

Dans quel cas cette règle est-elle valable ? 

Je ne vois pas de quelle règle tu parles.

En fait je en vois pas pourquoi l’appel de value dans validate renvoie
la valeur et que dans un callback il fait référence à une variable locale…

Hello !

Julien B. a écrit :

Pour moi self.value et value correspondent tous les deux aux mêmes
accesseurs, mais apparemment ça n’est pas le cas…

En Ruby, absolument pas. Tout champ étant privé, il ne peut être accédé
que si tu fournis explicitement self comme receveur. Dans le cas
contraire, Ruby considère que tu utilises une variable locale.

C’est une bévue très fréquente quand on débute :slight_smile:

Dans ce cas, pourquoi je n’ai pas une erreur disant que value n’est pas
définie ?

Il n’y a aucune raison que tu aies une erreur. tu a uniquement instancié
une
variable appelée value au sein de ta méthode set_status. ici “value” n’a
rien a voir avec la variable d’instance @value ou self.value .

Dans quel cas cette règle est-elle valable ?

Je ne vois pas de quelle règle tu parles.
Mickael

Le Jeu 25 janvier 2007 15:11, Julien B. a écrit :

        self.value = 'ok' unless self.value
    end

end

Les différents cas :

  • 1 (value = ‘ok’) : Tu vas créer ou utiliser une variable locale
    nommée"value" et tu vas lui affecter ‘ok’
  • 2 (self.value = ‘ok’) : Tu vas utiliser méthode d’instance (l’instance
    étant “self”) nommée “value=” et tu vas lui passer ‘ok’ en paramètre
  • 3 (@value = ‘ok’) : Tu vas créer ou utiliser une variable d’instance (ce
    qu’on appelle ailleurs un attribut) et tu vas lui affecter ‘ok’

Le cas (1) n’impacte pas ta base parce que c’est une variable locale, à la
fin de la méthode set_value() ta variable locale est
effacée/oubliée
Le cas (2) impacte ta base parce que tu utilises l’accesseur automatique
de active record qui est fait pour affecter des données à partir de
l’instance. Tu agis comme s’il s’agissait d’une opération externe
(utilisation de l’interface publique)

Le cas (3) peut fonctionner si ActiveRecord utilise les attributs pour
stocker les valeurs et les relire (je crois que ça ne fonctionne plus mais
je peux me tromper). Il s’agit cependant d’une procédure à éviter pour
lire un attribut de base dans un modèle ActiveRecord (si ça fonctionne
çautilise de toutes façons un comportement privé qui changera
potentiellement à l’avenir). La bonne méthode est d’utiliser les fonctions
prévues pour, voir ci-dessous :

  • 4 (write_attribute(:value, ‘ok’). “write_attribute()” est une méthode
    interne propre à ActiveRecord justement pour te permettre d’accéder à
    l’écriture d’un attribut de ta base sans utiliser l’accesseur public. Là
    ça devrait faire ce que tu veux.

Le résultat c’est :

  • si tu veux passer par la méthode publique, qui exécute éventuellement du
    code à toi avant d’enregistrer effectivement ta valeur, alors utilises le
    (2)
  • si tu veux entrer une valeur brute sans vérification et interaction,
    utilises le (4)

Voir http://railsmanual.org/class/ActiveRecord%3A%3ABase


Éric Daspet
http://eric.daspet.name/

Julien :

Dans ce cas, pourquoi je n’ai pas une erreur disant que value
n’est pas définie ?

Tu n’as pas besoin de définir ou de déclarer les variables
avec Ruby, tu l’as certainement remarqué aussi pour les
variables d’instance, que l’on utilise à la volée dans une
action d’un contrôleur, sans les avoir définis dans un
éventuel constructeur.

Dans quel cas cette règle est-elle valable ?
Uniquement dans les callbacks ?

Quand tu as une méthode dont le nom finit par égal (foo=),
tu bénéficies du sucre syntaxique qui te permet d’écrire
(ici avec bar le receveur)

bar.foo = ‘hello’

ie de séparer le = du reste du nom de la méthode.

D’autre part, en Ruby, on peut envoyer des messages sans
spécifier le receveur, celui-ci étant alors implicitement self.
Ceci évite d’écrire des choses pythoniques (même si on peut
toujours le faire) :

self.baz(self.foo + self.bar)

Mais si on est en implicite, si on écrit : foo
et que cet appel se fait sans paramètres, alors
il y a ambiguïté avec l’utilisation d’une éventuelle variable
locale foo.

Maintenant si on combine les deux choses,
quand on écrit foo = bar, est-ce un appel de méthode
(self#foo= ) ou l’affectation (éventuellement création à la
volée) de la variable foo ? là encore il y a ambiguïté.

On lève l’ambiguïté si on spécifie le receveur :
self.foo = bar

РJean-Fran̤ois.

Mickael :

def set_value
@value ||= ‘ok’
end

Ici Julien manipule l’attribut value, ie la colonne de sa table
user_networks et non une variable d’instance de la classe
UserNetwork.

Un moyen pour ne pas se tromper est d’utiliser write_attribute

РJean-Fran̤ois.

Jean-François wrote:

Parce qu’ici, value est une variable locale à #set_value.
Dans quel cas cette règle est-elle valable ?
D’autre part, en Ruby, on peut envoyer des messages sans

Maintenant si on combine les deux choses,
quand on écrit foo = bar, est-ce un appel de méthode
(self#foo= ) ou l’affectation (éventuellement création à la
volée) de la variable foo ? là encore il y a ambiguïté.

On lève l’ambiguïté si on spécifie le receveur :
self.foo = bar

РJean-Fran̤ois.

ah voilà en effet l’ambiguité qui me perturbait ! merci beaucoup à tous
:slight_smile:

on est d’accord que cette ambiguité n’existe pas sur la lecture ? si
l’accesseur en lecture value existe ce sera celui-là qui sera appelé et
non une variable non définie ?

Eric :

globalement d’accord avec toi, mais je tiens à préciser certaines choses

Les différents cas :

  • 1 (value = ‘ok’) : Tu vas créer ou utiliser une variable locale nommée
    “value” et tu vas lui affecter ‘ok’
  • 2 (self.value = ‘ok’) : Tu vas utiliser méthode d’instance (l’instance
    étant “self”) nommée “value=” et tu vas lui passer ‘ok’ en paramètre
  • 3 (@value = ‘ok’) : Tu vas créer ou utiliser une variable d’instance (ce
    qu’on appelle ailleurs un attribut) et tu vas lui affecter ‘ok’

Ouais. C’est aussi l’occasion de lever une autre ambiguïté, cette
fois-ci les attributs.

Au sens Ruby du terme, les variables d’instance sont des attributs.

Au sens ActiveRecord du terme, les attributs correspondent aux
colonnes de la table correspondant au modèle.

Les attributs (au sens AR) sont stockés dans l’attribut (au sens
Ruby, variable d’instance) @attributes de ton modèle fille d’AR::B.

D’autre part, on peut très bien créer des attributs (au sens Ruby)
de son modèle, pour stocker des données qui ne seront pas
persistentes. Exemple : attr_accessor :password dans le
plugin acts_as_authenticated (seule la version chiffrée est
sauvée dans la base). Un exemple similaire dans l’Agile Book.

Enfin, AR utilise des attributs (au sens Ruby) pour stocker
l’état de l’objet (si l’enregistrement n’a pas encore été sauvé
dans la base, les erreurs…)

Ces histoires d’attributs peuvent entraîner une certaine
confusion quand on découvre les macros attr_reader, attr_accessor
attr_accessible, attr_protected…

Le cas (1) n’impacte pas ta base parce que c’est une variable
locale, à la fin de la méthode set_value() ta variable locale est
effacée/oubliée

Le cas (2) impacte ta base parce que tu utilises l’accesseur
automatique de active record qui est fait pour affecter des
données à partir de l’instance. Tu agis comme s’il s’agissait
d’une opération externe (utilisation de l’interface publique)

Le cas (3) peut fonctionner

Je ne crois pas !

si ActiveRecord utilise les attributs pour stocker les valeurs et
les relire (je crois que ça ne fonctionne plus mais je peux me
tromper).

Ah ! ça a fonctionné quand ? Je suis curieux, sur ce coup.

Tout est stocké dans @attributes.

Il s’agit cependant d’une procédure à éviter pour lire un attribut
de base dans un modèle ActiveRecord (si ça fonctionne ça
utilise de toutes façons un comportement privé qui changera
potentiellement à l’avenir).

Oui.

La bonne méthode est d’utiliser les fonctions
prévues pour, voir ci-dessous :

On a oublié aussi (5) self[:value] = ‘ok’

  • si tu veux entrer une valeur brute sans vérification et interaction,
    utilises le (4)

Ou le (5)

РJean-Fran̤ois.