web-dev-qa-db-fra.com

Quand utiliser self dans Model?

Question: quand dois-je utiliser self dans mes modèles dans Rails?

J'ai une méthode set dans l'un de mes modèles.

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    self.active_flag = val
    self.save!
  end
end

Quand je fais ça, tout fonctionne bien. Cependant, quand je fais cela:

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
    save!
  end
end

La valeur active_flag ne change pas, mais échoue silencieusement. Quelqu'un peut-il expliquer?

Je ne trouve pas de doublons, mais si quelqu'un en trouve un, ça va aussi.

68
varatis

Lorsque vous effectuez une action sur l'instance qui appelle la méthode, vous utilisez self.

Avec ce code

class SocialData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
    save!
  end
end

Vous définissez une toute nouvelle variable locale de portée appelée active_flag, la définissant sur la valeur transmise, elle n'est associée à rien, elle est donc rapidement jetée lorsque la méthode se termine comme si elle n'avait jamais existé.

self.active_flag = val

Dit cependant à l'instance de modifier son propre attribut appelé active_flag, au lieu d'une toute nouvelle variable. Voilà pourquoi ça marche.

62
DVG

Cela se produit en raison de la portée. Lorsque vous êtes à l'intérieur d'une méthode et que vous essayez de définir une nouvelle variable comme celle-ci:

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
  end
end

Vous créez une toute nouvelle variable qui réside à l'intérieur de set_active_flag. Dès que l'exécution est terminée, elle disparaît, sans altérer self.active_flag (la variable d'instance réelle) en aucune façon.

CEPENDANT (c'était une source de confusion pour moi): quand vous essayez de lire une variable d'instance dans Ruby, comme ceci:

class SomeData < ActiveRecord::Base
  def whats_my_active_flag
    puts active_flag
  end
end

Vous obtiendrez en fait self.active_flag (la variable d'instance réelle) retournée.


voici pourquoi:

Ruby fera ce qu'il peut pour éviter de renvoyer nil.

  1. Il demande d'abord "est-ce que active_flag existent dans le cadre de whats_my_active_flag?
  2. Il recherche et réalise que la réponse est "non", donc il saute vers le haut d'un niveau, à l'instance de SomeData
  3. Il demande à nouveau la même chose: "Est-ce que active_flag existe-t-il dans ce domaine?
  4. La réponse est "yup" et donc ça dit "j'ai quelque chose pour toi" et ça retourne ça!

Cependant, si vous définissez active_flag dans le whats_my_active_flag, puis demandez-le, il recommence les étapes:

  1. Il demande "ne active_flag existent dans le cadre de whats_my_active_flag?
  2. La réponse est "yup", donc elle renvoie cette valeur

Dans les deux cas, il ne sera pas changer la valeur de self.active_flag sauf si vous le lui demandez explicitement.

Un moyen simple de décrire ce comportement est "il ne veut pas vous décevoir" et retourne nil - donc il fait de son mieux pour trouver ce qu'il peut.

Dans le même temps, "il ne veut pas gâcher des données que vous n'aviez pas l'intention de modifier", il ne modifie donc pas la variable d'instance elle-même.

J'espère que cela t'aides!

54
Yuval Karmi

C'est pour vous assurer que vous utilisez la méthode setter et que vous ne définissez pas la portée d'une nouvelle variable. C'est un Ruby et détail d'utilisation AR qui fait souvent trébucher les gens (l'autre étant la (mauvaise) utilisation d'une variable d'instance).

Notez qu'il y a déjà pdate_attributes! bien que je comprenne le désir d'abstraire.

Il y a aussi bascule! , ce qui pourrait être encore plus agréable, selon votre interface avec le drapeau.

1
Dave Newton

Lorsque vous utilisez active_flag = val Ruby pensait que vous définissez une variable locale, la meilleure façon est self.active_flag = val, Si vous l'avez, j'espère que vous savez que send(:active_flag=, val) fonctionnera aussi.

0
fangxing