Não diz que a constante é dinâmica. Diz que a tarefa é dinâmica.
sepp2k
Respostas:
141
Seu problema é que, toda vez que você executa o método, está atribuindo um novo valor à constante. Isso não é permitido, pois torna a constante não constante; mesmo que o conteúdo da string seja o mesmo (no momento, de qualquer maneira), o próprio objeto da string é diferente sempre que o método é chamado. Por exemplo:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Talvez se você explicou seu caso de uso - por que deseja alterar o valor de uma constante em um método - poderíamos ajudá-lo com uma melhor implementação.
Talvez você prefira ter uma variável de instância na classe?
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"
Se você realmente deseja alterar o valor de uma constante em um método, e sua constante é uma String ou uma Matriz, você pode 'trapacear' e usar o #replacemétodo para fazer com que o objeto adquira um novo valor sem realmente alterar o 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"
O OP nunca disse que queria alterar o valor da constante, mas apenas queria atribuir um valor. O caso de uso frequente que leva a esse erro Ruby é quando você cria o valor em um método a partir de outros ativos em tempo de execução (variáveis, argumentos de linha de comando, ENV), geralmente em um construtor, por exemplo def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end. É um daqueles casos em que Ruby não tem um caminho simples.
Arnaud Meuret 6/03/2013
2
@ArnaudMeuret Nesse caso, você deseja uma variável de instância (por exemplo @variable), não uma constante. Caso contrário, você seria redesignado DBtoda vez que instanciasse uma nova instância dessa classe.
precisa saber é o seguinte
2
@ Ajedi32 Esta situação geralmente surge de restrições externas e não de escolhas de design, como no meu exemplo com Sequel. Meu argumento é que atribuir um valor a uma constante é permitido por Ruby em certos escopos e não em outros. Costumava ser do desenvolvedor escolher sabiamente quando executar a tarefa. Ruby mudou nisso. Não é para todo mundo.
Arnaud Meuret 06/12/2013
2
@ArnaudMeuret Admito que nunca usei Sequel antes, então não posso dizer isso com 100% de certeza, mas apenas olhando a documentação para Sequel não vejo nada que diga que você DEVE atribuir o resultado Sequel.connecta uma constante chamada DB . De fato, a documentação diz explicitamente que isso é apenas uma recomendação. Isso não parece uma restrição externa para mim.
Ajedi32
@ Ajedi32 1) Eu nunca escrevi que (nome da constante ou mesmo que você precisava mantê-la em algum lugar) é apenas um exemplo 2) A restrição é que seu software pode não ter as informações necessárias até que você esteja em um contexto dinâmico .
Arnaud Meuret
69
Como as constantes no Ruby não devem ser alteradas, o Ruby o desencoraja de atribuir a elas partes do código que podem ser executadas mais de uma vez, como métodos internos.
Sob circunstâncias normais, você deve definir a constante dentro da própria classe:
Se, por algum motivo, você realmente precisar definir uma constante dentro de um método (talvez para algum tipo de metaprogramação), poderá usar const_set:
Novamente, porém, const_setnão é algo que você realmente precise recorrer em circunstâncias normais. Se você não tiver certeza se deseja realmente atribuir as constantes dessa maneira, considere uma das seguintes alternativas:
Variáveis de classe
Variáveis de classe se comportam como constantes de várias maneiras. São propriedades em uma classe e são acessíveis nas subclasses da classe em que estão definidas.
A diferença é que as variáveis de classe devem ser modificáveis e, portanto, podem ser atribuídas a métodos internos sem 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 classe
Atributos de classe são uma espécie de "variável de instância em uma classe". Eles se comportam um pouco como variáveis de classe, exceto que seus valores não são compartilhados com subclasses.
E, para completar, eu provavelmente devo mencionar: se você precisar atribuir um valor que só pode ser determinado após a instanciação da sua classe, há uma boa chance de você estar realmente procurando por uma variável de instância antiga simples.
No Ruby, qualquer variável cujo nome comece com uma letra maiúscula é uma constante e você pode atribuir a ela apenas uma vez. Escolha uma destas alternativas:
Você não pode nomear uma variável com letras maiúsculas ou o Ruby assumirá sua constante e desejará que ela mantenha seu valor constante; nesse caso, alterar seu valor seria um erro "erro de atribuição dinâmica constante". Com minúsculas deve ficar bem
Ruby não gosta que você esteja atribuindo a constante dentro de um método porque corre o risco de ser redesignada. Várias respostas do SO antes de mim oferecem a alternativa de atribuí-lo fora de um método - mas na classe, que é um lugar melhor para atribuí-lo.
Bem-vindo a SO John. Você pode melhorar essa resposta adicionando um código de amostra do que está descrevendo.
Cleptus
0
Muito obrigado a Dorian e Phrogz por me lembrar sobre o método array (e hash) #replace, que pode "substituir o conteúdo de um array ou hash".
A noção de que o valor de um CONSTANT pode ser alterado, mas com um aviso irritante, é um dos poucos erros conceituais do Ruby - eles devem ser totalmente imutáveis ou despejar completamente a idéia constante. Do ponto de vista de um codificador, uma constante é declarativa e intencional, um sinal para outros de que "esse valor é realmente imutável uma vez declarado / atribuído".
Mas, às vezes, uma "declaração óbvia" na verdade exclui outras oportunidades úteis futuras. Por exemplo...
Não são casos de uso legítimos onde o valor de um "constante" pode realmente precisam ser alterado: por exemplo, re-loading ARGV partir de uma linha de circuito REPL-like, em seguida, executar novamente ARGV através de mais (subsequente) OptionParser.parse! chama - voila! Dá à "linha de comando args" um novo utilitário dinâmico.
O problema prático é tanto com a suposição presuntivo que "ARGV deve ser uma constante", ou no próprio método de inicialização do optparse, que rígidos códigos a atribuição de ARGV ao @default_argv instância var para posterior processamento - que array (ARGV) realmente deve ser um parâmetro, incentivando a re-análise e reutilização, quando apropriado. A parametrização adequada, com um padrão apropriado (por exemplo, ARGV) evitaria a necessidade de alterar o ARGV "constante". Apenas alguns 2 ¢ de pensamentos ...
Respostas:
Seu problema é que, toda vez que você executa o método, está atribuindo um novo valor à constante. Isso não é permitido, pois torna a constante não constante; mesmo que o conteúdo da string seja o mesmo (no momento, de qualquer maneira), o próprio objeto da string é diferente sempre que o método é chamado. Por exemplo:
Talvez se você explicou seu caso de uso - por que deseja alterar o valor de uma constante em um método - poderíamos ajudá-lo com uma melhor implementação.
Talvez você prefira ter uma variável de instância na classe?
Se você realmente deseja alterar o valor de uma constante em um método, e sua constante é uma String ou uma Matriz, você pode 'trapacear' e usar o
#replace
método para fazer com que o objeto adquira um novo valor sem realmente alterar o objeto:fonte
def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end
. É um daqueles casos em que Ruby não tem um caminho simples.@variable
), não uma constante. Caso contrário, você seria redesignadoDB
toda vez que instanciasse uma nova instância dessa classe.Sequel.connect
a uma constante chamada DB . De fato, a documentação diz explicitamente que isso é apenas uma recomendação. Isso não parece uma restrição externa para mim.Como as constantes no Ruby não devem ser alteradas, o Ruby o desencoraja de atribuir a elas partes do código que podem ser executadas mais de uma vez, como métodos internos.
Sob circunstâncias normais, você deve definir a constante dentro da própria classe:
Se, por algum motivo, você realmente precisar definir uma constante dentro de um método (talvez para algum tipo de metaprogramação), poderá usar
const_set
:Novamente, porém,
const_set
não é algo que você realmente precise recorrer em circunstâncias normais. Se você não tiver certeza se deseja realmente atribuir as constantes dessa maneira, considere uma das seguintes alternativas:Variáveis de classe
Variáveis de classe se comportam como constantes de várias maneiras. São propriedades em uma classe e são acessíveis nas subclasses da classe em que estão definidas.
A diferença é que as variáveis de classe devem ser modificáveis e, portanto, podem ser atribuídas a métodos internos sem problemas.
Atributos de classe
Atributos de classe são uma espécie de "variável de instância em uma classe". Eles se comportam um pouco como variáveis de classe, exceto que seus valores não são compartilhados com subclasses.
Variáveis de instância
E, para completar, eu provavelmente devo mencionar: se você precisar atribuir um valor que só pode ser determinado após a instanciação da sua classe, há uma boa chance de você estar realmente procurando por uma variável de instância antiga simples.
fonte
No Ruby, qualquer variável cujo nome comece com uma letra maiúscula é uma constante e você pode atribuir a ela apenas uma vez. Escolha uma destas alternativas:
fonte
As constantes em ruby não podem ser definidas dentro de métodos. Veja as notas na parte inferior desta página, por exemplo
fonte
Você não pode nomear uma variável com letras maiúsculas ou o Ruby assumirá sua constante e desejará que ela mantenha seu valor constante; nesse caso, alterar seu valor seria um erro "erro de atribuição dinâmica constante". Com minúsculas deve ficar bem
fonte
Ruby não gosta que você esteja atribuindo a constante dentro de um método porque corre o risco de ser redesignada. Várias respostas do SO antes de mim oferecem a alternativa de atribuí-lo fora de um método - mas na classe, que é um lugar melhor para atribuí-lo.
fonte
Muito obrigado a Dorian e Phrogz por me lembrar sobre o método array (e hash) #replace, que pode "substituir o conteúdo de um array ou hash".
A noção de que o valor de um CONSTANT pode ser alterado, mas com um aviso irritante, é um dos poucos erros conceituais do Ruby - eles devem ser totalmente imutáveis ou despejar completamente a idéia constante. Do ponto de vista de um codificador, uma constante é declarativa e intencional, um sinal para outros de que "esse valor é realmente imutável uma vez declarado / atribuído".
Mas, às vezes, uma "declaração óbvia" na verdade exclui outras oportunidades úteis futuras. Por exemplo...
Não são casos de uso legítimos onde o valor de um "constante" pode realmente precisam ser alterado: por exemplo, re-loading ARGV partir de uma linha de circuito REPL-like, em seguida, executar novamente ARGV através de mais (subsequente) OptionParser.parse! chama - voila! Dá à "linha de comando args" um novo utilitário dinâmico.
O problema prático é tanto com a suposição presuntivo que "ARGV deve ser uma constante", ou no próprio método de inicialização do optparse, que rígidos códigos a atribuição de ARGV ao @default_argv instância var para posterior processamento - que array (ARGV) realmente deve ser um parâmetro, incentivando a re-análise e reutilização, quando apropriado. A parametrização adequada, com um padrão apropriado (por exemplo, ARGV) evitaria a necessidade de alterar o ARGV "constante". Apenas alguns 2 ¢ de pensamentos ...
fonte