Forum: Ruby Help with class variable example from "Well Grounded Rubyist"

0946b736110f6af95c93bce0ea6d46ff?d=identicon&s=25 Gerald Vim (gvim)
on 2014-03-15 02:20
(Received via mailing list)
Can someone help me grok this example from "Well Grounded Rubyist"?

class Person
   attr_accessor :age
   def initialize(options)
     self.age = options[:age]
   end

   def teenager?
     (13..19) === age
   end
end

As I understand it instantiating an object with, for example:

Person.new(age: 17)

... sets the class variable age to 17. I would have expected age to be
an instance variable, set the usual way with:

   def initialize(age)
     @age = age
   end

.... but, for some reason, a class variable is used here though it is
settable.

Next the options hash. This is where I get completely confused. The hash
is passed to the initialize method though it doesn't yet exist and
object instantiation doesn't refer to it either. Then, the class
variable age is somehow passed the new options hash using the age value
as the key but ....... now it's the symbol :age. WTF???

gvim
09a32175057418748822c587ac08c429?d=identicon&s=25 Abinoam Jr. (abinoampraxedes_m)
on 2014-03-15 04:13
(Received via mailing list)
On Fri, Mar 14, 2014 at 10:18 PM, gvim <gvimrc@gmail.com> wrote:
>   end
>     @age = age
>   end
>
> .... but, for some reason, a class variable is used here though it is
> settable.

It's not a class variable.
It's a regular method calling.

attr_accessor :age
has the same effect as

def age
  @age
end

def age=(age)
  @age=age
end

so...

self.age = options[:age]

calls the method (setter) age=

>
> Next the options hash. This is where I get completely confused. The hash is
> passed to the initialize method though it doesn't yet exist and object
> instantiation doesn't refer to it either.
> Then, the class variable age is
> somehow passed the new options hash using the age value as the key but
> ....... now it's the symbol :age. WTF???

The three bellow are the same.

Person.new({:age => 17})
Person.new({age: 17})
Person.new(age: 17)

When passing a Hash you may ommit the curly brackets.
When the key is a symbol, you can use the new (1.9) Hash literal syntax
0946b736110f6af95c93bce0ea6d46ff?d=identicon&s=25 Gerald Vim (gvim)
on 2014-03-15 17:49
(Received via mailing list)
On 15/03/2014 03:12, Abinoam Jr. wrote:
>
> def age=(age)
>    @age=age
> end
>
> so...
>
> self.age = options[:age]
>
> calls the method (setter) age=
>

I still don't understand where the options hash came from and how :age
is being used as a hash key if you say it's a method call. Is options
some kind of system hash?

gvim
3df767279ce7d81db0a5bb30f5136863?d=identicon&s=25 Matthew Kerwin (mattyk)
on 2014-03-15 23:00
(Received via mailing list)
gvim wrote:
>
> I still don't understand where the options hash came from and how :age is
being used as a hash key if you say it's a method call. Is options some
kind of system hash?
>

Read the rest of Abinoam's earlier response (sorry if I got your name
wrong). He showed that there are multiple ways of passing a hash as a
parameter to a function call.

Also note the line:
  def initialize(options)

It's explicitly named. No magic required.
0946b736110f6af95c93bce0ea6d46ff?d=identicon&s=25 Gerald Vim (gvim)
on 2014-03-17 15:59
(Received via mailing list)
On 15/03/2014 22:00, Matthew Kerwin wrote:
>    def initialize(options)
>
> It's explicitly named. No magic required.
>

It may be explicitly named and obvious to you but, as I said earlier, I
haven't a clue what the options hash refers to.

gvim
E088bb5c80fd3c4fd02c2020cdacbaf0?d=identicon&s=25 Jesús Gabriel y Galán (Guest)
on 2014-03-17 16:30
(Received via mailing list)
On Mon, Mar 17, 2014 at 3:58 PM, gvim <gvimrc@gmail.com> wrote:
>> Read the rest of Abinoam's earlier response (sorry if I got your name
> haven't a clue what the options hash refers to.
>
> gvim

Let's try the explanation from the other direction:

class Person
  attr_accessor :age
  def initialize(options)
    self.age = options[:age]
  end

  def teenager?
    (13..19) === age
  end
end

Person.new(age: 17)


When you call a method with this syntax: object.method(key: value)
what it means is:
Create a hash with a key with the symbol :key and the value -> value.
So this is equivalent to

a = { :key => value}

It's a little syntactic sugar to make it look like you are naming
method parameters, but in the end it's just a simple hash object, with
keys and values, where the keys happen to be symbols.

So, this hash is pass to the method, in this case "new", which in turn
will pass it internally to "initialize". Let's look at that:

  def initialize(options)
    self.age = options[:age]
  end

You have declared that your initialize method receives one parameter
called options. options is the name of the local variable, local to
the method, that is bound to the object that you pass to the "new"
method. In this example it happens to be a hash, remember, so we can
call the method [] on it, with a symbol and get an integer back, cause
that's what you put in, when calling "new". You created a hash with
key :age and value 17, so options[:age] returns 17.

Summarizing this part, what you see is equivalent to:

options = { :age => 17}
options[:age] #=> returns 17

Now, the part related to the age instance variable. As Abinoam
explained, attr_accessor creates two methods for you:

def age=(age)
  @age = age
end

and

def age
  @age
end

So, internally you do have an instance variable called @age. From the
outside, you could set it like

person = Person.new(17)
person.age=18 # birthday --> this calls the method age=
puts person.age   # this calls the method age

From the inside, you can call the methods on self  (the current
instance):

class Person
  def test
    puts self.age
  end
end

Person.new(25).test   # should print 25

Now, the age method can be called without self, like this:

class Person
  def test
    puts age
  end
end

There's no ambiguity here that you mean the method age. So no problem.
But the setter is different. What should Ruby do with this?

class Person
  def test_new_age new_age
    age = new_age
  end
end

As you can see, this looks exactly the same as a regular local
variable assigment. To avoid this ambiguity Ruby tells you to call
methods with the self receiver, in order to differentiate from local
variable assignments. So you need to do:

class Person
  def test_new_age new_age
    self.age = new_age
  end
end

And everything is fine again:

person = Person.new(25)
person.test_new_age(26)
person.age #=> 26

So, coming back to your original example:
  def initialize(options)
    self.age = options[:age]
  end

It's taking the value assigned to key :age from the hash named
options, and calling the method age= of the object self, passing the
value it got from the hash. The method age= is the one generated by
attr_accessor, which just stores the value in the @age instance
variable.

Hope this helps,

Jesus.
0946b736110f6af95c93bce0ea6d46ff?d=identicon&s=25 Gerald Vim (gvim)
on 2014-03-17 16:38
(Received via mailing list)
On 17/03/2014 15:29, Jes?s Gabriel y Gal?n wrote:
>      (13..19) === age
>
>      self.age = options[:age]
> Summarizing this part, what you see is equivalent to:
>
> person.age=18 # birthday --> this calls the method age=
> Person.new(25).test   # should print 25
> But the setter is different. What should Ruby do with this?
> variable assignments. So you need to do:
> person.test_new_age(26)
> attr_accessor, which just stores the value in the @age instance
> variable.
>
> Hope this helps,
>
> Jesus.
>

Yes, very clear. Thanks very much Jesus.

gvim
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.