Forum: Ruby Novice: Understanding instance 'variables' and methods

9eda22ab946ad605650d36f9852cba0e?d=identicon&s=25 Steve Tu (stevet)
on 2012-11-26 17:49
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?
6e32b16ec35070a346dd4e08799589e5?d=identicon&s=25 Kaspar Schiess (kaspar)
on 2012-11-26 18:21
(Received via mailing list)
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
E088bb5c80fd3c4fd02c2020cdacbaf0?d=identicon&s=25 "Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> (Guest)
on 2012-11-26 18:37
(Received via mailing list)
On Mon, Nov 26, 2012 at 5:49 PM, Steve Tucknott <lists@ruby-forum.com>
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.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2012-11-26 18:49
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.
9eda22ab946ad605650d36f9852cba0e?d=identicon&s=25 Steve Tu (stevet)
on 2012-11-27 09:26
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.
3853dd5371ac1e094fc45d6c2aa0e459?d=identicon&s=25 Carlo E. Prelz (Guest)
on 2012-11-27 09:35
(Received via mailing list)
Subject: Re: Novice: Understanding instance 'variables' and methods
  Date: Tue 27 Nov 12 05:26:46PM +0900

Quoting Steve Tu (lists@ruby-forum.com):

> 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
9eda22ab946ad605650d36f9852cba0e?d=identicon&s=25 Steve Tu (stevet)
on 2012-11-27 10:40
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.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2012-11-27 19:23
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 [](k)
   ...
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
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2012-11-27 20:20
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.
9a45896e48a382fe5c656b8873e0dfcb?d=identicon&s=25 Stu (Guest)
on 2012-11-27 22:07
(Received via mailing list)
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.
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.