Think of your source code as a world teeming with vibrant citizens: variables, classes, methods, and so on. If you want to get technical, you can call these citizens language constructs.
In many programming languages, language constructs behave more like ghosts than fleshed-out citizens: you can see them in your source code, but they disappear before the program runs. Take C++, for example. Once the compiler has finished its job, things like variables and methods have lost their concreteness; they are just locations in memory. You can’t ask a class for its instance methods, because by the time you ask the question, the class has faded away. In languages such as C++, runtime is an eerily quiet place—a ghost town.
In other languages, such as Ruby, runtime is more like a busy marketplace. Most language constructs are still there, buzzing all around. You can even walk up to a language construct and ask it questions about itself. This is called introspection.
Let’s watch introspection in action. Take a look at the following code.
| class Greeting |
| def initialize(text) |
| @text = text |
| end |
| |
| def welcome |
| @text |
| end |
| end |
| |
| my_object = Greeting.new("Hello") |
I defined a Greeting class and created a Greeting object. I can now turn to the language constructs and ask them questions.
| my_object.class # => Greeting |
I asked my_object about its class, and it replied in no uncertain terms: “I’m a Greeting.” Now I can ask the class for a list of its instance methods.
| my_object.class.instance_methods(false) # => [:welcome] |
The class answered with an array containing a single method name: welcome. (The false argument means, “List only instance methods you defined yourself, not those ones you inherited.”) Let’s peek into the object itself, asking for its instance variables.
| my_object.instance_variables # => [:@text] |
Again, the object’s reply was loud and clear. Because objects and classes are first-class citizens in Ruby, you can get a lot of information from running code.
However, this is only half of the picture. Sure, you can read language constructs at runtime, but what about writing them? What if you want to add new instance methods to Greeting, alongside welcome, while the program is running? You might be wondering why on earth anyone would want to do that. Allow me to explain by telling a story.