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

J

J2M

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
 
A

ara.t.howard

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

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

-a
 
L

Logan Capaldo

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 }
 
J

J2M

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
 
J

J2M

Logan said:
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 :eek:ption => "something"
that :eek:ption => "something else"
the_other :eek:ption => "and something else"
end

Thanks,
James
 
D

Daniel Schierbeck

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

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


Cheers,
Daniel
 
L

Logan Capaldo

class Foo
def bar
yield
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

ara.t.howard

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
 
T

Tim Fletcher

Logan said:
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 }
 
M

Martin DeMello

class Foo
def bar
yield
rescue NameError => method_call
p method_call.name
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
 
T

Tim Fletcher

Ah, see what you mean now :)

Martin said:
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
 
J

J2M

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

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
 
D

dblack

Hi --

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

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) }

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 :) 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

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
J

J2M

Hi --


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

I just include it into the singleton class, I just chopped it out here!
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.
But the block *is* a lambda :) 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 :)
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.
 
D

dblack

Hi --

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 :)

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


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
J

J2M

You may be right; I didn't demo it because I couldn't figure out
exactly what code I needed to add :) 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
 
D

dblack

Hi --

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?

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

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
J

J2M

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
 
D

dblack

Hi --

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

@@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

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
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

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,135
Latest member
VeronaShap
Top