From: Ben Nagy Date: 2006-09-04T14:53:59+09:00 Subject: Re: Exploring Metaprogramming > -----Original Message----- [...] > On 9/3/06, Logan Capaldo wrote: [...] > > class Creature > > def self.command(cmd) > > old_initialize = instance_method(:initialize) > > define_method(:initialize) do |*args| > > @commands ||= [] > > @commands |= [cmd] > > old_initialize.bind(self).call(*args) > > end > > end > > > > def commands > > @commands ||= [] > > end > > end > > > > class Dragon < Creature > > command :fly > > def fly > > puts "I'm flying" > > end [...] I'm doing a very similar thing, but my approach was different. I have something vaguely like: class Creature class << self; attr_reader :commands end def self.command( cmd ) @commands ||= [] @commands |= [cmd] end [...] def initialize( *args ) self.class.commands.each do |command| #do something [...or maybe even...] @commands=self.class.commands Which kind of puts a sticky note on the fridge of the derived class that it needs to do something, but allows you to put off what you're doing until you're actually instantiating the object. This way when you get to #do something, you can do it based on the arguments passed at initialize time, like say you get dragon=Dragon.new(AmazinglyPowerful) - you can make your breathe_fire command hot enough to melt sand. I have also actually been passing code as blocks: Class Dragon < Creature command :fly, proc {puts "Lookit me, Mom!"} end Which lets you instance_eval the block later in the correct namespace, or just define the method at runtime with define_method. That can also be used in combination with a trick I found here on ruby-lang via google to instance_eval a block with arguments; said trick will soon not be neccesary, so I'll just add a note to google instance_exec and you'll probably dig it up fine. Anyway, I really like the line above that rebinds the initialize method to perform a kind of 'reverse super'... old_initialize.bind(self).call(*args) ...and I'm trying to work out if it provides some benefits I have overlooked, apart from the fact that it will make the initialize method look a little cleaner and the things like self.command look a little messier. Cheers, ben