0

I am confused by the following code from Poignant Guide:

# The guts of life force within Dwemthy's Array
class Creature

  # Get a metaclass for this class
  def self.metaclass; class << self; self; end; end

  # Advanced metaprogramming code for nice, clean traits
  def self.traits( *arr )
    return @traits if arr.empty?

    # 1. Set up accessors for each variable
    attr_accessor( *arr )

    # 2. Add a new class method to for each trait.
    arr.each do |a|
      metaclass.instance_eval do
        define_method( a ) do |val|
          @traits ||= {}
          @traits[a] = val
        end
      end
    end

    # 3. For each monster, the `initialize' method
    #    should use the default number for each trait.
    class_eval do
      define_method( :initialize ) do
        self.class.traits.each do |k,v|
          instance_variable_set("@#{k}", v)
        end
      end
    end

  end

  # Creature attributes are read-only
  traits :life, :strength, :charisma, :weapon
end

The above code is used to create a new class, as in the following:

class Dragon < Creature
  life( 1340 )     # tough scales
  strength( 451 )  # bristling veins
  charisma( 1020 ) # toothy smile
  weapon( 939 )    # fire breath
end

I need to study the basics of meta-programming more on my own, but for now I just want to know, where does the val block argument come from in define_method( a ) do |val|? It represents the point values assigned to each trait, but I don't understand how each of those numbers become a block argument.

Also, why is a passed in the parentheses to define_method, while val is passed in as a block argument?

I've read over this question on the subject of define_method arguments, but it doesn't address the reasons for passing arguments to define_method rather than to the block.

Community
  • 1
  • 1
mrwnt10
  • 1,234
  • 1
  • 17
  • 28

1 Answers1

1

In the form

define_method(:foo){|x| ...}

:foo is the method name and x is the argument. They have different roles. It is the same as:

def foo(x)
  ...
end
sawa
  • 165,429
  • 45
  • 277
  • 381
  • Ok. I get that. How does `define_method` know what `val` is though? Why doesn't it think, for instance, that `1340` is another `trait` instead of assigning it to `val` like it does? – mrwnt10 Aug 10 '13 at 18:32
  • Nvm. I thought about it some more and I think I get it now. Thanks a lot. – mrwnt10 Aug 10 '13 at 18:41
  • 1
    `val` is a variable name. It does not matter what it is. Just like `def foo(x); puts x end ` is the same as `def foo(y); puts y end`, so is `define_method(:foo){|x| puts x}` the same as `define_method(:foo){|y| puts y}`. – sawa Aug 10 '13 at 18:42
  • Got it. I was thinking in too particular a context. – mrwnt10 Aug 10 '13 at 18:56