When Do Ruby Instance Variables Get Set

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



Leave a reply



Submit