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