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

>> Most of what people need to grasp and embrace, in order to understand
>> and use the vast bulk of these techniques, and others, is:
>>
>> * classes are objects
>> * each object (or almost each object) has both a "birth class" and a
>> singleton class
>> * objects don't have methods; they look for them along a
>> class/module search path, with well-defined rules of order and
>> inclusion
>> * there's one and only one 'self' at every point in the program
>> * instance variables belong to 'self', always
>> * class variables are completely unrelated to instance variables,
>> in spite of the use of @ in one and @@ in the other
>> * the def and class keywords start new local scopes
>> * the class keyword can take either a constant (for a named class)
>> or a "<< object" expression (for a singleton class)
>
> I have to admit, I don't understand what this means in practical
> terms. In fact I have a question I need to find an answer to, and I
> want to post it in this thread, rather than as its own thing, in the
> possibly idealistic hope that somebody can frame an answer in terms of
> these principles, and help me understand them.

If they're not of practical value, then they're not of value. So I'll
give it a try :slight_smile:

Now I actually have the inverse problem. The good news is the code
works perfectly -- thank you, by the way -- but the bad news is I
don't totally know why.

> Basically, I need to change a class method. It's the Ferret method
> full_text_search, in a Rails app.
>
> e.g.:
>
> Thing.full_text_search("asdf")
>
> I need to modify this class method. I tried alias, but did it wrong.
>
> alias original_full_text_search full_text_search
>
> but of course it doesn't work, because it's not an instance method but
> a class method. Is this a good place for class_eval? I could be way
> off.
>
> Actually now that I re-read it, I think I do understand the basic
> concepts, I think I'm just weak on the syntax.

In general, if you want to do:

   alias old_x x

you want to do so inside a class definition body for the class where x
is defined (or a subclass of that class).

