method_missing, respond_to? and AR

Hi,

I have a class Page with attribute 'title'. I want Project objects to
call Page methods when they don't find the called method. If the method
is not found whithin Page, super is called. For example, I can write:

p = Project.first
p.title #which calls page.title (it works)

Here is my code:

class Project < AR::Base
def method_missing(method_name, *args)
    if page.respond_to?(method_name)
       a = page.send(method_name, *args)
    elsif respond_to?(method_name)
       a = send(method_name, *args)
    else
      super
    end
    return a
  end
end

It works fine with p.title. But if I do this:

p.respond_to :title_before_type_cast

=> true

p.title_before_type_cast

I get a SystemStackError...

I don't understand why respond_to? returns true AND
p.send(:title_before_type_cast) calls method_missing at the same time?

Thanks!

Sorry, I made a mistake.

Actually, Project has the attribute 'mission'.

p.title_before_type_cast

=> "Page title"

p.mission

=> "Project mission"

p.mission_before_type_cast

=> SystemStackError...

So, p.respond_to?(:mission_before_type_cast) returns true AND
p.send(:mission_before_type_cast) that calls method_missing at the same
time?

I hope it is not so confusing.

Thanks

It works fine with p.title. But if I do this:

p.respond_to :title_before_type_cast

=> true

p.title_before_type_cast

I get a SystemStackError...

I don't understand why respond_to? returns true AND
p.send(:title_before_type_cast) calls method_missing at the same time?

Because some activerecord methods are generated on the fly by method missing. respond_to? returns true because if you were to call the method it wouldn't raise NoMethodError or anything like that

Fred

Hi --

It works fine with p.title. But if I do this:

p.respond_to :title_before_type_cast

=> true

p.title_before_type_cast

I get a SystemStackError...

I don't understand why respond_to? returns true AND
p.send(:title_before_type_cast) calls method_missing at the same time?

Because some activerecord methods are generated on the fly by method missing. respond_to? returns true because if you were to call the method it wouldn't raise NoMethodError or anything like that

respond_to? actually also generates the methods on the fly.

t = Team.find(:first)

=> #<Team id: 277401923, name: "Persuaders", mascot: "Cat",
created_at: "2008-07-16 00:23:09", updated_at: "2008-07-16 00:23:09">

t.methods.grep(/mascot/)

=> []

t.respond_to?(:mascot)

=> true

t.methods.grep(/mascot/)

=> ["mascot?", "mascot", "mascot="]

Like method_missing, it does this whether or not its argument is one
of the column attributes. Using a new session and a new t:

t.methods.grep(/mascot/)

=> []

t.respond_to?(:blah)

=> false

t.methods.grep(/mascot/)

=> ["mascot", "mascot=", "mascot?"]

In the default, out-of-the-box case, respond_to? doesn't necessarily
predict whether NoMethodError will be raised:

class C
   def method_missing(m)
     if /exists/.match(m.to_s)
       puts "That method will be treated as existent!"
     end
   end
end

c = C.new
c.this_sort_of_exists # "That method..."
print "Do I respond to :this_sort_of_exists?
puts c.respond_to?(:this_sort_of_exists) # false

David

Whoops, add <space>" at end of that line. (The code actually is
tested; I deleted the " in the email in the course of adding comments
:slight_smile:

David

Thanks guys for the replies. It helped me understand some behaviours I
found pretty strange.

I realized that my code had a contradiction that raised the
SystemStackError.

class Project < AR::Base
def method_missing(method_name, *args)
    if page.respond_to?(method_name)
       a = page.send(method_name, *args)
    elsif respond_to?(method_name)
       a = send(method_name, *args) #calls back method_missing
    else
      super
    end
    return a
  end
end

You can't call a missing method inside method_missing, and repond_to? on
that method doesn't have sens since it is supposed to me missing in the
first place.
I also realized that some attributes were found due to some other code I
had in a module.

New simplified method_missing:

class Project < AR::Base
def method_missing(method_name, *args)
    if page.respond_to?(method_name)
       a = page.send(method_name, *args)
    else
      super #looks for the method in AR::Base or raises NoMethodError
    end
    return a
  end
end

Wahid