Well-Grounded Rubyist -- Person class implementation in chapter 4 question

Hi,

I am new to programming and new to Ruby.

I have been studying Ruby using David Black's book, and I need bit of
help with this:

Code in listing 4.11, 4.12 and 4.13 needs to be pieced together (I
think) to do something useful. However it's a bit confusing... whether
they are meant to just studied as examples separately, or whether I am
supposed to piece them together. Please have a look at the code:

If you could explain a bit what's going on in 4.13 that would be great.

On my computer I have combined the code in one file "person_class.rb"
and I am getting this error:

person_class.rb:17:in `has_friends': undefined method `<<' for
nil:NilClass (NoMethodError) from person_class.rb:43:in `<main>'

···

--
Posted via http://www.ruby-forum.com/.

Looks like your Person class initialises its @hobbies and @friends instance variables with things that don't come from anywhere

class Person
   PEOPLE =
     attr_reader :name, :hobbies, :friends

     def initialize(name)
         @name = name
         @hobbies = *hobbies* <-- where do these come from?
         @friends = *friends*
         PEOPLE << self
     end

     def has_hobbies(hobby)
         @hobbies << hobby
     end

     def has_friends(friend)
         @friends << friend
     end

I expect those are supposed to be arrays? In which case the << operator would be valid as a push in your has_hobbies/ has_friends methods.

Sam

···

On 09/07/13 18:14, Arslan Farooq wrote:

Hi,

I am new to programming and new to Ruby.

I have been studying Ruby using David Black's book, and I need bit of
help with this:

Code in listing 4.11, 4.12 and 4.13 needs to be pieced together (I
think) to do something useful. However it's a bit confusing... whether
they are meant to just studied as examples separately, or whether I am
supposed to piece them together. Please have a look at the code:

From "Well Grounded Rubyist" by David A. Black, Listing 4.11 Simple usage of the Person Class (page 107), Listing 4.12 Implementation of the main logic of the Person class (page 108), Listing 4.13 Full implementation of Person.method_missing (page 109), · GitHub

If you could explain a bit what's going on in 4.13 that would be great.

On my computer I have combined the code in one file "person_class.rb"
and I am getting this error:

person_class.rb:17:in `has_friends': undefined method `<<' for
nil:NilClass (NoMethodError) from person_class.rb:43:in `<main>'

Firstly, the error you are receiving has been explained by Sam. Now, to the
interesting part:
Code in 4.13 defines method_missing for Person class. This method gets run
if you call some undefined method in the class. So, for example, if you do
Person.blablabla("argument", 2), ruby looks in the Person's class methods
(not instance methods, because blablabla was sent to the class itself, not
to the instance of it) for a method with name "blablabla". It does not find
such method, do then it looks whether the Person class has method_missing
method defined. It does, so ruby calls Person.method_missing(:blablabla,
"argument", 2), passing the parameters we called "blablabla" with to the
method_missing. Ruby puts name method to the "m" variable and the rest of
the arguments - to the args array (because it's marked with asterisk - so
called splat method, you can read a little bit about it
here<http://endofline.wordpress.com/2011/01/21/the-strange-ruby-splat/&gt;\).
Then, ruby converts :blablabla symbol to string and checks whether it
starts with "all_with_". It doesn't, so ruby calls super, which means "call
parent class' method with the same name of the current one and pass it the
same arguments". Now, let's have a look at what happens if we call
Person.all_with_name("Julia"). Method Person.all_with_name is not defined,
so ruby goes to method_missing for help. This time method's name starts
with "all_with_", so ruby extracts the rest of the method's name after
"all_with_" to attr variable. Now, ruby checks whether Person class has
instance method with the name stored in newly created attr variable (which
now contains "name"). Then ruby finds all persons from PEOPLE array, who
have name equal to the args[0], which is first argument passed to the
Person.all_with_name. "person.send" is another way of calling an instance
method, e.g. person.send("haha") is the same as person.haha. Docs for
object.send is here <Class: Object (Ruby 2.0.0).
So, at last ruby finds all persons, whose name includes "Julia" as a
substring. And, as this PEOPLE.find_all is the last line in the method to
be executed, method_missing returns an array of found persons.
P.S. If we called Person.all_with_blablabla, we would get ArgumentError
exception, because Person does not have instance method blablabla defined,
which is checked in line 6 of 4.13 gist.
P.P.S. I am myself not really experienced rubyist, so my explanation may
contain some mistakes. Be sure to correct them if you notice.
P.P.P.S. Also, my English is far from perfect, sorry for that.

···

2013/7/9 Arslan Farooq <lists@ruby-forum.com>

Hi,

I am new to programming and new to Ruby.

I have been studying Ruby using David Black's book, and I need bit of
help with this:

Code in listing 4.11, 4.12 and 4.13 needs to be pieced together (I
think) to do something useful. However it's a bit confusing... whether
they are meant to just studied as examples separately, or whether I am
supposed to piece them together. Please have a look at the code:

From "Well Grounded Rubyist" by David A. Black, Listing 4.11 Simple usage of the Person Class (page 107), Listing 4.12 Implementation of the main logic of the Person class (page 108), Listing 4.13 Full implementation of Person.method_missing (page 109), · GitHub

If you could explain a bit what's going on in 4.13 that would be great.

On my computer I have combined the code in one file "person_class.rb"
and I am getting this error:

person_class.rb:17:in `has_friends': undefined method `<<' for
nil:NilClass (NoMethodError) from person_class.rb:43:in `<main>'

--
Posted via http://www.ruby-forum.com/\.

Thank you Sam!

@hobbies = *hobbies* <-- where do these come from?
@friends = *friends*

You were right.

Doing this solved the problem:

def initialize(name)
@name = name
@hobbies = <--
@friends = <--
PEOPLE << self
end

···

--
Posted via http://www.ruby-forum.com/\.