When do Ruby instance variables get set?
Instance variables in ruby may be a bit confusing when first learning Ruby, especially if you are accustomed to another OO language like Java.
You cannot simply declare an instance variable.
One of the most important things to know about instance variables in ruby, apart from the notation with an @ sign prefix, is that they spring into life the first time they are assigned to.
class Hello
def create_some_state
@hello = "hello"
end
end
h = Hello.new
p h.instance_variables
h.create_some_state
p h.instance_variables
# Output
[]
["@hello"]
You can use the method Object#instance_variables
to list all instance variables of an object.
You normally “declare” and initialize all the instance variables in the initialize method. Another way to clearly document which instance variables that should be publicly available is to use the Module methods attr_accessor
(read/write), attr_writer
(write) and attr_reader
(read). These methods will synthesize different accessor methods for the listed instance variable.
class Hello
attr_accessor :hello
end
h = Hello.new
p h.instance_variables
h.hello = "hello"
p h.instance_variables
# Output
[]
["@hello"]
The instance variable still isn’t created until it’s assigned to using the synthesized Hello#hello=
method.
Another important issue, like kch described, is that you need to be aware of the different contexts active when declaring a class. When declaring a class the default receiver (self) in the outermost scope will be the object that represents the class itself. Hence your code will first create a class instance variable when assigning to @hello
on the class level.
Inside methods self will be the object on which the method is invoked, hence you are trying to print the value of an instance variable with the name @hello
in the object, which doesn’t exists (note that it’s perfectly legal to read a non existing instance variable).
Why is `instance_variable_set` necessary in Ruby?
In the case of a "simple" variable assignment for an instance variable like:
@foo = "foo"
You couldn't do
"@#{foo}" = "bar" # syntax error, unexpected '=', expecting end-of-input
But you could do something similar with instance_variable_set:
instance_variable_set("@#{foo}", "bar")
p @foo # "bar"
As per your question Aren't these two lines the same?, for that example they're similar, but isn't the use people tend to give to instance_variable_set.
Get the value of an instance variable given its name
The most idiomatic way to achieve this is:
some_object.instance_variable_get("@#{name}")
There is no need to use +
or intern
; Ruby will handle this just fine. However, if you find yourself reaching into another object and pulling out its ivar, there's a reasonably good chance that you have broken encapsulation.
If you explicitly want to access an ivar, the right thing to do is to make it an accessor. Consider the following:
class Computer
def new(cpus)
@cpus = cpus
end
end
In this case, if you did Computer.new
, you would be forced to use instance_variable_get
to get at @cpus
. But if you're doing this, you probably mean for @cpus
to be public. What you should do is:
class Computer
attr_reader :cpus
end
Now you can do Computer.new(4).cpus
.
Note that you can reopen any existing class and make a private ivar into a reader. Since an accessor is just a method, you can do Computer.new(4).send(var_that_evaluates_to_cpus)
RUBY instance variable is reinitialized when calling another method
what would be the right way to do something like this ?
my_instance = 0
This creates a local variable instead of calling your setter. Give ruby a hint that you want to call the method:
self.my_instance = 0
Or you can set the instance variable directly:
@my_instance = 0
Using operators, how to set a variable equal to one of two other variables depending on which one has a value, in Ruby?
You can write
Module.const_defined?(:A) ? A : (B == true ? B : C)
to obtain the value of A
.
A not defined
B = true
Module.const_defined?(:A) ? A : (B == true ? B : C)
#=> true
A not defined
B = false
Module.const_defined?(:A) ? A : (B == true ? B : C)
#=> 42
A = "cat"
B = false
C = 42
Module.const_defined?(:A) ? A : (B == true ? B : C)
#=> "cat"
See Module#const_defined?.
Related Topics
Difference Between Ruby 1.8 and Ruby 1.9
Using 'Return' in a Ruby Block
Ruby Regular Expression Using Variable Name
Pressing Ctrl + a in Selenium Webdriver
Is There a Reason That We Cannot Iterate on "Reverse Range" in Ruby
Ruby - Print the Variable Name and Then Its Value
How to Use Ruby/Rails Variables Inside Sass
How to Use Ruby to Colorize the Text Output to a Terminal
Rails Sessions Current Practices
Difference or Value of These Block Coding Styles in Ruby
What Does the Unary Question Mark () Operator Do
How to Skip the First Line of a CSV File and Make the Second Line the Header
Code Block Passed to Each Works With Brackets But Not With 'Do'-'End' (Ruby)