Stumbled into Neal Ford's post today which spawn off several other blog posts else where too, including Avi Bryant's new blog. How timely(this was actually one month ago)! I was just musing about this very thing and that's how I found the this blog post. Smalltalk has an adventage over Ruby(an pretty much any other language) because the runtime is always running. Runtime = development time = compile time. This allows for a lot of introspection to occur during development. Things like code completion, for example, are done by inspecting the objects in the runtime, rather than by inspecting the source code like in modern Java IDEs. Of course it can do more than that: things like excuting arbitrary code when you are at a breakpoint, to give another example. You could also query the system for the object class hierarchies to do anything you want with them, including creating new classes and methods on the fly.
Now, one thing about the Ruby metaprogramming techniques some complain about is the fact that they are hard to debug, because you can't see the code that you are executing. In contrast, you can get the source code to any method during runtime, which means you can easily debug through it(yes, but aside from that, debugging in Ruby still sucks anyway). It's the explicit vs implicit thing again. But the advantage of the Ruby approach is that it's more dry - you specify the declaration once, and that's it, the code that's generated is never seen anywhere, it removes the issue that all code generating tools have: that there's a versioning problem between the generated code and the spec that feeds into the generator.
Which way is better? Clearly(how can you argue with "clearly"? you can't), neither one is. Is there a best of both worlds? Hard to say.
Going back to the ActiveRecord has_many example, some have mentioned that this can be done without using code generation. In Smalltalk, apparently this is done by using an annotation. The class code can then presumably query the annotation information when it needs to get an association, I assume using something like method_missing(I'll have to check this), which you could do the exact same thing in Ruby, only in place of the annotation you'd still have the class initializer. In deed, that is how Hibernate does it for Java as well. So why do the Ruby guys like to use code generation in that case? It's just their style! If someone develops a new kung fu style that kicks your ass, are you going to say he's doing it wrong? Also, in Ruby method_missing is slow, so there's an incentive to avoid it.