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

On 15 Aug 2008, at 19:22, Erik L. wrote:

@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

On 15 Aug 2008, at 22:55, Erik L. wrote:

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