How do I catch a missing method on a passed block?

I would like to invoke method_missing on baz in this code;

x = Foo.new
x.bar { baz }

I tried the following, and although the method is added to the proc
objects singleton class, the method_missing method never gets called.
Is there any way to achieve this? My current code (that doesn’t work)
is below;

class Foo

def bar(*values, &block)

if block_given?

  class << block
    def method_missing(m, *args, &block)
      puts "missing #{m}"
    end
  end

  block.call

end

end
end

On Tue, 5 Dec 2006, J2M wrote:

class Foo

 block.call

end
end
end

but block will respond_to? ‘call’ - ergo your method_missing hook
will
never fire? what are you trying to do?

-a

On Tue, Dec 05, 2006 at 08:04:33AM +0900, J2M wrote:

I would like to invoke method_missing on baz in this code;

x = Foo.new
x.bar { baz }

I tried the following, and although the method is added to the proc
objects singleton class, the method_missing method never gets called.
Is there any way to achieve this? My current code (that doesn’t work)
is below;

You’d want to define the method_missing
<— here
x.bar { baz }

Obviously that’s not always practical, so what you need to do is change
self. That can be acheived by using #instance_eval. This has
disadvantages, obviously, but that’s how to do it if you want to do it.

Or you can pass a parameter into the block:
x.bar { |q| q.baz }

Logan C. wrote:

Or you can pass a parameter into the block:
x.bar { |q| q.baz }

Thanks Logan, that is what I ended up doing, I just wanted to avoid
having to pass the parameter if it was at all possible. I am writing a
dsl so wanted it to be as clean as possible e.g.

define_stuff do
this :option => “something”
that :option => “something else”
the_other :option => “and something else”
end

Thanks,
James

On Tue, 2006-12-05 at 08:04 +0900, J2M wrote:

class Foo

  block.call

end  

end
end

class Foo
def bar
yield
rescue NoMethodError => error
# mojo goes here
end
end

Cheers,
Daniel

[email protected] wrote:

but block will respond_to? ‘call’ - ergo your method_missing hook will
never fire? what are you trying to do?

I want to catch a missing method (baz) inside the passed block.

Thanks,
James

On Tue, Dec 05, 2006 at 11:54:06PM +0900, Daniel S. wrote:

  end  
rescue NoMethodError => error
  # mojo goes here
end

end

If ruby were common lisp, that could actually work. But it’s not and our
exceptions are not resuamble :).

-a ! Thanks, I will give this a shot. I really appreciate your help.
James

On Tue, 5 Dec 2006, J2M wrote:

[email protected] wrote:

but block will respond_to? ‘call’ - ergo your method_missing hook will
never fire? what are you trying to do?

I want to catch a missing method (baz) inside the passed block.

here’s how to catch it:

harp:~ > cat a.rb
class Foo
def bar *a, &b
if b
singleton_class = class << b; self; end
singleton_class.module_eval{
def method_missing m, *a, &b
p [m, a, b]
end
}
b.instance_eval &b
end
end
end

Foo.new.bar{ baz }

harp:~ > ruby a.rb
[:baz, [], nil]

and here’s how to forward it to another object, in this case the Foo
object:

harp:~ > cat a.rb
class Foo
def bar *a, &b
if b
this = self
singleton_class = class << b; self; end
singleton_class.module_eval{
@@this = this
def method_missing m, *a, &b
@@this.send m, *a, &b
end
}
b.instance_eval &b
end
end

 def baz
   p 'baz'
 end

end

Foo.new.bar{ baz }

harp:~ > ruby a.rb
“baz”

there are many ways to skin that cat. can you start out by just telling
us
exactly what you want are trying to do? the various ways of solving
this
each have consequences which could more easily be chosen from if we
knew.

kind regards.

-a

On 12/6/06, Tim F. [email protected] wrote:

end
end

Foo.new.bar { baz }

Breaks:

irb(main):008:0> a = Foo.new
=> #Foo:0x2b301a826778
irb(main):009:0> a.bar { baz }
:baz
=> nil
irb(main):010:0> a.bar { baz; quux }
:baz
=> nil

martin

Ah, see what you mean now :slight_smile:

Logan C. wrote:

If ruby were common lisp, that could actually work. But it’s not and our
exceptions are not resuamble :).

class Foo
def bar
yield
rescue NameError => method_call
p method_call.name
end
end

Foo.new.bar { baz }

This is what I finally ended up with - which I think is quite elegant
:slight_smile:

class Foo
def bar(quantal, *args, &block)
if block_given?
lambd = lambda(&block)
class << lambd
@@return = {}.extend(Appenders::Hashs)
def method_missing(m, *args, &block)
return @@return << { m.to_sym => args_to_attributes(args) }
end
end
attributes = lambd.instance_eval(&lambd)
end
parse quantal, attributes
end
end # Foo

What made this sweet for me was turning the &block into a lambda let me
return the result from the instance_eval to bar where other solutions
kept them in scope of the &block.

James

[email protected] wrote:

Hi –

method?
I just include it into the singleton class, I just chopped it out here!

     end
   end
   attributes = lambd.instance_eval(&lambd)
 end
 parse quantal, attributes

If that if block hasn’t run, attributes is going to be undefined. Did
you mean to put this line inside the block?

If no block is passed, I had some code (that is snipped for the sake of
brevity) to build the attributes.

Sorry lazy cut and paste job, I should have stripped it back to the
original question.

end
end # Foo

What made this sweet for me was turning the &block into a lambda let me
return the result from the instance_eval to bar where other solutions
kept them in scope of the &block.

But the block is a lambda :slight_smile: That’s what the &block argument does:
it captures the code block in an object and assigns a variable to it.

But without the lambda it is a raw proc that doesn’t return the result
of the method - from what I understand from page 344 of Pickaxe. I
tried without and didn’t get the result passed back from
method_missing, but I will now test that again :slight_smile:

You could probably also get rid of the class variable by using
define_method. (Not essential, but always a bit of a plus style-wise,
I think.)

Sweet, didn’t think of that, I was wanting to get rid of it.

Thanks David.

Hi –

On Tue, 12 Dec 2006, J2M wrote:

method_missing, but I will now test that again :slight_smile:
You may be right; I didn’t demo it because I couldn’t figure out
exactly what code I needed to add :slight_smile: You may be hitting one of the
Proc/proc/lambda/block difference things.

David

Hi –

On Tue, 12 Dec 2006, J2M wrote:

       return @@return << { m.to_sym => args_to_attributes(args) }

I’m curious where args_to_attributes is defined. Is it a top-level
method?

     end
   end
   attributes = lambd.instance_eval(&lambd)
 end
 parse quantal, attributes

If that if block hasn’t run, attributes is going to be undefined. Did
you mean to put this line inside the block?

end
end # Foo

What made this sweet for me was turning the &block into a lambda let me
return the result from the instance_eval to bar where other solutions
kept them in scope of the &block.

But the block is a lambda :slight_smile: That’s what the &block argument does:
it captures the code block in an object and assigns a variable to it.

You could probably also get rid of the class variable by using
define_method. (Not essential, but always a bit of a plus style-wise,
I think.)

David

Hi –

On Wed, 13 Dec 2006, J2M wrote:

But without the lambda it is a raw proc that doesn’t return the result
I don’t see how to get rid of the class variable as I am building a
hash from the repeated call to method_missing inside the block then
returning that through the instance_eval, if I don’t use the class
variable, I will only get the result of the last call to
method_missing?

I’d have to see the current version of the code to be sure, but
define_method gives you a closure, so if you have a local variable
outside it, that variable will be captured. Or maybe an instance
variable. Can you post what you’ve currently got?

David

[email protected] wrote:

method_missing, but I will now test that again :slight_smile:

You may be right; I didn’t demo it because I couldn’t figure out
exactly what code I needed to add :slight_smile: You may be hitting one of the
Proc/proc/lambda/block difference things.

You are right David, I chopped the lambda and it still works ;-/

I don’t see how to get rid of the class variable as I am building a
hash from the repeated call to method_missing inside the block then
returning that through the instance_eval, if I don’t use the class
variable, I will only get the result of the last call to
method_missing?

James

[email protected] wrote:

I’d have to see the current version of the code to be sure, but
define_method gives you a closure, so if you have a local variable
outside it, that variable will be captured. Or maybe an instance
variable. Can you post what you’ve currently got?

class Foo
def bar(*args, &block)
if block_given?
class << block
@@return = {}
lambd.define_method(:method_missing, (m, *args, &b)
return @@return.merge!({ m.to_sym => args })
end
end
attributes = lambd.instance_eval(&block)
end
p attributes
end
end # Foo

Thanks David, this is a good education for me.

James

Hi –

On Wed, 13 Dec 2006, J2M wrote:

 if block_given?

end
@@return is going to be a new variable each time you call bar with a
block, so it won’t accumulate results from multiple calls (which I
think is what you want to do).

See if this either does what you want or suggests some possibilities:

class Foo

 def bar(*args, &block)
   method_calls = @method_calls ||= {}
   if block
     (class << block; self; end).class_eval do
       define_method(:method_missing) do |m,*args|
         method_calls.merge!({ m.to_sym => args })
       end
     end
     attributes = block.instance_eval(&block)
     p attributes
   end
 end

end

x = Foo.new
x.bar { baz(1,2,3) }
x.bar { blah(4,5,6) }

Output:

{:baz=>[1, 2, 3]}
{:baz=>[1, 2, 3], :blah=>[4, 5, 6]}

David