What happens when you call Object.new?
by Paweł Świątkowski
15 May 2015
While reading RubyGems source code for second or third time this week, I encountered something weird, which led me to this little investigation. Namely, in one class there was a constructor with
def initialize, but just above it there was a static method definition with
def self.new. How did it work? Soon I found out.
MyClass.new obviously looks like calling a class static method, though I always thought that there is some magic involved there, which chose to call
initialize instead. Turned out I was wrong. Luckily MRI’s code is readable enough to track it there. So, let’s have a look:
This is what static method
new of class
Object looks like. We can see some memory allocation there and then
rb_obj_call_init(obj, argc, argv) which, as one could guess, calls
initialize of newly allocated object. And one would be right:
PASS_PASSED_BLOCK is some kind of weird macro I don’t really understand (yes, my C-fu is weak), it is followed by something that clearly looks like calling a
idInitialize (which is an alias for “initialize” word from
REGISTER_SYMID(idInitialize, "initialize");) on
obj with arguments.
For reference, this is how Rubinius implements it:
So, what can we use this knowledge for? Let’s jump back to RubyGems source. The overridden
self.new there looks like this:
Basically, there are some checks on the argument and then, if those indicate old format of the gem, different class is instantiated and returned. Familiar? Well, yes, this sounds more or less like Factory design pattern. And we can use it for that. Consider following code:
Pretty neat, huh? There is, however one big caveat here: the other class you are instantiating cannot inherit from the class that is being called or you end up with stack level too deep, which is completely understandable. Another way to do it is to define
self.new in children classes as follows:
This might not be the most useful trick in the world, but experimenting with it brought me closed to understand how Ruby works.