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:
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 `’
supposed to piece them together. Please have a look at the code:
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.
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
herehttp://endofline.wordpress.com/2011/01/21/the-strange-ruby-splat/).
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 http://ruby-doc.org/core-2.0/Object.html#method-i-send.
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.