Tricky metaprogramming

Hi
This is a tricky one I think.I have a Rails plugin that handles
collections. For example I want to be able to write the following in a
model:

class User < ActiveRecord::Base
  my_class_method :collections => [:pages, :chapters, :books]
end

With only the relevant pieces left and with neutral names the module
file looks like this:

module MyModule1
  module MyModule2 #:nodoc:
    module MyModule3 #:nodoc:

      def self.included(mod)
        mod.extend(ClassMethods)
      end

      module ClassMethods
        def my_class_method(*args)
          @collections = args[0][:collections]

          extend
MyModule1::MyModule2::MyModule3::CollectionSingletonMethods
          include
MyModule1::MyModule2::MyModule3::CollectionInstanceMethods
        end
      end

      module CollectionSingletonMethods
      end

      module CollectionInstanceMethods
        def self.included(base)
          base.class_eval do
            @collections.each do |collection|
              define_method collection do
                # Do something useful for the collection here
              end
            end
          end
        end
      end
    end
  end
end

This means I can create one method per entry in the collection to be
called like user_instance.pages, user_instance.chapters and so on. So
far so good.

But now I want to define one method that does something for every
collection. So this call:

user_instance.do_something_for_all

would call a method that if it was hardcoded would look like something
like this:

class User
  def so_something_for_all
    @pages = []
    @chapters = []
    @books = []
  end
end

Does anyone know how to accomplish this in the framework above. I
would be extremely happy for all input in this, references to texts
about it is very welcome.

Kindest regards

Erik Lindblad

user_instance.do_something_for_all

would call a method that if it was hardcoded would look like something
like this:

class User
def so_something_for_all
   @pages = []
   @chapters = []
   @books = []
end
end

Does anyone know how to accomplish this in the framework above. I
would be extremely happy for all input in this, references to texts
about it is very welcome.

is it any more complicated than

def do_something_for_all
   self.class.collections.each {|collection| yield
self.send(collection)}
end

or have I missed something? (assuming you have a class method that
returns the names of the collections and an accessor method for each
collection)

Fred

Hi Fred and thanks for taking the time to read through this long
question.

I think for this particular example I can build a method in the way
you describe. I got so intertwined in Procs and metaprogramming that
this solution eluded me.

But for arguments sake, assume I wanted to construct the whole method
body in advance, for example as a string and create a method from
that. Would that be possible? Maybe put the whole define_method
thingie in a string and run eval on that?

Kindest regards

Erik

Hi Fred and thanks for taking the time to read through this long
question.

I think for this particular example I can build a method in the way
you describe. I got so intertwined in Procs and metaprogramming that
this solution eluded me.

But for arguments sake, assume I wanted to construct the whole method
body in advance, for example as a string and create a method from
that. Would that be possible? Maybe put the whole define_method
thingie in a string and run eval on that?

I'm not quite sure what you're getting at but you ca do pretty much
anything you want with the string form of class_eval.

Fred

Hi Fred

Yes, the string form of class_eval did just the trick.

Many thanks for the help. Priceless.

Regards

Erik