No dice que la constante es dinámica. Dice que la asignación es dinámica.
sepp2k
Respuestas:
141
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?
classMyClassclass<<self
attr_accessor :my_constant
enddef my_method
self.class.my_constant ="blah"endend
p MyClass.my_constant #=> nilMyClass.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 #replacemétodo para hacer que el objeto tome un nuevo valor sin cambiar realmente el objeto:
classMyClass
BAR ="blah"def cheat(new_bar)
BAR.replace new_bar
endend
p MyClass::BAR #=> "blah"MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
El OP nunca dijo que quería cambiar el valor de la constante, sino que solo quería asignar un valor. El caso de uso frecuente que conduce a este error de Ruby es cuando construye el valor en un método a partir de otros activos de tiempo de ejecución (variables, argumentos de línea de comandos, ENV), generalmente en un constructor, por ejemplo 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.
Arnaud Meuret
2
@ArnaudMeuret Para ese caso, desea una variable de instancia (por ejemplo @variable), no una constante. De lo contrario, volvería a asignar DBcada vez que creara una instancia nueva de esa clase.
Ajedi32
2
@ Ajedi32 Esta situación generalmente surge de restricciones externas, no de elecciones de diseño, como mi ejemplo con Sequel. Mi punto es que Ruby permite asignar un valor a una constante en ciertos ámbitos y no en otros. Solía depender del desarrollador elegir sabiamente cuándo realizar la tarea. Ruby cambió en esto. No es para el bien de todos.
Arnaud Meuret
2
@ArnaudMeuret Admito que nunca he usado Sequel antes, así que no puedo decir esto con 100% de certeza, pero solo mirando la documentación de Sequel no veo nada que diga que TIENES que asignar el resultado Sequel.connecta 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.
Ajedi32
@ Ajedi32 1) Nunca escribí eso (nombre de la constante o incluso que tenía que guardarlo en algún lugar) es solo un ejemplo 2) La restricción es que su software puede no tener la información necesaria hasta que normalmente se encuentre en un contexto dinámico .
Arnaud Meuret
69
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:
Sin embargo, una vez más, const_setno 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:
Variables de clase
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.
classMyClassdefself.my_class_variable
@@my_class_variableenddef my_method
@@my_class_variable="foo"endendclassSubClass<MyClassendMyClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClassSubClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClassMyClass.new.my_method
MyClass.my_class_variable #=> "foo"SubClass.my_class_variable #=> "foo"
Atributos de clase
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.
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.
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:
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
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.
Weicome a SO John. Puede considerar mejorar esta respuesta agregando un código de muestra de lo que está describiendo.
Cleptus
0
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 ¢ ...
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:
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?
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:fuente
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 asignarDB
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:
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
: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:Variables de clase
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.
Atributos de clase
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.
Variables de instancia
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.
fuente
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:
fuente
Las constantes en ruby no se pueden definir dentro de los métodos. Vea las notas al final de esta página, por ejemplo
fuente
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
fuente
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.
fuente
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 ¢ ...
fuente