From: Luc Juggery Date: 2006-10-23T20:19:04+09:00 Subject: Learning ruby - metaprogramming question [from poignant guide] Hello all, I started learning Ruby not long ago and I'd really like to understand concepts behind it. Poignant Guide to ruby (http://poignantguide.net/ruby/chapter-6.html) is a very usefull book for this. Below example is extracted from this book and some things remain quite magical for me. ---------------------------------------------------------------------- # 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 --------------------- class Creature traits :life, :strength, :charisma, :weapon end --------------------- --------- generated Creature class ---------- class Creature # 1. set up reader and writer methods attr_accessor :life, :strength, :charisma, :weapon # 2. add new class methods to use in creature def self.life( val ) @traits ||= {} @traits['life'] = val end def self.strength( val ) @traits ||= {} @traits['strength'] = val end def self.charisma( val ) @traits ||= {} @traits['charisma'] = val end def self.weapon( val ) @traits ||= {} @traits['weapon'] = val end # 3. initialize sets the default points for # each trait def initialize self.class.traits.each do |k,v| instance_variable_set("@#{k}", v) end end end ---------------------------------- The thing I do not really understand is the instance_eval and class_eval in the self.traits method. It seems that metaclass.instance_eval define_method create a Class method in the Creature class. Creating a method in the singleton class of a Class class always define a Class method? can the Singleton class can also be used to create an instance method of Creature ? Is the way presented in this example is what is commonly done to dynamically define Class and instance method of a Class or are there any other possible syntaxes ? The self.traits method also define accessors methods: attr_accessor *arr, how come this is not done within a define_method statement? Well.... I hope I am not getting to confusing.... I would really appreciate your help as I'd really like to understand how that's working. I have already read several article on metaprogramming but some things remain quite obscure to me :-) Thanks a lot, Luc