Exploring Metaprogramming

I've got some downtime this weekend, so I'm pooring over various examples of
metaprogramming w/ Ruby.

I really like _why's example regarding Dwemthy, but am curious as to how to
achieve my end.

The article in question is here:

http://poignantguide.net/ruby/chapter-6.html#section3

_why's method allows us to build 'traits' for the character (by calling the
'trait' class method), and the meta programming behind the scenes builds an
'initialize' instance method for the class.

What if I want to build a second method called 'command'? It should place
the argument in an array that can be referenced later. It will need to also
build an initialize method for my class, but one of them will have to be
overwritten.

Does anyone have any smart ways to combine or chain these two initialize
methods together?

I ultimately want to do something like this:

class Dragon < Creature
   traits :life
   life 100

   command :fly

   def fly
     puts "I am flying..."
   end

end

After an instance is created, it will contain the instance variable
'commands', which is an array holding the symbol 'fly'.

Does anyone have any ideas? In the end, I'd be adding more and more of
these commands to my DSL. This is a DSL, right? :wink:

I've got some downtime this weekend, so I'm pooring over various examples of
metaprogramming w/ Ruby.

I really like _why's example regarding Dwemthy, but am curious as to how to
achieve my end.

The article in question is here:

http://poignantguide.net/ruby/chapter-6.html#section3

_why's method allows us to build 'traits' for the character (by calling the
'trait' class method), and the meta programming behind the scenes builds an
'initialize' instance method for the class.

What if I want to build a second method called 'command'? It should place
the argument in an array that can be referenced later. It will need to also
build an initialize method for my class, but one of them will have to be
overwritten.

Does anyone have any smart ways to combine or chain these two initialize
methods together?

I ultimately want to do something like this:

class Dragon < Creature
  traits :life
  life 100

  command :fly

  def fly
    puts "I am flying..."
  end

end

After an instance is created, it will contain the instance variable
'commands', which is an array holding the symbol 'fly'.

here's something to get you started:

class Creature
   def self.command(com)
     @commands ||= []
     @commands |= [com]
   end

   def self.commands
     @commands ||= []
   end
end

class Dragon < Creature
   command :fly
end

p Dragon.commands #=> [:fly]

···

On Sep 3, 2006, at 10:39 PM, Michael Gorsuch wrote:

Does anyone have any ideas? In the end, I'd be adding more and more of
these commands to my DSL. This is a DSL, right? :wink:

Sorry I seem to have under estimated what you wanted.

try this instead:

% cat dragon.rb
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

   command :breathe_fire

   def breathe_fire
     puts "I'm breathing fire!"
   end
end

dragon = Dragon.new
p dragon.commands

% ruby dragon.rb
[:breathe_fire, :fly]

···

On Sep 3, 2006, at 10:39 PM, Michael Gorsuch wrote:

What if I want to build a second method called 'command'? It should place
the argument in an array that can be referenced later. It will need to also
build an initialize method for my class, but one of them will have to be
overwritten.

Does anyone have any smart ways to combine or chain these two initialize
methods together?

I ultimately want to do something like this:

class Dragon < Creature
  traits :life
  life 100

  command :fly

  def fly
    puts "I am flying..."
  end

end

After an instance is created, it will contain the instance variable
'commands', which is an array holding the symbol 'fly'.

Does anyone have any ideas? In the end, I'd be adding more and more of
these commands to my DSL. This is a DSL, right? :wink:

Thanks a ton for this! This is amazing!

···

On 9/3/06, Logan Capaldo <logancapaldo@gmail.com> wrote:

On Sep 3, 2006, at 10:39 PM, Michael Gorsuch wrote:

>
> What if I want to build a second method called 'command'? It
> should place
> the argument in an array that can be referenced later. It will
> need to also
> build an initialize method for my class, but one of them will have
> to be
> overwritten.
>
> Does anyone have any smart ways to combine or chain these two
> initialize
> methods together?
>
> I ultimately want to do something like this:
>
> class Dragon < Creature
> traits :life
> life 100
>
> command :fly
>
> def fly
> puts "I am flying..."
> end
>
> end
>
> After an instance is created, it will contain the instance variable
> 'commands', which is an array holding the symbol 'fly'.
>
> Does anyone have any ideas? In the end, I'd be adding more and
> more of
> these commands to my DSL. This is a DSL, right? :wink:

Sorry I seem to have under estimated what you wanted.

try this instead:

% cat dragon.rb
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

   command :breathe_fire

   def breathe_fire
     puts "I'm breathing fire!"
   end
end

dragon = Dragon.new
p dragon.commands

% ruby dragon.rb
[:breathe_fire, :fly]

[...]
[...]

> 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

···

-----Original Message-----
On 9/3/06, Logan Capaldo <logancapaldo@gmail.com> wrote: