method_missing and send

N

Neil Mc Laughlin

I'm in the process of writing a logging package for a project. The actual
logging methods can be specified at runtime and are implemented with a class
method_missing call.

That works fine until I call the warn method using 'send'. I would have
expected the object to respond identically to both the direct invocation and
the indirect invocation via 'send'.

Is this a bug in ruby or have I missed something?

class B
def self.method_missing(sym, *args)
puts "%s: %s"%[sym, args[1]]
end
end

B.log(self, "There was a young man called McKnight,")
B.send:)log, self, "Who could travel faster than light.")
B.warn(self, "One day at about noon, he went to the moon")
B.send:)warn, self, "And arrived back the previous night.")

bash-2.05b$ ruby -v
ruby 1.8.1 (2004-01-27) [i386-mswin32]
bash-2.05b$
bash-2.05b$ tmp.rb
log: There was a young man called McKnight,
log: Who could travel faster than light.
warn: One day at about noon, he went to the moon
/tmp.rb:71:in `warn': wrong number of arguments(2 for 1) (ArgumentError)
from ./tmp.rb:71:in `send'
from ./tmp.rb:71
bash-2.05b$


Neil.
 
G

gabriele renzi

il Mon, 14 Jun 2004 20:13:42 +0900, "Neil Mc Laughlin"
I'm in the process of writing a logging package for a project. The actual
logging methods can be specified at runtime and are implemented with a class
method_missing call.

That works fine until I call the warn method using 'send'. I would have
expected the object to respond identically to both the direct invocation and
the indirect invocation via 'send'.

Is this a bug in ruby or have I missed something?

well, you have to consider that warn is already a method in ruby;foo
=> nil


OTOH it works for me:["t", "t", "y"]
=> nil[:t, :t, :t, :t]
=> nil

so I believe you did something wrong. But I wonder: why don'tu you use
the logger module (that comes with ruby 1.8) or log4r ?
 
T

ts

N> Is this a bug in ruby or have I missed something?

#warn is a private method of Kernel

When you call it, like a public method (i.e. B.warn), ruby don't find it
and call #method_missing

With #send ruby is able to find any methods (even private) and call it.

For example

svg% cat b.rb
#!/usr/bin/ruby
class A
class << self
def method_missing(*args)
puts "method_missing : #{args}"
end

private
def tt(*args)
puts "tt : #{args}"
end
end
end

A.tt(1, 2)
A.send:)tt, 3, 4)
svg%

svg% b.rb
method_missing : tt12
tt : 34
svg%


Guy Decoux
 
N

Neil Mc Laughlin

Thanks for that. I see that send does indeed call the private method.

I'm still a bit puzzled as to why 'obj.send:)msg)' should behave differently
from 'obj.msg'. My impression was that they are different ways of expressing
the same concept - sending a message to an object. That's why I expected the
same result from either call.

Does anyone else think this odd or even in violation of the principle of
least surprise?

Regards, Neil

----- Original Message -----
From: "ts" <[email protected]>
To: "ruby-talk ML" <[email protected]>
Cc: <[email protected]>
Sent: Monday, June 14, 2004 12:25 PM
Subject: Re: method_missing and send
 
J

Jean-Hugues ROBERT

I'm in the process of writing a logging package for a project. The actual
logging methods can be specified at runtime and are implemented with a class
method_missing call.

That works fine until I call the warn method using 'send'. I would have
expected the object to respond identically to both the direct invocation and
the indirect invocation via 'send'.

Is this a bug in ruby or have I missed something?

class B
def self.method_missing(sym, *args)
puts "%s: %s"%[sym, args[1]]
end
end

B.log(self, "There was a young man called McKnight,")
B.send:)log, self, "Who could travel faster than light.")
B.warn(self, "One day at about noon, he went to the moon")
B.send:)warn, self, "And arrived back the previous night.")

bash-2.05b$ ruby -v
ruby 1.8.1 (2004-01-27) [i386-mswin32]
bash-2.05b$
bash-2.05b$ tmp.rb
log: There was a young man called McKnight,
log: Who could travel faster than light.
warn: One day at about noon, he went to the moon
../tmp.rb:71:in `warn': wrong number of arguments(2 for 1) (ArgumentError)
from ./tmp.rb:71:in `send'
from ./tmp.rb:71
bash-2.05b$


Neil.

Hi,

As a side note, you may want to consider a solution
using blocks:

def trace( domain = :debug, &block )
if $domains[domain] then
p block.call()
end
end

trace{ "debug"}
trace( :info){ "Hello"}
trace( :warn){ "fast" }

Doing so is rather efficient when the said domain is disabled.
As a result you can keep traces in production code.

You can even:

def off( domain )
if domain == :trace then
alias trace noop
end
$domains[domain] = false
end

def noop( *a ); end

...

off( :trace) # turns all tracing off

Yours,

JeanHuguesRobert
 
J

Joel VanderWerf

Jean-Hugues ROBERT said:
As a side note, you may want to consider a solution
using blocks:

def trace( domain = :debug, &block )
if $domains[domain] then
p block.call()
end
end

Nice. Small point: it's more efficient to use yield than to instantiate
a Proc:

def trace( domain = :debug)
if $domains[domain] then
p yield
end
end

Even if the trace is disabled, the &block construct will instantiate a Proc.
 
D

David Heinemeier Hansson

I'm still a bit puzzled as to why 'obj.send:)msg)' should behave
differently
from 'obj.msg'. My impression was that they are different ways of
expressing
the same concept - sending a message to an object. That's why I
expected the
same result from either call.

Does anyone else think this odd or even in violation of the principle
of
least surprise?

I too found it odd, but very convenient. By using obj#send, you also
get to sidestep the encapsulation support. So it's possible to call
protected and private methods this way. That might be considered in
violation of POLS, but it's pretty darn useful in those edge cases
where you really do want to, knowingly, sidestep encapsulation.
--
David Heinemeier Hansson,
http://www.instiki.org/ -- A No-Step-Three Wiki in Ruby
http://www.basecamphq.com/ -- Web-based Project Management
http://www.loudthinking.com/ -- Broadcasting Brain
http://www.nextangle.com/ -- Development & Consulting Services
 
P

Paul Brannan

I too found it odd, but very convenient. By using obj#send, you also
get to sidestep the encapsulation support. So it's possible to call
protected and private methods this way. That might be considered in
violation of POLS, but it's pretty darn useful in those edge cases
where you really do want to, knowingly, sidestep encapsulation.

IMO instance_eval is better for that case.

Paul
 

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

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top