A "class method" is basically a singleton method of a Class object.
That means that the class where it's defined is the singleton method
of that class object (because that's what singleton classes are: the
places where objects' singleton methods are defined).

Ah, ok. The class object creates a singleton, which is where all the
method definitions live?

In your case, the object in question is Thing. To get into a class
definition body for a singleton class, you use the class keyword plus
the "<< object" construct:

   class << Thing

And this is because the class, having been defined, now has a
singleton containing all its methods, and we don't want to create a
new patch, we just want to append new methods to the existing
singleton?

You can now perform the aliasing:

     alias original_full_text_search full_text_search
     # redefinitions etc. will follow here

   end

Here's a complete mini-example:

class Thing
   def self.x
     puts "old x"
   end
end

class << Thing
   alias old_x x
   def x
     puts "new x..."
     old_x
   end
end

Thing.x

I think I do get it, although I admit I'm still puzzled on the
singleton class / birth class thing.

···

--
Giles Bowkett
http://www.gilesgoatboy.org

http://gilesgoatboy.blogspot.com

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

Everything is being passed by reference, but the references themselves
are 'hidden', to enable GC.
data = File.read('ridiculously large file')
some_object.some_method(data)

The data is not copied; some_method just receives a reference, and a
binding (the String object containing the data is bound to the name
'data').

Calling methods on 'data' implicitly dereferences it (this isn't
really how it's referred to in Ruby, but it seems to be how C people
like to think of it. heh.)

All you need to do to 'free' memory is keep your methods short. Local
variables will go out of scope, and be collected for you.

By the way.. the "real things done with ruby" book is called 'The Ruby
Way - 2nd Edition', by Hal Fulton. =)

···

On 12/13/06, Christopher Coleman-Smith <chris.colemansmith@gmail.com> wrote:

Simple question, i come from a C/++ background and i'm just really
starting to get "real things done with ruby" ( that should be a book
title). Anyway as i start writing methods that return objects and
variables and take objects and variables i've started to wonder how they
actually get passed around. I haven't found a specific pointer or
reference type construction, although since everything i've tried to do
has worked i've not tried very hard.

I'm guessing that everything gets passed around by reference? Is that
true? Also what about situations where you'd really want to use a
pointer, like referring to huge things or ,god forbid, doing grimy
things with arrays. It is very nice having all these things automated,
like the garbage collection etc, but i still find myself wanting to free
things. I've only just gotten over wanting to semi-colon every statement.

Probably by asking this i'm showing how i really haven't groked ruby at
all. I'm happy to admit that.

Hi --

Simple question, i come from a C/++ background and i'm just really starting to get "real things done with ruby" ( that should be a book title). Anyway as i start writing methods that return objects and variables and take objects and variables i've started to wonder how they actually get passed around. I haven't found a specific pointer or reference type construction, although since everything i've tried to do has worked i've not tried very hard.

I'm guessing that everything gets passed around by reference? Is that true? Also what about situations where you'd really want to use a pointer, like referring to huge things or ,god forbid, doing grimy things with arrays. It is very nice having all these things automated, like the garbage collection etc, but i still find myself wanting to free things. I've only just gotten over wanting to semi-colon every statement.

Basically, everything (or almost everything) is a reference:

   a = [1,2,3] # a is assigned a reference to the array
   b = a # b gets a copy of that reference
   b.push(4) # the array now has four elements
   p a # as you'll see if you print it via any reference

When you pass objects around via variables, technically you're passing
by value... but the "value" of the variable is a reference to an
object.

When you pass objects around via literals (like puts "hello"), a
reference to the literal is sent.

All of this means that if you do something to the arguments in a
method, the changes are permanent in the original object:

   def add_element(array)
     array.push("new element!")
   end

   a = [1,2,3]
   add_element(a)
   p a

So you'll see a fair amount of "dup"ing going on in methods that want
to be safe.

There's no reference-to-a-reference. It's not like C pointers, which
can point to other pointers. Every Ruby reference is exactly one step
away from the object it refers to, and all references to an object are
equivalent to all other references to that object.

Some values are immediate; i.e., when you assign a variable to them,
they get stored directly and not via a reference: integers, true,
false, nil, and symbols (and any I've forgotten). Hence the "almost
everything" above.

David

···

On Thu, 14 Dec 2006, Christopher Coleman-Smith wrote:

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

Hi --

>> Most of what people need to grasp and embrace, in order to understand
>> and use the vast bulk of these techniques, and others, is:
>>
>> * classes are objects
>> * each object (or almost each object) has both a "birth class" and a
>> singleton class
>> * objects don't have methods; they look for them along a
>> class/module search path, with well-defined rules of order and
>> inclusion
>> * there's one and only one 'self' at every point in the program
>> * instance variables belong to 'self', always
>> * class variables are completely unrelated to instance variables,
>> in spite of the use of @ in one and @@ in the other
>> * the def and class keywords start new local scopes
>> * the class keyword can take either a constant (for a named class)
>> or a "<< object" expression (for a singleton class)
>
> I have to admit, I don't understand what this means in practical
> terms. In fact I have a question I need to find an answer to, and I
> want to post it in this thread, rather than as its own thing, in the
> possibly idealistic hope that somebody can frame an answer in terms of
> these principles, and help me understand them.

If they're not of practical value, then they're not of value. So I'll
give it a try :slight_smile:

Now I actually have the inverse problem. The good news is the code
works perfectly -- thank you, by the way -- but the bad news is I
don't totally know why.

> Basically, I need to change a class method. It's the Ferret method
> full_text_search, in a Rails app.
>
> e.g.:
>
> Thing.full_text_search("asdf")
>
> I need to modify this class method. I tried alias, but did it wrong.
>
> alias original_full_text_search full_text_search
>
> but of course it doesn't work, because it's not an instance method but
> a class method. Is this a good place for class_eval? I could be way
> off.
>
> Actually now that I re-read it, I think I do understand the basic
> concepts, I think I'm just weak on the syntax.

In general, if you want to do:

   alias old_x x

you want to do so inside a class definition body for the class where x
is defined (or a subclass of that class).

A "class method" is basically a singleton method of a Class object.
That means that the class where it's defined is the singleton method
of that class object (because that's what singleton classes are: the
places where objects' singleton methods are defined).

Ah, ok. The class object creates a singleton, which is where all the
method definitions live?

All the singleton method definitions of that class object, yes. It's
really just a not-very-special case of the general principle that an
object's singleton methods live in the object's singleton class.

It's perhaps easier to see the workings of it when the object isn't a
class:

   str = "I am a string
   class << str
     def just_for_me
       puts "This method is just for me!"
     end
   end

I've defined a singleton method on the one String object, str. No
other string will have that method. If I'd defined it in String, they
would. But I've defined it in str's singleton class (class << str),
so only str will have it:

   str.just_for_me # This method is just for me!
   "other".just_for_me # NoMethodError

Now, watch as I take that example -- and the explanation -- and
substitute a Class object for the String object:

   class Thing; end
   class << Thing
     def just_for_me
       puts "This method is just for me!
     end
   end

I've defined a singleton method on the one Class object, Thing. No
other class will have that method. If I'd defined it in Class, they
would. But I've defined it in Thing's singleton class (class <<
Thing), so only Thing will have it:

   Thing.just_for_me # This method is just for me!
   Array.just_for_me # NoMethodError

Note that in the Thing example, just_for_me is what we would call a
"class method". A class method, then, is just a singleton method
defined for a class object.

In your case, the object in question is Thing. To get into a class
definition body for a singleton class, you use the class keyword plus
the "<< object" construct:

   class << Thing

And this is because the class, having been defined, now has a
singleton containing all its methods, and we don't want to create a
new patch, we just want to append new methods to the existing
singleton?

Yes, we want to define methods inside the singleton class of Thing.
The singleton class of Thing is not actually created until we ask to
get inside it, but that's really an implementation detail.

I think I do get it, although I admit I'm still puzzled on the
singleton class / birth class thing.

It's nature vs. nurture. Every object comes into existence with
certain capabilities -- that is, when you send it messages, it
searches through specific classes and modules, in a specific order,
looking for corresponding methods. From that perspective, all
instances of a given class are the same as each other.

But you can also add capabilities to an object. An object that is
"born" ignorant of a certain message need not remain that way. You
can do this by adding to the object's class -- if you want *all*
instances of that class to have the new capability. Or, if you just
want one object to have it, you can add a method to that object's
singleton class.

So by its nature, a String instance can do certain things:

   str = "hello there"
   str.split(/ll/)
   str.upcase
   str.reverse

etc. By way of nurture, we can add to this string by adding to all
strings:

   class String
     def new_method
       ...
     end
   end

or by adding a method to this one string's singleton class, as in the
examples above.

(You can, by the way, also insert a method into an object's singleton
class with the "def" keyword, like this:

   def str.new_method
     ...
   end

or

   def Thing.some_class_method
     ...
   end

.)

The main thing going on in all this is that Ruby objects can change
over the course of their lives. That's the principle being enforced;
and singleton classes are just the way Matz has chosen to bring that
principle to life.

I get a lot of mileage out of thinking of the method look-up path.
When you send a message to an object, it walks the path -- a
succession of classes and modules, like rooms strung along a corridor
-- and looks for the method. First, it checks in its own singleton
class (if you've created one), and in any modules mixed in to the
singleton class. If no method with the right name is found, it
proceeds to its "birth" class, and then to the modules mixed in there.
And so on -- until it gets to Object, and the module that's mixed in
*there* (Kernel). Then, if it still hasn't found the method, it gives
up.

This post could probably do with some more editing but I don't have it
in me right now :slight_smile: I hope it's helpful. You might also have a look
at a little essay that covers similar material:
http://www.wobblini.net/singletons.html\. (And my book, of course :slight_smile:

David

···

On Thu, 14 Dec 2006, Giles Bowkett wrote:

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

Hi --

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

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

···

On Tue, 12 Dec 2006, J2M wrote:

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

Hi --

>Simple question, i come from a C/++ background and i'm just really
>starting to get "real things done with ruby" ( that should be a book
>title). Anyway as i start writing methods that return objects and
>variables and take objects and variables i've started to wonder how they
>actually get passed around. I haven't found a specific pointer or
>reference type construction, although since everything i've tried to do
>has worked i've not tried very hard.
>
>I'm guessing that everything gets passed around by reference? Is that
>true? Also what about situations where you'd really want to use a pointer,
>like referring to huge things or ,god forbid, doing grimy things with
>arrays. It is very nice having all these things automated, like the
>garbage collection etc, but i still find myself wanting to free things.
>I've only just gotten over wanting to semi-colon every statement.

Basically, everything (or almost everything) is a reference:

  a = [1,2,3] # a is assigned a reference to the array
  b = a # b gets a copy of that reference
  b.push(4) # the array now has four elements
  p a # as you'll see if you print it via any reference

When you pass objects around via variables, technically you're passing
by value... but the "value" of the variable is a reference to an
object.

When you pass objects around via literals (like puts "hello"), a
reference to the literal is sent.

All of this means that if you do something to the arguments in a
method, the changes are permanent in the original object:

  def add_element(array)
    array.push("new element!")
  end

  a = [1,2,3]
  add_element(a)
  p a

Just to make it clearer: anything destructive or mutating you do to the
object inside the method will propagate to the original object.

One perhaps surprising aspect of the 'everything is a reference value'
style of argument passing is that assignment to the variable name will
simply have it point to a different object rather than modify the
original:

a = "Hello!"

def foo(string)
   string = "Hello to you too!"
end

foo a
p a # => "Hello!"

So you'll see a fair amount of "dup"ing going on in methods that want
to be safe.

There's no reference-to-a-reference. It's not like C pointers, which
can point to other pointers. Every Ruby reference is exactly one step
away from the object it refers to, and all references to an object are
equivalent to all other references to that object.

Some values are immediate; i.e., when you assign a variable to them,
they get stored directly and not via a reference: integers, true,
false, nil, and symbols (and any I've forgotten). Hence the "almost
everything" above.

I think this can just be considered to be an implementation detail,
frankly. Fixnums etc. just do not have any destructive methods that
one could employ inside a method.

···

On 2006.12.14 05:20, dblack@wobblini.net wrote:

On Thu, 14 Dec 2006, Christopher Coleman-Smith wrote:

This post could probably do with some more editing but I don't have it
in me right now :slight_smile: I hope it's helpful. You might also have a look
at a little essay that covers similar material:
http://www.wobblini.net/singletons.html\. (And my book, of course :slight_smile:

Actually I might as well give you some free advertizing, I did in fact
check your book on this. I only just saw this e-mail today, missed it
somehow, but I was thinking about the question, so I re-read bits of
your book and it did actually clear some of this up for me.
Specifically the singleton/birth class thing.

···

--
Giles Bowkett
http://www.gilesgoatboy.org

http://gilesgoatboy.blogspot.com

dblack@wobblini.net wrote:

Hi --

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

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

Hi --

All of this means that if you do something to the arguments in a
method, the changes are permanent in the original object:

  def add_element(array)
    array.push("new element!")
  end

  a = [1,2,3]
  add_element(a)
  p a

Just to make it clearer: anything destructive or mutating you do to the
object inside the method will propagate to the original object.

And yet clearer: the object inside the method IS the original object
:slight_smile:

Some values are immediate; i.e., when you assign a variable to them,
they get stored directly and not via a reference: integers, true,
false, nil, and symbols (and any I've forgotten). Hence the "almost
everything" above.

I think this can just be considered to be an implementation detail,
frankly. Fixnums etc. just do not have any destructive methods that
one could employ inside a method.

The immediate-value thing does play a role in some language-design
things, though, particularly the absence of x++ on the grounds that
doing 1++ doesn't make any sense and, given x = 1, x *is* 1.

David

···

On Thu, 14 Dec 2006, Eero Saynatkari wrote:

On 2006.12.14 05:20, dblack@wobblini.net wrote:

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

Hi --

···

On Sat, 16 Dec 2006, Giles Bowkett wrote:

This post could probably do with some more editing but I don't have it
in me right now :slight_smile: I hope it's helpful. You might also have a look
at a little essay that covers similar material:
http://www.wobblini.net/singletons.html\. (And my book, of course :slight_smile:

Actually I might as well give you some free advertizing, I did in fact
check your book on this. I only just saw this e-mail today, missed it
somehow, but I was thinking about the question, so I re-read bits of
your book and it did actually clear some of this up for me.
Specifically the singleton/birth class thing.

Cool -- on all counts: the advertising of course :slight_smile: but particularly
the fact that it's coming clear for you.

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)

Hi --

···

On Tue, 12 Dec 2006, J2M wrote:

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

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

dblack@wobblini.net wrote:

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

Hi --

···

On Wed, 13 Dec 2006, J2M wrote:

dblack@wobblini.net wrote:

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

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)

dblack@wobblini.net 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 --

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

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

···

On Wed, 13 Dec 2006, J2M wrote:

dblack@wobblini.net wrote:

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

dblack@wobblini.net wrote:

> 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

Whoops, that wasn't actually my code :-/ Just to restore a little
credibility it should have read;

          def method_missing(m, *args, &block)
            return @@return << { m.to_sym => args }
          end

> end
> attributes = lambd.instance_eval(&block)
> end
> p attributes
> end
> end

Which works fine ( I do want to reset @@return each time).

@@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 = {}
       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

But this is nicer because the class variable was a kludge because I
couldn't figure out how to keep a variable from outside the block in
scope.

Thanks,
James