Metaprogramming Ruby
Program Like the Ruby Pros
Paolo Perrotta
Version: P3.0 (May 2011)
Copyright 2011, The Pragmatic Bookshelf. This book is licensed tothe individual who purchased it. We don't copy-protect itbecause that would limit your ability to use it for yourown purposes. Please don't break this trustdon't allow othersto use your copy of the book. Thanks.
Dave & Andy.
Table of Contents
Copyright 2011, The Pragmatic Bookshelf.
Chapter 1
Foreword
Ruby inherits characteristics from various languagesLisp, Small-talk, C, and Perl, to name a few. Metaprogramming comes from Lisp (and Smalltalk). Its a bit like magic, which makes something astonishing possible. There are two kinds of magic: white magic, which does good things, and black magic, which can do nasty things. Likewise, there are two aspects to metaprogramming. If you discipline yourself, you can do good things, such as enhancing the language without tweaking its syntax by macros or enabling internal domain-specific languages. But you can fall into the dark side of metaprogramming. Metaprogramming can confuse easily.
Ruby trusts you. Ruby treats you as a grown-up programmer. It gives you great power such as metaprogramming. But you need to remember that with great power comes great responsibility.
Enjoy programming in Ruby.
Copyright 2011, The Pragmatic Bookshelf.
Chapter 2
Acknowledgments
Before I begin, I need to thank a few people. Im talking to you, gentlemen: Joe Armstrong, Satoshi Asakawa, Paul Barry, Emmanuel Bernard, Roberto Bettazzoni, Ola Bini, Piergiuliano Bossi, Simone Busoli, Andrea Cisternino, Davide DAlto, Mauro Di Nuzzo, Marco Di Timoteo, Mauricio Fernandez, Jay Fields, Michele Finelli, Neal Ford, Florian Frank, Sanne Grinovero, Federico Gobbo, Florian Gro, Sebastian Hennebrder, Doug Hudson, Jurek Husakowski, Lyle Johnson, Luca Marchetti, MenTaLguY, Carlo Pecchia, Andrea Provaglio, Mike Roberts, Martin Rodgers, Jeremy Sydik, Andrea Tomasini, Marco Trincardi, Ivan Vaghi, Giancarlo Valente, Davide Varvello, Jim Weirich, and the dozens of readers who reported problems and errata while this book was in beta. Whether you provided reviews, quotes, fixes, opinions, or moral support, theres at least one line in this book that changed for the better because of you. Did I say one line? For some of you, make that a few chapters. In particular, Ola, Satoshi, and Jurek deserve a special place on this page and my enduring gratitude.
Thanks to the staff at the Pragmatic Bookshelf: Janet Furlow, Seth Maislin, Steve Peter, Susannah Davidson Pfalzer, and Kim Wimpsett. Dave and Andy, thank you for believing in this project when times got rough. Jill, thank you for making my awkward prose look so effortless. Our crunch week in Venice was a lot of hard work, but it was definitely worth it. And speaking of Venice: thank you, Lucio, for being such a dear old friend.
Mom and Dad, thank you for your support, for your love, and for never asking why I was taking so long to finish this book.
Most authors closing thanks go to their partners, and now I know why. When youre about to finish a book, you turn back to the day when you started writing, and it feels so far away. I remember writing over lunch breaks, nights, and weekends, locked for days or weeks inside my study, a hotel room in some foreign city, or a seashore house that would have suited a hermit. Its such a lonesome endeavorand yet, I never felt alone. Thank you, Mirella.
Copyright 2011, The Pragmatic Bookshelf.
Chapter 3
Introduction
Will write code that writes code that writes code for food.
Martin Rodgers
Metaprogramming it sounds cool! It sounds like a design technique for high-level enterprise architects or a fashionable buzzword that has found its way into press releases.
In fact, far from being an abstract concept or a bit of marketing-speak, metaprogramming is a collection of down-to-earth, pragmatic coding techniques. It doesnt just sound cool; it is cool. Here are some of the things you can do with metaprogramming in the Ruby language:
Say you want to write a Ruby program that connects to an external systemmaybe a web service or a Java program. With metaprogramming, you can write a wrapper that takes any method call and routes it to the external system. If somebody adds methods to the external system later, you dont have to change your Ruby wrapper; the wrapper will support the new methods right away. Thats magic!
Maybe you have a problem that would be best solved with a programming language thats specific to that problem. You could go to the trouble of writing your own language, custom parser and all. Or you could just use Ruby, bending its syntax until it looks like a specific language for your problem. You can even write your own little interpreter that reads code written in your Ruby-based language from a file.
You can remove duplication from your Ruby program at a level that Java programmers can only dream of. Lets say you have twenty methods in a class, and they all look the same. How about defining all those methods at once, with just a few lines of code? Or maybe you want to call a sequence of similarly named methods. How would you like a single short line of code that calls all the methods whose names match a patternlike, say, all methods that begin with test
?
You can stretch and twist Ruby to meet your needs, rather than adapt to the language as it is. For example, you can enhance any class (even a core class like Array
) with that method you miss so dearly, you can wrap logging functionality around a method that you want to monitor, you can execute custom code whenever a client inherits from your favorite classthe list goes on. You are limited only by your own, undoubtedly fertile, imagination.
Metaprogramming gives you the power to do all these things. Lets see what it looks like.
The M Word
Youre probably expecting a definition of metaprogramming right from the start. Heres one for you:
Metaprogramming is writing code that writes code.
Well get to a more precise definition in a short while, but this one will do for now. What do I mean by code that writes code, and how is that useful in your daily work? Before I answer those questions, lets take a step back and look at programming languages themselves.
Code Generators and Compilers
In metaprogramming, you write code that writes code. But isnt that what code generators and compilers do? For example, you can write annotated Java code and then use a code generator to output XML configuration files. In a broad sense, this XML generation is an example of metaprogramming. In fact, many people think about code generation when the m word comes up.
This particular brand of metaprogramming implies that you use a program to generate or otherwise manipulate a second, distinct programand then you run the second program. After you run the code generator, you can actually read the generated code and (if you want to test your tolerance for pain) even modify it by hand before you finally run it. This is also what happens under the hood with C++ templates: the compiler turns your templates into a regular C++ program before compiling them, and then you run the compiled program.
In this book, Ill stick to a different meaning of metaprogramming , focusing on code that manipulates itself at runtime. Only a few languages can do that effectively, and Ruby is one of them. You can think of this as dynamic metaprogramming to distinguish it from the static metaprogramming of code generators and compilers.