aXon
aXon

Reputation: 125

Ruby and public_method_defined? : strange behaviour

Whilst reading through the book "The well grounded Rubyist", I came across some strange behaviour. The idea behind the code is using one's own method_missing method. The only thing I am not able to grasp is, why this code gets executed, as I do not have any Person.all_with_* class methods defined, which in turn means that the self.public_method_defined?(attr) returns true (attr is friends and then hobbies).

#!/usr/bin/env ruby1.9

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

        def initialize(mame)
                @name = name
                @hobbies = []
                @friends = []
                PEOPLE << self
        end

        def has_hobby(hobby)
                @hobbies << hobby
        end

        def has_friend(friend)
                @friends << friend
        end

        def self.method_missing(m,*args)
                method = m.to_s
                if method.start_with?("all_with_")
                        attr = method[9..-1]
                        if self.public_method_defined?(attr)
                                PEOPLE.find_all do |person|
                                        person.send(attr).include?(args[0])
                                end
                        else
                                raise ArgumentError, "Can't find #{attr}"
                        end
                else
                        super
                end
        end
end

j = Person.new("John") 
p = Person.new("Paul") 
g = Person.new("George") 
r = Person.new("Ringo")

j.has_friend(p) 
j.has_friend(g) 
g.has_friend(p) 
r.has_hobby("rings")

Person.all_with_friends(p).each do |person| 
        puts "#{person.name} is friends with #{p.name}"
end 

Person.all_with_hobbies("rings").each do |person|
        puts "#{person.name} is into rings"
end

The output is

is friends with 
 is friends with 
 is into rings

which is really understandable, as there is nothing to be executed.


hmm, is is because I have accessors for friends and hobbies? But I thought they would only be accessed in instances, and not on the class-object level.

Upvotes: 1

Views: 290

Answers (1)

mikej
mikej

Reputation: 66263

Assuming that the code pasted into your question is the exact code that you're running then the problem is a typing mistake in initialize:

def initialize(mame) should be def initialize(name)

If the name of the parameter name is mistyped then this causes the line @name = name, rather than meaning

"set @name to the value of the parameter"

to mean

"set @name to the value returned by the method name (your attr reader)" (which of course is nil because it hasn't been set)

Hence all your Beatles get left without their names being set and the output is the 2 friends for Paul and Ringo's hobby but with all the names blank.

Upvotes: 4

Related Questions