Dynamic constant assignment
In Ruby, any variable whose name starts with a capital letter is a constant and you can only assign to it once. Choose one of these alternatives:
class MyClass
MYCONSTANT = "blah"
def mymethod
MYCONSTANT
end
end
class MyClass
def mymethod
my_constant = "blah"
end
end
Constants in ruby cannot be defined inside methods. See the notes at the bottom of this page, for example
Because constants in Ruby aren't meant to be changed, Ruby discourages you from assigning to them in parts of code which might get executed more than once, such as inside methods.
Under normal circumstances, you should define the constant inside the class itself:
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT #=> "foo"
If for some reason though you really do need to define a constant inside a method (perhaps for some type of metaprogramming), you can use 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"
Again though, const_set
isn't something you should really have to resort to under normal circumstances. If you're not sure whether you really want to be assigning to constants this way, you may want to consider one of the following alternatives:
Class variables
Class variables behave like constants in many ways. They are properties on a class, and they are accessible in subclasses of the class they are defined on.
The difference is that class variables are meant to be modifiable, and can therefore be assigned to inside methods with no issue.
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"
Class attributes
Class attributes are a sort of "instance variable on a class". They behave a bit like class variables, except that their values are not shared with subclasses.
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"
Instance variables
And just for completeness I should probably mention: if you need to assign a value which can only be determined after your class has been instantiated, there's a good chance you might actually be looking for a plain old instance variable.
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
Your problem is that each time you run the method you are assigning a new value to the constant. This is not allowed, as it makes the constant non-constant; even though the contents of the string are the same (for the moment, anyhow), the actual string object itself is different each time the method is called. For example:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Perhaps if you explained your use case—why you want to change the value of a constant in a method—we could help you with a better implementation.
Perhaps you'd rather have an instance variable on the class?
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"
If you really want to change the value of a constant in a method, and your constant is a String or an Array, you can 'cheat' and use the #replace
method to cause the object to take on a new value without actually changing the object:
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"