The following is a Ruby code snippet from Why's Poignant Guide to Ruby Chapter 6, where he attempts to demonstrate metaprogramming in Ruby:
# Get a metaclass for this class
def self.metaclass; class << self; self; end; end
I am not that well acquainted with Ruby, but is this what it would look like in expanded form?
def self.metaclass
def self.self
end
end
At least that's how I understand it. However, it still don't comprehend what this code does, exactly. What is its purpose?
Further on in the code, Why adds this:
arr.each do |a|
metaclass.instance_eval do
define_method( a ) do |val|
@traits ||= {}
@traits[a] = val
end
end
end
If I understand it correctly, this piece of code adds a new value to @traits with the given name and value. Is that correct?
Thanks for your help, here's the full source code that caused me trouble, for anyone who wants to see it:
# 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
And in usage:
class Dragon < Creature
life( 1340 ) # tough scales
strength( 451 ) # bristling veins
charisma( 1020 ) # toothy smile
weapon( 939 ) # fire breath
end
self is a reserved keyword in Ruby that always refers to the current object and classes are also objects, but the object self refers to frequently changes based on the situation or context. So if you're in an instance, self refers to the instance. If you're in a class, self refers to that class.
the method self refers to the object it belongs to. Class definitions are objects too.
class Foo
def self.bar # Create a method invoked by Foo.bar instead of Foo.new.bar
42 # the return value of this method (value of last expression)
end
end
class Foo
def self.jim # Another method on the class itself
class << self # Change the 'self' to be the metaclass of the current object
self # Evaluate the current 'self' as the 'return value' of
end # class<<self…end; and since this is the last expression in
end # the method, its value is the return value for the method
end
In short: what you are seeing defines a method named metaclass
on the Creature
class itself (not for instances). When you run this method, it finds the metaclass of Creature
and returns that.
Read around the 'net for what the "metaclass" of an object is.
In expanded form it looks exactly the same:
def self.metaclass
class << self
self
end
end
Notice it's just returning self
, which because it's evaluated within the context of the metaclass, which in effect what class << self
does, self
is the metaclass.
With the introduction of things like define_singleton_method, the number of cases where you need to have access to the metaclass directly are becoming very small.
It's all very complicated and seemingly a consequence of the "everything is an object" design principle.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With