Novice: Understanding instance 'variables' and methods

A question - or comment - on instance variables.

Looking at ‘:symbols’ today, I came across this example:

class Test
puts :Test.object_id.to_s
def test
puts :test.object_id.to_s
@test = 10
puts :test.object_id.to_s
end
end
t = Test.new
t.test

So far, my understanding was that both methods and variables used lower
case names, and that methods could be written both with and without
‘()’. To me that then made the t.test line a bit confusing, as it
appears to invoke the method test on t. How then could you ever access
the instance variable test of t?

Am I then also right in assuming that in Ruby there aren’t any true
visible instance variables, as to make the variable visible, you have to
include an accessor - which effectively creates the getter/setter of
that variable?

So if I changed the above code to have an instance variable of ‘value’,
with an accessor, I could then have:

@test = @value = 10

and then access t.value - giving me 10. BUT t.value is then really a
call to the ‘generated’ getter of value - so is really a method call?

Do I have that about right?

And in the above case, where a ‘variable’ and method have the same name,
is there a way to force the use of a specific ‘setter/getter’ - ie to
have t.test return the value of test, rather than call the method test
on t?

Hei,

A question - or comment - on instance variables.
I can’t say I understood all of your post - but here’s what might clear
things up for you: In Ruby, there’s no public access for instance
variables. Assume in #initialize you do

@foo = 1

To get at the value of @foo, you absolutely must define a getter, either
by

attr_reader :foo

or so

def foo
return @foo
end

The only scope @foo is equal to 1 is the instance scope of your class.
No external access without some sort of getter. And of course,
#instance_variable_get and other meta-methods.

k

On Mon, Nov 26, 2012 at 5:49 PM, Steve T. [email protected]
wrote:

end
Am I then also right in assuming that in Ruby there aren’t any true
and then access t.value - giving me 10. BUT t.value is then really a
call to the ‘generated’ getter of value - so is really a method call?

Do I have that about right?

And in the above case, where a ‘variable’ and method have the same name,
is there a way to force the use of a specific ‘setter/getter’ - ie to
have t.test return the value of test, rather than call the method test
on t?

As has been answered, you cannot get at the instance variables
directly from the outside. It’s all calling methods.
But I think you have also another confusion, regarding local
variables and methods:

class A
def test
“test: the method”
end
def some_method
test = “a variable”
puts test #=> a variable
puts self.test #=> test: the method
puts test() #=> test: the method
end
end

If you have a local variable and a method with the same name, the
local variable “wins”, and you have to use “self.” or parenthesis to
tell Ruby you want to call the method.

Jesus.

I am nearly there!

If I have

class My_class
attr_accessor :test
end

my_c = My_class.new
my_c.test = 10
my_c.test

Then I have automatically created two methods in My_class - the
reader/writer - both with a method name of ‘test’.

my_c is then an instance of that class, and to access the variables I
simply use my_c.test - BUT although my_c.test looks like it is a simple
variable, it is really a call to a method of the name ‘test’.

This is where I had the problem with the symbols test above - as I
wanted to get to the getter of test, not the setter. But it seemed that
every time that I used t.test it invoked the setter.

I’ll look at the example again and try to see why I was getting that
effect as I would guess that if I call the test method with an arg
passed via the =, then it would invoke the setter, otherwise with no
args it would invoke the getter (why doesn’t Ruby use ? suffix on the
getter - ie my_c.test? or t.test?).

Thanks for all of your help.

Steve Tu wrote in post #1086523:

So far, my understanding was that both methods and variables used lower
case names,

That’s true.

and that methods could be written both with and without
‘()’.

That’s correct.

To me that then made the t.test line a bit confusing, as it
appears to invoke the method test on t.

It does.

How then could you ever access
the instance variable test of t?

puts t.instance_variable_get(:@test)

Or, you can reopen the class and redefine test():

class Test
def test
@test
end
end

result = t.test
puts result

–output:–
10

However, the example you posted is terrible, and no one would ever write
code like that.

Am I then also right in assuming that in Ruby there aren’t any true
visible instance variables,

Instance variables are “private” in ruby. That means you cannot
directly access an instance variable–instead you have to use a method
to accesses an instance variable.

as to make the variable visible, you have to
include an accessor - which effectively creates the getter/setter of
that variable?

You can either write an accessor by hand:

def test
@test
end

Or, you can tell ruby to create an accessor for you:

class Dog
attr_reader :test

def initialize(t)
@test = t
end
end

d = Dog.new(10)
puts d.test

–output:–
10

So if I changed the above code to have an instance variable of ‘value’,
with an accessor, I could then have:

@test = @value = 10

No. You could write something like:

class Dog
attr_reader :test
attr_accessor :value

def initialize(t)
@test = t
end
end

d = Dog.new(10)
d.value = ‘hello’ #setter
puts d.value #getter

BUT t.value is then really a
call to the ‘generated’ getter of value - so is really a method call?

Yes. In fact, when you write:

x = 1 + 2

“+” is actually the name of an Integer method. And because “+” is the
name of a method, you can write that line like this:

x = 1.+(2)
puts x

–output:–
3

