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