#respond_to? not working for dynamically generated methods

M

Maurice Gladwell

Brian said:
In many cases it may be cheaper to attempt the call and to rescue a
NoMethodError, than to have 'respond_to?' perform almost as much work as
the
call you're testing.

That's certinly true, but the rescue technique you describe is not a
substitute for a working #respond_to?. You're actually sending the
message, not asking the object whether it will respond to it.

These are two completely different operations. The only relevant
solution here would be if you aspired to emulate Object#respond_to? by
checking if the call raises NoMethodError, but that's infeasible because
method call commonly have side effects and/or are destructive.
 
B

Brian Candler

That's certinly true, but the rescue technique you describe is not a
substitute for a working #respond_to?. You're actually sending the
message, not asking the object whether it will respond to it.

These are two completely different operations. The only relevant
solution here would be if you aspired to emulate Object#respond_to? by
checking if the call raises NoMethodError, but that's infeasible because
method call commonly have side effects and/or are destructive.

I'm saying that if you're doing

object.foo if object.respond_to? :foo

then you may as well do

object.foo rescue nil

(or a more specific rescue for NoMethodError)

I guess there are other uses for respond_to?, but I very rarely use it -
especially with ActiveRecord, which was the example given. Why do you want
to check that an ActiveRecord class responds usefully to
find_by_foo_and_bar, but not actually call find_by_foo_and_bar?

Regards,

Brian.
 
T

Tim Hunter

James said:
I don't really understand this stance. My opinion is that providing
a method_missing() implementation is a convenient way for a
programmer to define a lot of dynamic methods. This increases the
messages an object responds to.

Following from that logic, I believe you should also override
respond_to?() to reflect those new messages. It's my opinion
therefore that this thread has exposed a bug in Rails that should be
patched.

FWIW, there's an entry about this in the Ruby Style Guide on
rubygarden.org:
http://wiki.rubygarden.org/Ruby/page/show/RubyStyleGuide/RespondToGoesWithMethodMissing
 
M

Maurice Gladwell

David said:
If it's absolutely known what messages every object will respond to,
then we don't need #respond_to? :)

#respond_to? is usually used when we don't know exactly which object is
referenced by a particular name, and especially what it can do ("duck
typing"). So we check if `foo.respond_to? :to_s` to see if we can tell
it to convert itself to a String.

But if we do know that foo is, say, an Array, we should - as the system
programmers - know which messages it will respond_to and how. It's
generally never the case that we know which object we have, yet are not
sure which messages it will respond to.
I don't know... it's obviously
just a slightly different way of looking at it. I'm uneasy with
having to rewrite respond_to? every time I implement method_missing;
it seems like a lot of work and an awfully tight coupling.

Yes, that is true. Everyone would be happy (some much happier, others a
tiny bit happier :) if we had a really working #respond_to? that would
cover all cases. But implementing such a thing elegantly seems
non-trivial.

It would be interesting to see an implementation of James Edward Gray's
suggestion above. ("You use an Extract Method refactoring to pull out
the matching logic into a separate private method and use that in
both implementations.")
 
D

dblack

Hi --

FWIW, there's an entry about this in the Ruby Style Guide on
rubygarden.org:
http://wiki.rubygarden.org/Ruby/page/show/RubyStyleGuide/RespondToGoesWithMethodMissing

Hmmm... perhaps there needs to be a second entry explaining why it's
better not to put oneself in the position of having to maintain two
almost identical methods in parallel instead of one :)


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
D

dblack

Hi --

#respond_to? is usually used when we don't know exactly which object is
referenced by a particular name, and especially what it can do ("duck
typing"). So we check if `foo.respond_to? :to_s` to see if we can tell
it to convert itself to a String.

#respond_to? != duck typing, though. At least "hard" duck typing is
just:

foo.to_s
But if we do know that foo is, say, an Array, we should - as the system
programmers - know which messages it will respond_to and how. It's
generally never the case that we know which object we have, yet are not
sure which messages it will respond to.

Well... there you get into the issue of class vs. type. If you have a
spare six months, you can read the archives of this list on that topic
:) But certainly in ActiveRecord you get objects (association
collections) that will tell you that they are Arrays but which have
many methods beyond the vanilla Array.new object.

While you're right that my rand(2) example is contrived, I'm not ready
to say that it's so clear that method_missing can never be used to
handle cases that we couldn't anticipate and enumerate. That seems
like a kind of retro-constraint, just so that it will interoperate
with respond_to?.
Yes, that is true. Everyone would be happy (some much happier, others a
tiny bit happier :) if we had a really working #respond_to? that would
cover all cases. But implementing such a thing elegantly seems
non-trivial.

I consider #respond_to? to be working, but I know what you mean :)


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
J

James Edward Gray II

It would be interesting to see an implementation of James Edward
Gray's
suggestion above. ("You use an Extract Method refactoring to pull out
the matching logic into a separate private method and use that in
both implementations.")

#!/usr/bin/env ruby -wKU

class Test
def initialize(*fields)
@fields = fields.map { |f| f.to_s }
end

def find(*args)
"Performing query with args:\n#{args.inspect}"
end

def method_missing(meth, *args, &block)
if find_args = dynamic_finder?(meth)
find( find_args.first,
:conditions => find_args[1..-1].zip(args).
map { |n, v| "#{n} = '#
{v}'"}.
join(" AND ") )
else
super
end
end

def respond_to?(*args)
super || dynamic_finder?(args.first)
end

private

def dynamic_finder?(method_call)
if method_call.to_s =~ /\Afind(_all)?_by_(\w+)\Z/
args = [$1 ? :all : :first]
args += $2.split("_and_")
args if args[1..-1].all? { |f| @fields.include? f }
end
end
end

if __FILE__ == $PROGRAM_NAME
model = Test.new:)name, :age, :height, :weight)

p !!model.respond_to?:)find_by_name_and_height)
p !!model.respond_to?:)find_all_by_age)
p !!model.respond_to?:)find_all_by_race)

