It turns out (after a fair bit of googling/yahooing - spread the love man) that this is an example of a Ruby Singleton Class. I'll explain below but should first note that this is very much lifted from Ola Bini's post on the topic. I take it slower with some more code and hopefully some Java angles. Just to be clear, this is not original work but represents my thought processes in understanding Ola's illustration.
Ready? Lets go.
1. Ruby has no concept of things being final; unlike in Java. If I want to add a method or attribute to the String class, I just do it (you can even do this to Java classes when using JRuby, but let's not complicate things):
When I run it I get this:
2. In Ruby, everything is an object, and all objects have a class which they are an instance of. You can get at these classes by calling the method class on any object you like. When you start coding the methods of your class will only be the ones available by defaut to that object's class. We can see these in Netbeans if we press CTRL-SPACE to get code completion:
All still aboard? Lets complicate things further. We've added a method to foo which is a n instance of String. Is this method available to other Strings? Code completion will tell us:
And there's the clue; foo is now listed as a class with our bar method defined on it. What? Remember we said everything in Ruby is an object? Well, it turns out that even classes are objects. Confused? Free yourself from your Java chains! It means we can frig around with them just like anything else we can get our grubby mits on but we need to be aware of what Ruby does for us when we do.
What is happening is that when we define a new method on a specific object an new, anonymous class is inserted between that object and the real class (in this case "String") which defines it. (Remember, Ruby only has single inheritance so this is pretty easy to picture in your head.) When we call a method on foo, the interpreter first looks inside the anonymous class for a definition (if there is one) and then on to the real class.
Now for the final push. You'll have probably guessed that our anonymous class is actually what is called a Singleton class. Seems fair, but why the "Singleton"? As we've seen, all objects can have a Singleton class, even if the original user didn't intend it. You want it, you can add it. Fine. Now know that classes are objects themselves. Following me? I'll say it slowly as it took me a while too; C-l-a-s-s-e-s a-r-e o-b-j-e-c-t-s.
Lets take an example, the class String is actually an instance of the class Class,as are all classes. There is nothing special about these instances. Their names are capitalised simply because they are constants. Now, because every class is an instance of Class, what are called Class Methods in Ruby or Static Methods in Java are actually just a special case of Singleton Methods; ones which are defined on the instance of the class in question. Lets add a Singleton Method to the String class. As I'm coming to learn with Ruby, there's more than one way to do it, but there is a preferred way. First, the non preferred way:
The first way is pretty simple. It's dead like the first example, way up the top of this page but the new hello method is private (It turns out that "private" in Ruby isn't what "private" is in Java but that's another post...) ; a Singleton Method. So why is the second way preferred? It's because it makes explicit the Singleton Class, both to you the developer and also to Ruby.
First, you the developer: In Ruby you need to think about how the code reads to you as a human being, not as a compiler as Java has taught you. This way shows us that the Singleton class is defined inside the class definition, and hello is defined on it. Just read it and you'll see.
Second, Ruby: Look at the Navigator for this way and compare it with the navigator for the first way. There is an extra "self" class in between the String and our function. This is the embodyment of the Singleton class.
We should explain this "self" a little more. Used on it's own in our code, "self" lets us get access to the class instance we are using it on like the first class in the code below. When used in a similar way to the preferred way it now gets us the Singleton class instance. We can see this in the second class. Again, look at the Navigator pane where it is made clear:
What about the differences between what these methods return? The former (the so called "regular self") returns the class instance: "String". The latter returns the Singleton class for the String.
So finally, lets return to the comment which started all this in the first place:
So why is this important? Well, as the comment stated, this is a nice and simple way to inherit class instance variables but more importantly this idiom is used in meta-programming, a mainstay or the Ruby world. For more detail on this I'd better leave it to Ola and his blog.
3 comments:
Nice post!
Unless I'm missing something, the key to understanding the chunk of code in question is that singleton classes can have *both* methods and instance variables.
The singleton class for a class like Array gets inserted between the Array class and it's parent, the Class class.
The singleton class for a class of my own, say C, gets inserted between the C class and the Object class.
Singleton classes can have both methods and instance variables just like any old class. The attr_accessor :some_instance_variable sets up a getter method and a setter method for some_instance_variable.
Let's change the code chunk:
##############
class C
class << self
attr_accessor :some_instance_variable
end
end
C.some_instance_variable = "bladiblah"
puts C.some_instance_variable #=> "blahdiblah"
###############
The attr_accessor just creates a getter and setter for the instance variable so, this is the same thing as the following:
##############
class C
class << self
def some_instance_variable
@some_instance_variable
end
def some_instance_variable= (new_iVar)
@some_instance_variable = new_iVar
end
end
end
C.some_instance_variable = "bladiblah"
puts C.some_instance_variable # =>"blahdiblah"
###############
So, this is all pretty standard. The fascinating thing is that you can do it with those weird creatures--singleton classes.
Now, let's add a sub-class and try the same thing:
##############
class D < C
end
D.some_instance_variable = "bladiblah"
puts D.some_instance_variable # =>"blahdiblah"
##############
Why does *this* work? Because when the code is executed the compiler looks at the D class and doesn't find the setter method, some_instance_variable=, then it goes up one level and looks at its parent, the C class, and finds no setter method. Then it goes up another level and looks at the C singleton class. There it does find the setter method. Same thing happens with the getter method.
Note that to retrieve a singleton_class you can run: (class << hello; self; end)
check out http://adprog.blogspot.com/ for a table on method locations depending on whether they are instance, class or singleton methods.
Post a Comment