L
Luc Juggery
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
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