Wednesday July 30, 2008 |
Isopaleocopria
Gregory Murphy's Blogorrhea |
|
|
Ruby Eye for the Java guy: Constants In Java, it is common to use public, final, static fields to represent utility constants. Since the fields are public, they can be used from any package; and since they are final, values cannot be assigned to them once they have been initialized. If the field is a primitive type, such as It would seem like the need for an API to expose constants is universal, and so should find expression in every language. How to do this in Ruby? In Ruby, any field that begins with an upper-case letter is treated as constant. If you attempt to assign to a constant more than once, the interpreter will issue a warning. Constants may be defined inside of a class, and since the Ruby Class class inherits from Module, you can use the module "::" accessor syntax to read the constant's value:
class MyClass
MY_VAR = "xxx"
end
puts MyClass::MY_VAR #=> "xxx"
MyClass::MY_VAR = "yyy" #=> "warning: already initialized constant MY_VAR"
However, the Ruby String class is mutable, so the constant is still open to modification:
MyClass::MY_VAR[1] = "z" puts MyClass::MY_VAR #=> "xzx"Everything in Ruby is an object, and all objects inherit from the Object class. Object defines a freeze method, which, once invoked, prevents any further modification to the object's state. Once frozen, an object can never be "thawed". So a final, immutable class constant in Ruby might look like this:
class MyClass
MY_VAR = "xxx"
MY_VAR.freeze
end
puts MyClass::MY_VAR #=> "xxx"
MyClass::MY_VAR = "yyy" #=> "warning: already initialized constant MY_VAR"
MyClass::MY_VAR[1] = "z" #=> "in '[]=': can't modify frozen string (TypeError)
The attempt to modify the frozen object results in an unchecked error, and execution halts. Re-assigning to the constant results in a warning only, and execution continues. It is possible to make assignment a fatal error, too, if we wrap the constants in a module, and freeze the module:
module MyModule
MY_VAR = "xxx"
MY_VAR.freeze
end
MyConstants.freeze
MyModule::MY_VAR = "YYY" #=> "can't modify frozen module (TypeError)
Be careful, if you want to "mix in" the module's functionality, callers of your class will access the module's constants through an unfrozen path:
class MyClass
include MyConstants
end
MyClass::VAR = "qqq"
MyClass::VAR[1] = 'r'
puts MyClass::VAR #=> "qrq"
One solution might be to freeze your class, if you want to guarantee the immutability of everything in the class. If you want to leave the class open to extension or modification, you could wrap the included constants in an inner module:
module MyConstants
VAR = "xxx"
VAR.freeze
end
MyConstants.freeze
class MyClass
class Constants
include MyConstants
end
Constants.freeze
end
puts MyClass::Constants::VAR #=> "xxx"
(2008-07-30 10:13:21.0)
Permalink
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||