method(std).call and send(sth)

Bartek Puzoń wrote:

I've got two Models. A and B. Model A has an attribute "name". Model B has an attribute "title".

I can get the name by a.method(:name).call I can NOT get the title by b.method(:title).call. Send works in both cases (a.send(:name), b.send(:title).

I have rewritten my methods using send. But WHY did the former not work?

In model A you have something like:

def name    ... end

and in model B you don't have the equivalent for title.

Rails uses method_missing to dynamically create column methods for you without you needing to explicitly create them in your model unless you want to override the default behavior or add new ones.

send will follow the path through method_missing, if it's been defined, in search of the method to execute while method.call does not. I'm not a Ruby expert so I don't know why there's that difference.

This is analogous to how respond_to? will return true for methods that are defined through method_missing but you don't see them if you look at the methods array for that class.

Hi --

Bartek Puzoń wrote:

I've got two Models. A and B. Model A has an attribute "name". Model B has an attribute "title".

I can get the name by a.method(:name).call I can NOT get the title by b.method(:title).call. Send works in both cases (a.send(:name), b.send(:title).

I have rewritten my methods using send. But WHY did the former not work?

In model A you have something like:

def name   ... end

and in model B you don't have the equivalent for title.

Rails uses method_missing to dynamically create column methods for you without you needing to explicitly create them in your model unless you want to override the default behavior or add new ones.

send will follow the path through method_missing, if it's been defined, in search of the method to execute while method.call does not. I'm not a Ruby expert so I don't know why there's that difference.

This is analogous to how respond_to? will return true for methods that are defined through method_missing but you don't see them if you look at the methods array for that class.

Except... respond_to? doesn't do that :slight_smile: It really can't, since method_missing can do anything. You can sometimes (and people do) override respond_to? so that it reflects what your method_missing does, but it's difficult if things are very dynamic.

There was a thread about this on ruby-talk recently, with some interesting discussion of having a kind of third, high-level spec that could be used to define both method_missing and respond_to? to avoid having to do it all twice. I myself am not bothered by it; I think the implication of the "missing" in "method_missing" is that the object does *not* respond to the message, but a case can certainly be made that "respond" should include cases where the response is manufactured on the fly.

David

dblack@wobblini.net wrote:

Hi --

Bartek Puzoń wrote:

I've got two Models. A and B. Model A has an attribute "name". Model B has an attribute "title".

I can get the name by a.method(:name).call I can NOT get the title by b.method(:title).call. Send works in both cases (a.send(:name), b.send(:title).

I have rewritten my methods using send. But WHY did the former not work?

In model A you have something like:

def name   ... end

and in model B you don't have the equivalent for title.

Rails uses method_missing to dynamically create column methods for you without you needing to explicitly create them in your model unless you want to override the default behavior or add new ones.

send will follow the path through method_missing, if it's been defined, in search of the method to execute while method.call does not. I'm not a Ruby expert so I don't know why there's that difference.

This is analogous to how respond_to? will return true for methods that are defined through method_missing but you don't see them if you look at the methods array for that class.

Except... respond_to? doesn't do that :slight_smile: It really can't, since method_missing can do anything. You can sometimes (and people do) override respond_to? so that it reflects what your method_missing does, but it's difficult if things are very dynamic.

So is Rails doing something funky with respond_to? then to get that to work?

E.g. if I have:

class Author < ActiveRecord::Base

   def name    end

end

class Post < ActiveRecord::Base    # title is column in Post end

And then in script/console I get this:

>> a = Author.new => #<Author:0x349bee0 @new_record=true, @attributes={"name"=>nil}> >> a.method("name").call => nil >> a.methods.grep(/name/) => ["name", "table_name_prefix", "attribute_names", "table_name_suffix", "pluralize_table_names"]

>> b = Post.new => #<Post:0x34891f0 @new_record=true, @attributes={"title"=>nil}> >> b.method("title").call NameError: undefined method `title' for class `Post'          from (irb):5:in `method'          from (irb):5 >> b.methods.grep(/title/) => >> b.respond_to?("title") => true >> b.send("title") => nil

Hi --

Bartek Puzoń wrote:

I've got two Models. A and B. Model A has an attribute "name". Model B has an attribute "title".

I can get the name by a.method(:name).call I can NOT get the title by b.method(:title).call. Send works in both cases (a.send(:name), b.send(:title).

I have rewritten my methods using send. But WHY did the former not work?

In model A you have something like:

def name   ... end

and in model B you don't have the equivalent for title.

Rails uses method_missing to dynamically create column methods for you without you needing to explicitly create them in your model unless you want to override the default behavior or add new ones.

send will follow the path through method_missing, if it's been defined, in search of the method to execute while method.call does not. I'm not a Ruby expert so I don't know why there's that difference.

Just to comment on this point: the reason is that if you do this:

   obj.method("x").call

by the time you get to the .call part, you've already failed if there's no "x" method. So call will never be called.

This is analogous to how respond_to? will return true for methods that are defined through method_missing but you don't see them if you look at the methods array for that class.

Except... respond_to? doesn't do that :slight_smile: It really can't, since method_missing can do anything. You can sometimes (and people do) override respond_to? so that it reflects what your method_missing does, but it's difficult if things are very dynamic.

So is Rails doing something funky with respond_to? then to get that to work?

Yes -- respond_to? is overriden in base.rb, and method_missing is engineered in parallel, so that column names will be treated as methods and will also return true for respond_to?. You can see a counter-example, where method_missing and respond_to? go in different directions, in the find_by_* family of AR class methods:

   >> User.find_by_nick("dblack")    => #<User:0x335bbe8 @attributes={"modified_at"=>nil, ...    >> User.respond_to?("find_by_nick")    => false

That's basically what happens by default, while the behavior you've observed for the column names has to be brought about explicitly by overriding respond_to?

David