Ruby / Variables and constants
From WhyNotWiki
[edit] Can you reference variables that haven't been initialized?
Yes, if they are global variables or instance variables.
No, if they are local variables or class variables.
def demonstrate
puts $a
puts @a
puts a # error!
puts @@a # error!
end
demonstrate
nil
nil
using_undefined_variables.rb:6:in `demonstrate': undefined local variable or method `a' for main:Object (NameError)
from using_undefined_variables.rb:9
irb -> a
NameError: undefined local variable or method `a' for main:Object
from (irb):1
irb -> @@a
NameError: uninitialized class variable @@a in Object
from (irb):7
irb -> @a
=> nil
irb -> $a
=> nil
[edit] How do you check if a variable is defined?
Not like this:
irb -> defined?(:a)
=> "expression"
because that checks if the symbol
defined? is a very special method that doesn't act like other methods. The difference is that you can pass undefined local and class variables to defined? and you won't get an error!
defined? returns nil if the variable (or whatever?) is not defined and returns what kind of "thing" it is if it is defined...
irb -> defined?(a)
=> nil
irb -> defined?(@a)
=> nil
irb -> defined?(@@a)
=> nil
irb -> defined?($a)
=> nil
irb -> a = 'a local variable'
=> "a local variable"
irb -> @a = 'an instance variable'
=> "an instance variable"
irb -> @@a = 'a class variable'
=> "a class variable"
irb -> $a = 'a global variable'
=> "a global variable"
irb -> defined?(a)
=> "local-variable"
irb -> defined?(@a)
=> "instance-variable"
irb -> defined?(@@a)
=> "class variable"
irb -> defined?($a)
=> "global-variable"
[edit] How do you check if a constant is defined?
At first I thought you might use const_defined? ... but then I saw that you have to ask that of a specific module. What if you just define a constant at the global scope (not in a module)? Then which module would you to check if that constant is defined? I couldn't get that to work at all.
Instead, use the language-built-in (I couldn't find it in the RDoc for Kernel/Module/etc.) check defined? . It will be nil if it's not defined, non-nil if it is defined!
Foo = true puts Module.const_defined?(:Foo) # => false puts defined? Foo # => constant
[edit] What's the difference between a constant and a variable?
Besides that constants begin with a capital letter and variables do not...?
[edit] Does it let you change constants? (Yes)
Yes, but it will raise a warning:
irb -> PI
NameError: uninitialized constant PI
from (irb):1
irb -> PI=3
=> 3
irb -> PI=4
(irb):3: warning: already initialized constant PI
=> 4
irb -> PI
=> 4
However, you may not change a constant from a method apparently:
irb -> def foo; PI=4; end
SyntaxError: compile error
(irb):1: dynamic constant assignment
def foo; PI=4; end
^
from (irb):1
However, this is Ruby, and like most restrictions in Ruby, you can get around this one:
irb -> def foo; self::class::const_set(:PI, 4); end
=> nil
irb -> foo
=> 4
irb -> PI
=> 4
[edit] Different scope: To whom do the constants actually belong?
They always belong to a class/module, it appears; you can't just have local constants like in the same way that you can have local variables.
irb -> MODEL_DIR="/path/to/models"
=> "/path/to/models"
irb -> Kernel::MODEL_DIR = "something else entirely"
=> "something else entirely"
irb -> MODEL_DIR
=> "/path/to/models"
irb -> Object::MODEL_DIR
=> "/path/to/models"
irb -> class Foo
irb(main):010:1> MODEL_DIR="Foo's MODEL_DIR"
irb(main):011:1> end
=> "Foo's MODEL_DIR"
irb -> MODEL_DIR
=> "/path/to/models"
irb -> Object::const_get(:MODEL_DIR)
=> "/path/to/models"
irb -> Foo::MODEL_DIR
=> "Foo's MODEL_DIR"
I would have to say that it looks like unless Ruby has some reason to think you want it elsewhere (f.e., you give an explicit scope, Foo::, or you're inside of a class), it will think you want the constant to be defined for the class object "Object".
[edit] Checking if a constant is defined
This could be useful, for example, if you want to conditionally mix in a module or something only if a class is available.
include CoolFeature if CoolFeatureModule.const_defined?(:CoolClass)
(not a good example, I know)
[edit] Caveat: Did you mean to have 2 variables reference the same object or two copies of the object?
I easily forget that variables and objects are different. A variable just points to an object. So you can have two variables referring to the same object ... even without meaning to! Really! Believe me!
Demonstration:
irb -> original = new = "hi there"; new.capitalize!; original + " => " + new
=> "Hi there => Hi there"
It may not be obvious unless you know Ruby, but original = new = "hi there" causes both original and new (two variables) to both point to the same object in memory. So changing the one variable will affect the other. See, they have the same object_id:
irb -> original == new
=> true
irb -> original.object_id == new.object_id
=> true
irb -> "#{original.object_id} == #{new.object_id}"
=> "-604147648 == -604147648"
The proper way to create independently modifiable copies of an object is with the dup (duplication) method, like so:
irb -> original = "hi there"; new = original.dup; new.capitalize!; original + " => " + new
=> "hi there => Hi there"
It's a little more verbose, but I don't know of a conciser way of doing it!
Observe how the objects have different object IDs:
irb -> original == new
=> false
irb -> original.object_id == new.object_id
=> false
irb -> "#{original.object_id} != #{new.object_id}"
=> "-604271038 != -604271048"
That's because they really are two separate objects!
[edit] Variable scope/accessibility
| local | instance | class | global |
|---|---|---|---|
| v | @v | @@v | $v |
| instance | instance | accessor/getter | accessor/setter | class | class | accessor/getter | accessor/setter | |
|---|---|---|---|---|---|---|---|---|
| normally (from within instance/class): | @v | @v= | object.v | object.v= | @@v | Klass.v | Klass.v= | |
| cheating (when scope rules would prevent normal access): | object.instance_variable_get(:@v) | object.instance_variable_set(:@v, new_value) | object.send(:v) | object.send(:v) | klass.class_variable_get(:@@v) | klass.class_variable_set(:@@v, new_value) ? | klass.send(:v) ?? | klass.send(:v) |
cattr_accessor|attr_accessor
mattr_accessor (for modules)
[edit] Instance (member) variables
From within a method, it's easy enough to access an instance variable. You just refer to it as @foo or whatever it's called.
Outside of the class, it's a little bit harder:
# Doesn't work:
irb -> a.@foo
SyntaxError: compile error
(irb):9: syntax error, unexpected tIVAR
from (irb):9
# But you can access it this way:
irb -> a.instance_variable_get(:@foo)
=> []
Reminder: You should never access an instance variable like that (I only use it for debugging). Instead, create accessor methods for it, using attr_writer, attr_reader, or attr_accesor.
[edit] Class variables
People.send(:class_variable_set, :@@lifespan, 0.minute)
[edit] mod.class_variables will only report which class variable have been set (initialized)
irb -> class A; def self.hi; @@a = 1 end; end
=> nil
# You might expect it to list @@a, but it doesn't really treat it as a class_variable until the moment it is initialized
irb -> A.class_variables
=> []
irb -> A.hi
=> 1
irb -> A.class_variables
=> ["@@a"]
For reasons such as that, it's usually a good idea to initialize class variable in the class scope, even if you will later change them from within a method...
irb -> class B; @@b = nil; def self.hi; @@b = 1 end; end
=> nil
irb -> B.class_variables
=> ["@@b"]
Variables edit (Category edit) Category:Variables
Constants edit (Category edit) Category:Constants
