[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: Exploring Metaprogramming

Ben Nagy

9/4/2006 5:54:00 AM

> -----Original Message-----
[...]
> On 9/3/06, Logan Capaldo <logancapaldo@gmail.com> 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