puts

puts model.find_by_name_and_height("James", "61 in.")
puts model.find_all_by_name_and_height
end

James Edward Gray II
 
R

Robert Dober

It would be interesting to see an implementation of James Edward
Gray's
suggestion above. ("You use an Extract Method refactoring to pull out
the matching logic into a separate private method and use that in
both implementations.")

#!/usr/bin/env ruby -wKU

class Test
def initialize(*fields)
@fields = fields.map { |f| f.to_s }
end

def find(*args)
"Performing query with args:\n#{args.inspect}"
end

def method_missing(meth, *args, &block)
if find_args = dynamic_finder?(meth)
find( find_args.first,
:conditions => find_args[1..-1].zip(args).
map { |n, v| "#{n} = '#
{v}'"}.
join(" AND ") )
else
super
end
end

def respond_to?(*args)
super || dynamic_finder?(args.first)
end

private

def dynamic_finder?(method_call)
if method_call.to_s =~ /\Afind(_all)?_by_(\w+)\Z/
args = [$1 ? :all : :first]
args += $2.split("_and_")
args if args[1..-1].all? { |f| @fields.include? f }
end
end
end

if __FILE__ == $PROGRAM_NAME
model = Test.new:)name, :age, :height, :weight)

p !!model.respond_to?:)find_by_name_and_height)
p !!model.respond_to?:)find_all_by_age)
p !!model.respond_to?:)find_all_by_race)

puts

puts model.find_by_name_and_height("James", "61 in.")
puts model.find_all_by_name_and_height
end

James Edward Gray II

This is very interesting, I just imagine that dynamic_finder does not
give consistent results depending on events outside the Ruby
program(*). Are you still happy with the semantics, instead of
respond_to? giving false negatives it gives false positives now.
A second issue was brought up by David,( I have no idea why he put a smiley ;)
You have created a dependency between method_missing and respond_to? I
really feel bad about this.

Robert

(*) or maybe by threading
 
R

Robert Dober

I'm not really sure what else to say. I read the documentation for
respond_to?():

$ ri -T Object#respond_to?
----------------------------------------------------- Object#respond_to?
obj.respond_to?(symbol, include_private=false) => true or false
------------------------------------------------------------------------
Returns +true+> if _obj_ responds to the given method. Private
methods are included in the search only if the optional second
parameter evaluates to +true+.

