class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
me donne l'erreur:
SyntaxError: erreur d'affectation de constante dynamique
Pourquoi est-ce considéré comme une constante dynamique? Je lui attribue juste une chaîne.
class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
me donne l'erreur:
SyntaxError: erreur d'affectation de constante dynamique
Pourquoi est-ce considéré comme une constante dynamique? Je lui attribue juste une chaîne.
Réponses:
Votre problème est que chaque fois que vous exécutez la méthode, vous attribuez une nouvelle valeur à la constante. Ceci n'est pas autorisé, car cela rend la constante non constante; même si le contenu de la chaîne est le même (pour le moment, en tout cas), l' objet chaîne lui-même est différent à chaque fois que la méthode est appelée. Par exemple:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Peut-être que si vous expliquiez votre cas d'utilisation - pourquoi vous voulez changer la valeur d'une constante dans une méthode - nous pourrions vous aider avec une meilleure implémentation.
Peut-être préférez-vous avoir une variable d'instance sur la classe?
class MyClass
class << self
attr_accessor :my_constant
end
def my_method
self.class.my_constant = "blah"
end
end
p MyClass.my_constant #=> nil
MyClass.new.my_method
p MyClass.my_constant #=> "blah"
Si vous voulez vraiment changer la valeur d'une constante dans une méthode et que votre constante est une chaîne ou un tableau, vous pouvez `` tricher '' et utiliser la #replace
méthode pour que l'objet prenne une nouvelle valeur sans changer réellement l'objet:
class MyClass
BAR = "blah"
def cheat(new_bar)
BAR.replace new_bar
end
end
p MyClass::BAR #=> "blah"
MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end
. C'est l'un de ces cas où Ruby n'a pas de moyen simple.
@variable
), pas une constante. Sinon, vous réassigneriez DB
chaque fois que vous instanciez une nouvelle instance de cette classe.
Sequel.connect
à une constante nommée DB . En fait, la documentation dit explicitement que ce n'est qu'une recommandation. Cela ne me semble pas une contrainte extérieure.
Étant donné que les constantes de Ruby ne sont pas censées être modifiées, Ruby vous déconseille de leur attribuer des parties de code qui pourraient être exécutées plus d'une fois, comme les méthodes internes.
Dans des circonstances normales, vous devez définir la constante à l'intérieur de la classe elle-même:
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT #=> "foo"
Si, pour une raison quelconque, vous avez vraiment besoin de définir une constante à l'intérieur d'une méthode (peut-être pour un type de métaprogrammation), vous pouvez utiliser const_set
:
class MyClass
def my_method
self.class.const_set(:MY_CONSTANT, "foo")
end
end
MyClass::MY_CONSTANT
#=> NameError: uninitialized constant MyClass::MY_CONSTANT
MyClass.new.my_method
MyClass::MY_CONSTANT #=> "foo"
Encore une fois, ce const_set
n'est pas quelque chose auquel vous devriez vraiment avoir recours dans des circonstances normales. Si vous ne savez pas si vous voulez vraiment attribuer des constantes de cette façon, vous pouvez envisager l'une des alternatives suivantes:
Les variables de classe se comportent comme des constantes à bien des égards. Ce sont des propriétés sur une classe et elles sont accessibles dans les sous-classes de la classe sur laquelle elles sont définies.
La différence est que les variables de classe sont censées être modifiables et peuvent donc être affectées à des méthodes internes sans problème.
class MyClass
def self.my_class_variable
@@my_class_variable
end
def my_method
@@my_class_variable = "foo"
end
end
class SubClass < MyClass
end
MyClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass
SubClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass
MyClass.new.my_method
MyClass.my_class_variable #=> "foo"
SubClass.my_class_variable #=> "foo"
Les attributs de classe sont une sorte de "variable d'instance sur une classe". Ils se comportent un peu comme des variables de classe, sauf que leurs valeurs ne sont pas partagées avec les sous-classes.
class MyClass
class << self
attr_accessor :my_class_attribute
end
def my_method
self.class.my_class_attribute = "blah"
end
end
class SubClass < MyClass
end
MyClass.my_class_attribute #=> nil
SubClass.my_class_attribute #=> nil
MyClass.new.my_method
MyClass.my_class_attribute #=> "blah"
SubClass.my_class_attribute #=> nil
SubClass.new.my_method
SubClass.my_class_attribute #=> "blah"
Et juste pour être complet, je devrais probablement mentionner: si vous avez besoin d'attribuer une valeur qui ne peut être déterminée qu'après que votre classe a été instanciée, il y a de bonnes chances que vous recherchiez une variable d'instance ancienne.
class MyClass
attr_accessor :instance_variable
def my_method
@instance_variable = "blah"
end
end
my_object = MyClass.new
my_object.instance_variable #=> nil
my_object.my_method
my_object.instance_variable #=> "blah"
MyClass.new.instance_variable #=> nil
Dans Ruby, toute variable dont le nom commence par une majuscule est une constante et vous ne pouvez lui attribuer qu'une seule fois. Choisissez l'une de ces alternatives:
class MyClass
MYCONSTANT = "blah"
def mymethod
MYCONSTANT
end
end
class MyClass
def mymethod
my_constant = "blah"
end
end
Les constantes de ruby ne peuvent pas être définies dans les méthodes. Voir les notes au bas de cette page, par exemple
Vous ne pouvez pas nommer une variable avec des lettres majuscules ou Ruby assumera sa constante et voudra qu'elle garde sa valeur constante, auquel cas changer sa valeur serait une erreur une "erreur d'affectation de constante dynamique". Avec des minuscules devrait être bien
class MyClass
def mymethod
myconstant = "blah"
end
end
Ruby n'aime pas que vous affectiez la constante à l'intérieur d'une méthode car cela risque de se réaffecter. Plusieurs réponses SO avant moi donnent l'alternative de l'attribuer en dehors d'une méthode - mais dans la classe, qui est un meilleur endroit pour l'attribuer.
Un grand merci à Dorian et Phrogz pour m'avoir rappelé la méthode de tableau (et de hachage) #replace, qui peut "remplacer le contenu d'un tableau ou d'un hachage".
La notion selon laquelle la valeur d'un CONSTANT peut être modifiée, mais avec un avertissement ennuyeux, est l'une des rares erreurs conceptuelles de Ruby - celles-ci devraient soit être totalement immuables, soit vider complètement l'idée de constante. Du point de vue d'un codeur, une constante est déclarative et intentionnelle, un signal à l'autre que «cette valeur est vraiment immuable une fois déclarée / attribuée».
Mais parfois, une «déclaration évidente» exclut en fait d'autres opportunités utiles futures. Par exemple...
Il y a des cas d'utilisation légitimes où la valeur d'une «constante» peut vraiment avoir besoin d'être modifiée: par exemple, recharger ARGV à partir d'une boucle d'invite de type REPL, puis réexécuter ARGV via plus d'OptionParser.parse (suivant)! appels - voila! Donne aux "arguments de ligne de commande" un tout nouvel utilitaire dynamique.
Le problème pratique est soit avec l'hypothèse présumée que "ARGV doit être une constante", soit dans la propre méthode d'initialisation d'optparse, qui code en dur l'affectation d'ARGV à l'instance var @default_argv pour un traitement ultérieur - ce tableau (ARGV) devrait être un paramètre encourageant la ré-analyse et la réutilisation, le cas échéant. Un paramétrage correct, avec une valeur par défaut appropriée (par exemple, ARGV) éviterait de devoir jamais changer l'ARGV «constant». Juste quelques 2 ¢ dignes de réflexion ...