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