I know that ActiveRecord model classes will except find_by_*()
methods, so the fact that respond_to?() returns false for them makes
the above documentation lie.
James the documentation is not correct anyway, look at the wording,
nothing ever can reply to methods, only to messages. I liked a lot
what David has said about this. >That does not seem good to me and
it's
certainly possible to fix it. Thus, I can only reason that it is a
bug in ActiveRecord.
That is kind of a thing I have not considered in my reply, I
misunderstood that you were dreaming about a magical
Object#respond_to? which would interpret the semantics of
#method_missing generally, hence my surprise.
Is it a good idea not to? What exactly is the use case for
respond_to?()? I have only ever used it to find out if something I
want is available. If I can't do that reliably, it doesn't really
help me much.
Hmm sure but I feel its need more in MetaProgramming, if you want to
fix ActiveRecord I cannot argue with you at all I do not know that
stuff, so I just bail out of the discussion.

However if you would like to spend some time in philosophy about
respond_to? I see a clearcut dilemma between the ideal behavior of
respond_to? and method_missing. See my example above but it is easy to
imagine that #method_missing is dispatching depending on an external
datasource.
How would you want #respond_to? to handle this?

I just feel that for practical reasons #respond_to? does a good job
already. This might
Robert
 
W

Wolfgang Nádasi-Donner

James said:
What exactly is the use case for respond_to?()?

Only as a remark for a special usage, which is not easy (impossible?) without
"respond_to?()".

I have some Libraries for textual analysis, which are usually used via "irb".
The application works on large Arrays of String objects. Based on regular
expressions I define sometimes methods for some of these object, during one
textual analysis often several methods.

Then I can categorize the String objects in the Array by using "respond_to?()".

It is very easy and helpful (may be it sounds complex an strange, but this is
only my bad English).

Wolfgang Nádasi-Donner
 
J

James Edward Gray II

This is very interesting, I just imagine that dynamic_finder does not
give consistent results depending on events outside the Ruby
program(*). Are you still happy with the semantics, instead of
respond_to? giving false negatives it gives false positives now.

Well, if it can't be made accurate, that's a different thing. First,
we would probably need to document that it can't be trusted in such a
case.
You have created a dependency between method_missing and respond_to? I
really feel bad about this.

I guess that means that I made it so that respond_to?() needs to be
updated if method_missing() is? Yes, I guess that's right.

David says that's bad. OK. I thought respond_to?() lying to me was
bad. So I guess we just chose to care about different things.

I guess each person needs to decide what is more important to them.

James Edward Gray II
 
R

Robert Dober

Well, if it can't be made accurate, that's a different thing. First,
we would probably need to document that it can't be trusted in such a
case.


I guess that means that I made it so that respond_to?() needs to be
updated if method_missing() is? Yes, I guess that's right.

David says that's bad. OK.
And so do I, well that sounds quite prepotent, I know.
I thought respond_to?() lying to me was
bad. So I guess we just chose to care about different things.

I guess each person needs to decide what is more important to them.
Well I was hoping to convince you, sure I was, but I see now that
after having discussed things we just have different opinions and that
is that of course.

The most important point I wanted to make - and I probably failed - is
that I am not sure if it really lies for me, but this is a tricky
issue and I can understand what you dislike about it.

Cheers
Robert
 
R

Robert Dober

Only as a remark for a special usage, which is not easy (impossible?) wit= hout
"respond_to?()".

I have some Libraries for textual analysis, which are usually used via "i= rb".
The application works on large Arrays of String objects. Based on regular
expressions I define sometimes methods for some of these object, during o= ne
textual analysis often several methods.

Then I can categorize the String objects in the Array by using "respond_t= o?()".

It is very easy and helpful (may be it sounds complex an strange, but thi= s is
only my bad English).
Hmm as fellow native German Speaker I think your English is perfect.
Could you give an example?
I somehow feel that you might use a different approach than
hitchhiking methods, or are the used for different purposes too.

Sounds interesting.

Cheers
Robert


--=20
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw
 
W

