Hi --

On Sun, 14 May 2006, Pat Maddox wrote:

> I saw a link for why's article "Seeing Metaclasses Clearly" [1]
> earlier this week.  Been looking at it, and honestly I can't say it's
> making things more clear for me.  The final example is:
>
> class MailTruck
>  def self.company( name )
>    meta_def :company do; name; end
>  end
> end
>
> class HappyTruck < MailTruck
>  company "Happy's -- We Bring the Mail, and That's It!"
> end
>
> I simply don't see how that's different from
>
> class MailTruck
> def self.company(name)
>   instance_eval do
>     define_method(name) { name }
>   end
> end
> end
>
> except for, um, my code doesn't work - wrong number of arguments (0 for 1)

The problem is that you're defining an *instance* method of MailTruck,
called "company".  Meanwhile, the class method company is still the
above method.  So when you call HappyTruck.company, it's running the
method that expects the (name) argument.

You'd need to do:

   def self.company(name)
     metaclass.instance_eval do     # or class_eval
       define_method(name) { name }
     end
   end

(Note: I use this "metaclass" method under protest :-)  I believe it
should be called singleton_class; see
http://www.rcrchive.net/rcr/show/231.)

I think part of the confusion may be that the same name ("company") is
being used for both the method that creates a new method, and for that
new method itself.  The idea is that the parent class has a method,
company, and every time a child class calls that method, the child
class gets a *new* method called company, which reports back the
argument to the original method.  I think the collapse of levels in
your code might stem from this collapsing of names.

Here's a rewrite of the example, using differentiated names.  See if
this helps:

class MailTruck
   def self.set_slogan( our_slogan )
     meta_def :slogan do; our_slogan; end
   end
end

class HappyTruck < MailTruck
    set_slogan "Happy's -- We Bring the Mail, and That's It!"
end

p HappyTruck.slogan

(It would be more "Rubyish" to use slogan= rather than set_slogan, but
anyway, I just wanted to change it by one increment.)

In this rewrite, set_slogan is the equivalent of the parent "company"
method, and slogan is equivalent of the child "company" method that
the parent method is able to create for each subclass.


David

-- 
David A. Black (dblack / wobblini.net)
* Ruby Power and Light, LLC (http://www.rubypowerandlight.com)
   > Ruby and Rails consultancy and training
* Author of "Ruby for Rails" from Manning Publications!
   > http://www.manning.com/black