Pre-Moose metaprogramming has a long history, but you were pretty much stuck rolling your own metamodel back then.
Moose changed this by providing extensible class generation. It tries to create a metamodel in which several specialized metamodels can coexist and work together, even on the same class.
Case in point, a little over a week ago Franck Cuny announced his new SPORE project.
SPORE aims to make using REST services much easier, by generating a lot of code to deal with the transport layer, presenting the data from the REST service using simple OO methods.
In context what's interesting about SPORE is the way that it leverages Moose to do that.
SPORE extends Moose's metamodel objects, specifically the object that represents methods in a class, to create the bridge between the appealing sugar layer (a simple 1:1 mapping between HTTP requests and method calls) and the underlying HTTP client.
Take a look at the Net::HTTP::Spore::Meta::Method class. This is the essence of the sugar layer, bridging the REST client with the sleek OO interface.
Compared with SOAP::Lite (not that the comparison is very fair), SPORE is a far simpler implementation that offers more (e.g. middlewares), even if you ignore the parts of SOAP::Lite that don't apply to SPORE.
Moose made it viable to design such projects "properly", without inflating the scope of the project. In fact, using Moose like this usually reduces the amount of code dramatically.
Before Moose writing a REST toolkit with a similar metaclass based design would be overengineering a simple idea to death. The project would probably never be truly finished due to the competing areas of focus (the metamodel vs. the HTTP client vs. high level REST features).
The alternative design approach is a hand rolled stack that does the bare minimum required for each step. This might do the job, and probably gets finished on time, the code is inherently brittle. It's hard to reuse the different parts because they don't stand alone. Most pre-Moose metaprogramming on the CPAN falls into this category.
KiokuDB is another example. Without Moose it's actually quite useless, it can't deal with more than a handful of classes out of the box. Sure, you could specify the appropriate serialization for every class you want to store, but at that point the design just doesn't make sense anymore; the limitations would make it unusable in practice.
Being able to assume that Moose introspection would be available for most objects stored in the database allowed me to remove all guesswork from the serialization, while still providing an acceptable user experience (it's very rare to need a custom typemap entry in practice).
This shortcut automatically reduced the scope of the project immensely, and allowed me to focus on the internals. The only thing that really separates KiokuDB from its predecessors is that I could build on Moose.
I'm really glad to see how Moose has literally changed the way we approach this set of problems. The MIT approach is now a sensible and pragmatic choice more often than before; or in other words we get a cleaner and more reusable CPAN for the same amount of effort.