class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
me da el error:
SyntaxError: error de asignación constante dinámica
¿Por qué se considera esto una constante dinámica? Solo le estoy asignando una cadena.
class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
me da el error:
SyntaxError: error de asignación constante dinámica
¿Por qué se considera esto una constante dinámica? Solo le estoy asignando una cadena.
Respuestas:
Su problema es que cada vez que ejecuta el método está asignando un nuevo valor a la constante. Esto no está permitido, ya que hace que la constante no sea constante; a pesar de que el contenido de la cadena es el mismo (por el momento, de todos modos), el objeto de la cadena en sí es diferente cada vez que se llama al método. Por ejemplo:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Quizás si explicara su caso de uso, por qué desea cambiar el valor de una constante en un método, podríamos ayudarlo con una mejor implementación.
¿Quizás prefiera tener una variable de instancia en la clase?
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 realmente desea cambiar el valor de una constante en un método, y su constante es una Cadena o una Matriz, puede 'engañar' y usar el #replace
método para hacer que el objeto tome un nuevo valor sin cambiar realmente el objeto:
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
. Es uno de esos casos en los que Ruby no tiene una forma simple.
@variable
), no una constante. De lo contrario, volvería a asignar DB
cada vez que creara una instancia nueva de esa clase.
Sequel.connect
a una constante llamada DB . De hecho, la documentación dice explícitamente que eso es solo una recomendación. Eso no me parece una restricción externa.
Debido a que las constantes en Ruby no están destinadas a ser cambiadas, Ruby lo desalienta de asignarles partes de código que podrían ejecutarse más de una vez, como los métodos internos.
En circunstancias normales, debe definir la constante dentro de la propia clase:
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT #=> "foo"
Si por alguna razón realmente necesita definir una constante dentro de un método (quizás para algún tipo de metaprogramación), puede usar 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"
Sin embargo, una vez más, const_set
no es algo a lo que realmente deba recurrir en circunstancias normales. Si no está seguro de si realmente desea asignar a las constantes de esta manera, puede considerar una de las siguientes alternativas:
Las variables de clase se comportan como constantes de muchas maneras. Son propiedades en una clase, y son accesibles en subclases de la clase en la que se definen.
La diferencia es que las variables de clase deben modificarse y, por lo tanto, pueden asignarse a métodos internos sin problemas.
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"
Los atributos de clase son una especie de "variable de instancia en una clase". Se comportan un poco como las variables de clase, excepto que sus valores no se comparten con las subclases.
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"
Y solo para completar, probablemente debería mencionar: si necesita asignar un valor que solo se puede determinar después de que se haya instanciado su clase, hay una buena probabilidad de que realmente esté buscando una variable de instancia antigua simple.
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
En Ruby, cualquier variable cuyo nombre comience con una letra mayúscula es una constante y solo puede asignarle una vez. Elija una de estas alternativas:
class MyClass
MYCONSTANT = "blah"
def mymethod
MYCONSTANT
end
end
class MyClass
def mymethod
my_constant = "blah"
end
end
Las constantes en ruby no se pueden definir dentro de los métodos. Vea las notas al final de esta página, por ejemplo
No puede nombrar una variable con letras mayúsculas o Ruby asumirá que es una constante y querrá que mantenga su valor constante, en cuyo caso cambiar su valor sería un error, un "error de asignación dinámica constante". Con minúsculas debería estar bien
class MyClass
def mymethod
myconstant = "blah"
end
end
A Ruby no le gusta que esté asignando la constante dentro de un método porque se arriesga a una reasignación. Varias respuestas SO ante mí dan la alternativa de asignarlo fuera de un método, pero en la clase, que es un mejor lugar para asignarlo.
Muchas gracias a Dorian y Phrogz por recordarme sobre el método de matriz (y hash) #replace, que puede "reemplazar el contenido de una matriz o hash".
La noción de que el valor de un CONSTANTE se puede cambiar, pero con una advertencia molesta, es uno de los pocos pasos erróneos conceptuales de Ruby: estos deben ser completamente inmutables o deshacerse por completo de la idea constante. Desde la perspectiva de un codificador, una constante es declarativa e intencional, una señal a otro de que "este valor es realmente inmutable una vez declarado / asignado".
Pero a veces una "declaración obvia" en realidad excluye otras oportunidades útiles futuras. Por ejemplo...
No son casos de uso legítimo en realidad podría ser necesario cambiar el valor de un "constante de": por ejemplo, volver a la carga ARGV desde un indicador de circuito REPL similar, volviendo a ejecutar a través de ARGV más OptionParser.parse (posterior)! llamadas - voila! Da a "argumentos de línea de comando" una utilidad dinámica completamente nueva.
El problema práctico es ya sea con la suposición de presunción de que "ARGV debe ser una constante", o en el propio método initialize de optparse, que duros códigos de la asignación de ARGV a la @default_argv ejemplo var para su posterior procesamiento - que array (ARGV) realmente debe ser un parámetro que fomente la repetición y reutilización, cuando corresponda. La parametrización adecuada, con un valor predeterminado apropiado (por ejemplo, ARGV) evitaría la necesidad de cambiar el ARGV "constante". Solo algunos pensamientos de 2 ¢ ...