And in the above case, where a ‘variable’ and method have the same name,
is there a way to force the use of a specific ‘setter/getter’ - ie to
have t.test return the value of test, rather than call the method test
on t?

You would never write a class that has two methods with the
same name–but the second method would overwrite the first method.

class Dog
def greet
puts ‘hello’
end

def greet
puts ‘goodbye’
end
end

d = Dog.new
d.greet

–output:–
goodbye

It’s like if you did this:

x = 1
x = 3

and asked if there was a way to retrieve the first value of x.

Subject: Re: Novice: Understanding instance ‘variables’ and methods
Date: Tue 27 Nov 12 05:26:46PM +0900

Quoting S. Tu ([email protected]):

Then I have automatically created two methods in My_class - the
reader/writer - both with a method name of ‘test’.

No. There cannot be two methods with the same name in Ruby.

One method (the getter) is ‘test’. The other (the setter) is called
‘test=’.

You can always have a look at what methods an objects answers to, with
the ‘methods’ method. For your class,

p my_c.methods

[:test, :test=, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class,
:singleton_class, :clone, :dup, :initialize_dup, :initialize_clone,
:taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze,
:frozen?, :to_s, :inspect, :methods, :singleton_methods,
:protected_methods, :private_methods, :public_methods,
:instance_variables, :instance_variable_get, :instance_variable_set,
:instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap,
:send, :public_send, :respond_to?, :respond_to_missing?, :extend,
:display, :method, :public_method, :define_singleton_method, :object_id,
:to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval,
:instance_exec, :send, :id]

The first two in the list are the ones you added with attr_accessor.

Carlo

carlo,
Got it.

So in the :symbols Test code, the problem was where I added the
accessor.
The original code just had the method ‘test’ which in effect was a
setter (presumably in Ruby parlance it could have been better called
test= ?).
I then added the ‘attr_accessor :test’ which presumably added a test=
method, leaving the original ‘test’ method. So what I in effect ended up
with were two setter methods ‘test’ (the original, which set @test to
10) and a new setter accessor (test=) that would set @test to whatever
value I passed. I didn’t actualyy have a getter method called ‘test’.

Now that starts to make sense, as every time I used t.test, I ended up
with the ‘puts’ lines.

Didn’t take me long eh? Sorry for being a bit thick.

Steve Tu wrote in post #1086651:

The original code just had the method ‘test’ which in effect was a
setter (presumably in Ruby parlance it could have been better called
test= ?).

Kind-of. There is special syntactic sugar for methods with names like
test=.

foo.test = 123

is mapped to a method call of the “test=” method, even though this looks
like an assignment (and there may be spaes around the equals sign). It’s
the same as:

foo.send(:test=, 123)

Furthermore, the return value of the method is forced to be 123, even if
it explicitly returns something else.

There are other examples:

def

end

foo[123]

def[]=(k,v)

end

foo[123] = 456

And even operators like + and - map to methods

class Test

binary +

def +(other)

end

unary +

def +@

end
end

attr methods actually do the creation, probably with convert to string
append the ‘@’ sigil in front and run it through eval, of what symbol
was
given to them.

If you need a quick conceptual open up irb and type:

puts ‘@’ + :verb.to_s

look into define_method, as well as other eval family methods.
Getter/Setters are simple syntax sugar as ruby allows the method name to
have ‘=’ so we don’t have to type get_verb or set_verb( val). attr is a
unique piece of automation for the common templating, boiler plate code,
you see in object oriented languages.

attr_accesor is a method which creates methods. It wouldn’t be as
obvious
if we where able to actually send the sigilled version of what’s being
created instead of a symbol. attr_writer( @foo, @bar) would be a bit
more
obvious. But that won’t work as @foo is just a variable and one that is
not
set. It would actually send the value nil and not the name which is why
using a symbol or string does.

First, I suggest that you learn more than one variable name. Naming
everything in your code test/Test/:test is just going to confuse you.

I then added the ‘attr_accessor :test’ which presumably added a test=
method, leaving the original ‘test’ method.

Writing attr_accessor :name inside a class is equivalent to writing:

def name #getter
@name
end

def name=(val) #setter
@name = val
end

And you call the ‘name=’ method like this:

d.name = 10

ruby ignores the space between ‘d.name’ and the ‘=’ sign.

Once again, if you redefine a method it will overwrite an existing
method:

class Dog
attr_accessor :name
end

d = Dog.new
d.name = ‘Sam’
puts d.name

–output:–
Sam

But see what happens here:

class Dog
attr_accessor :name

def name
‘hello’
end
end

d = Dog.new
d.name = ‘Sam’
puts d.name

–output:–
hello

The line attr_accessor :name tells ruby to create the methods:

def name #getter
@name
end

def name=(val) #setter
@name = val
end

…which ruby does. Next ruby sees the definition:

def name
‘hello’
end

in the Dog class, and that name method overwrites the name method that
ruby created. If you reverse the order of the statements:

class Dog
def name
‘hello’
end

attr_accessor :name

end

d = Dog.new
d.name = ‘Sam’
puts d.name

–output:–
Sam

…then ruby’s definition of the name method overwrites your definition
of the name method.