Wolfgang Nádasi-Donner

Robert said:
Sounds interesting.

I don't know, because it is a special situatuation, and it is very experimental.
During usage i build something like a hybride human-software process. At start
some helper libraries are required in "irb", and then I worked with several data
in different buffers, writing helpful code for analysis and changes and throw
the code away after the work is done (usually there is no future use for the
code). If one wants to use this in a "real program" s/he must find out how to
generate (=create) useful Ruby code based on some events - I mean code, that is
not predictable when the program starts.

Robert said:
Could you give an example?

This is difficult, because there is no fixed code, but I can decribe a typical
process.

Someone comes to me with a CD (or ZIP file via mail) with several textual files,
and says "...something is wrong there..", or "...can you help me by producing a
short overview...". Especially in the first situation I have nearly no addition
information. So I load one File into a line oriented buffer structure (based on
an Array of Strings) an start analysing the data by using helper methods and
regular expressions.

Sometimes (=often) the files have explicit relationships via textual remarks.
This is one case, where the usage of singleton methods for lines (=String
objects in an Array) comes up, because it is simple and easy to use. It follows
usually the same pattern:

buffer.each do |line|
if (md = line.match(/a pattern that is of some interest/)
# define a special method for the line
end
end

The definition will take place on the assumption, that the method may be useful
later on, which is not clear in the moment. It is possible, that the method
opens a new buffer, loads another file and does some action on it.

This can happen several times. Later it is possible that I want, that a buffer
applyies such a method for each line the method is defined. I can use the
following short pattern for this:

buffer.each{|line|line.my_method if line.respond_to?:)my_method)}

Remarks:

1) Performance isn't a interesting thing in this situation. When I think about
an output and plan what to do next, I am the bottle-neck in the system.

2) This kind of using singleton methods is attractive, because it isn't
complicated and simple to use (=not much to write), what is very interesting in
an interactive session.

3) Readability of the code isn't relevant, because it will be thrown away after
usage. This can be done, because the problems differ very much. Reusable things
will be build in the libraries, that I require in the very beginning.

4) Readability isn't a big problem, because I wrote some large TECO programs
long time ago... ;-)

I Think this is a very special usage of Ruby.

Wolfgang Nádasi-Donner
 
R

Robert Dober

I don't know, because it is a special situatuation, and it is very experi=
mental.
<snip>
Well it still sounds very interesting to me, I remember when I was
writing Emacs Macros for jumping between events in very very long log
files. If I guess correctly what you are doing here is very intersting
and quite complicated.
But to come back to respond_to?
Am I guessing correctly that you use respond_to? in order to manage
the abilities you have already created for your singletons, as these
abilities are methods?

Cheers
Robert

--=20
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw
 
W

Wolfgang Nádasi-Donner

Robert said:
Am I guessing correctly that you use respond_to? in order to manage
the abilities you have already created for your singletons, as these
abilities are methods?

Yes, these methods are something like "executable attributes" - but - I would
prefer in the moment to say: "don't use it in a real program!".

The usage background is as already named:
1) Execution speed does not matter (even one minute is not a problem),
2) readability does not matter (there are no comments at all),
3) size of code to write does matter (less is better).

I will not recommend to write software (=real programs with a life cycle) based
on this principles.

Wolfgang Nádasi-Donner
 
D

dblack

Hi --

Well, if it can't be made accurate, that's a different thing. First, we
would probably need to document that it can't be trusted in such a case.


I guess that means that I made it so that respond_to?() needs to be updated
if method_missing() is? Yes, I guess that's right.

David says that's bad. OK. I thought respond_to?() lying to me was bad. So
I guess we just chose to care about different things.

I just raise an eyebrow at that much repeated/parallel code.
I guess each person needs to decide what is more important to them.

I think it's just a matter of how you define respond_to? conceptually
-- as a handle on the methods in the object's method lookup path, or
as a registry of everything the object can do without raising an
error. It's interesting in this connection that delegation adds the
delegated methods to the respond_to? roster. I guess that summarizes
the difference for me: a method that's delegated is known to be taken
care of, whereas a method that's missing is missing (even if the
potential error can be averted).


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top